QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgsfilecontentsourcelineedit.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsfilecontentsourcelineedit.cpp
3 -----------------------
4 begin : July 2018
5 copyright : (C) 2018 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "moc_qgsfilecontentsourcelineedit.cpp"
18#include "qgssettings.h"
19#include "qgsmessagebar.h"
20#include "qgsfilterlineedit.h"
22
23#include <QFileDialog>
24#include <QHBoxLayout>
25#include <QImageReader>
26#include <QInputDialog>
27#include <QLineEdit>
28#include <QMenu>
29#include <QToolButton>
30#include <QUrl>
31#include <QMovie>
32
33//
34// QgsAbstractFileContentSourceLineEdit
35//
36
38 : QWidget( parent )
39{
40 QHBoxLayout *layout = new QHBoxLayout( this );
41 layout->setContentsMargins( 0, 0, 0, 0 );
42 mFileLineEdit = new QgsFilterLineEdit( this );
43 mFileLineEdit->setShowClearButton( true );
44 mFileToolButton = new QToolButton( this );
45 mFileToolButton->setText( QString( QChar( 0x2026 ) ) );
46 mPropertyOverrideButton = new QgsPropertyOverrideButton( this );
47 layout->addWidget( mFileLineEdit, 1 );
48 layout->addWidget( mFileToolButton );
49 layout->addWidget( mPropertyOverrideButton );
50 setLayout( layout );
51
52 QMenu *sourceMenu = new QMenu( mFileToolButton );
53
54 QAction *selectFileAction = new QAction( tr( "Select File…" ), sourceMenu );
55 connect( selectFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::selectFile );
56 sourceMenu->addAction( selectFileAction );
57
58 QAction *embedFileAction = new QAction( tr( "Embed File…" ), sourceMenu );
59 connect( embedFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::embedFile );
60 sourceMenu->addAction( embedFileAction );
61
62 QAction *extractFileAction = new QAction( tr( "Extract Embedded File…" ), sourceMenu );
63 connect( extractFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::extractFile );
64 sourceMenu->addAction( extractFileAction );
65
66 connect( sourceMenu, &QMenu::aboutToShow, this, [this, extractFileAction] {
67 extractFileAction->setEnabled( mMode == ModeBase64 );
68 } );
69
70 QAction *enterUrlAction = new QAction( tr( "From URL…" ), sourceMenu );
71 connect( enterUrlAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::selectUrl );
72 sourceMenu->addAction( enterUrlAction );
73
74 mFileToolButton->setMenu( sourceMenu );
75 mFileToolButton->setPopupMode( QToolButton::MenuButtonPopup );
76 connect( mFileToolButton, &QToolButton::clicked, this, &QgsAbstractFileContentSourceLineEdit::selectFile );
77
78 connect( mFileLineEdit, &QLineEdit::textEdited, this, &QgsAbstractFileContentSourceLineEdit::mFileLineEdit_textEdited );
79 connect( mFileLineEdit, &QgsFilterLineEdit::cleared, this, [=] {
80 mMode = ModeFile;
81 mFileLineEdit->setPlaceholderText( QString() );
82 mBase64.clear();
83 emit sourceChanged( QString() );
84 } );
85
86 mPropertyOverrideButton->setVisible( mPropertyOverrideButtonVisible );
87}
88
90{
91 switch ( mMode )
92 {
93 case ModeFile:
94 return mFileLineEdit->text();
95
96 case ModeBase64:
97 return mBase64;
98 }
99
100 return QString();
101}
102
104{
105 mLastPathKey = key;
106}
107
109{
110 mPropertyOverrideButtonVisible = visible;
111 mPropertyOverrideButton->setVisible( visible );
112}
113
115{
116 const bool isBase64 = source.startsWith( QLatin1String( "base64:" ), Qt::CaseInsensitive );
117
118 if ( ( !isBase64 && source == mFileLineEdit->text() && mBase64.isEmpty() ) || ( isBase64 && source == mBase64 ) )
119 return;
120
121 if ( isBase64 )
122 {
123 mMode = ModeBase64;
124 mBase64 = source;
125 mFileLineEdit->clear();
126 mFileLineEdit->setPlaceholderText( tr( "Embedded file" ) );
127 }
128 else
129 {
130 mMode = ModeFile;
131 mBase64.clear();
132 mFileLineEdit->setText( source );
133 mFileLineEdit->setPlaceholderText( QString() );
134 }
135
136 emit sourceChanged( source );
137}
138
139void QgsAbstractFileContentSourceLineEdit::selectFile()
140{
141 QgsSettings s;
142 const QString file = QFileDialog::getOpenFileName( nullptr, selectFileTitle(), defaultPath(), fileFilter() );
143 const QFileInfo fi( file );
144 if ( file.isEmpty() || !fi.exists() || file == source() )
145 {
146 return;
147 }
148 mMode = ModeFile;
149 mBase64.clear();
150 mFileLineEdit->setText( file );
151 mFileLineEdit->setPlaceholderText( QString() );
152 s.setValue( settingsKey(), fi.absolutePath() );
153 emit sourceChanged( mFileLineEdit->text() );
154}
155
156void QgsAbstractFileContentSourceLineEdit::selectUrl()
157{
158 bool ok = false;
159 const QString path = QInputDialog::getText( this, fileFromUrlTitle(), fileFromUrlText(), QLineEdit::Normal, mFileLineEdit->text(), &ok );
160 if ( ok && path != source() )
161 {
162 mMode = ModeFile;
163 mBase64.clear();
164 mFileLineEdit->setText( path );
165 mFileLineEdit->setPlaceholderText( QString() );
166 emit sourceChanged( mFileLineEdit->text() );
167 }
168}
169
170void QgsAbstractFileContentSourceLineEdit::embedFile()
171{
172 QgsSettings s;
173 const QString file = QFileDialog::getOpenFileName( nullptr, embedFileTitle(), defaultPath(), fileFilter() );
174 const QFileInfo fi( file );
175 if ( file.isEmpty() || !fi.exists() )
176 {
177 return;
178 }
179
180 s.setValue( settingsKey(), fi.absolutePath() );
181
182 // encode file as base64
183 QFile fileSource( file );
184 if ( !fileSource.open( QIODevice::ReadOnly ) )
185 {
186 return;
187 }
188
189 const QByteArray blob = fileSource.readAll();
190 const QByteArray encoded = blob.toBase64();
191
192 QString path( encoded );
193 path.prepend( QLatin1String( "base64:" ) );
194 if ( path == source() )
195 return;
196
197 mBase64 = path;
198 mMode = ModeBase64;
199
200 mFileLineEdit->clear();
201 mFileLineEdit->setPlaceholderText( tr( "Embedded file" ) );
202
203 emit sourceChanged( path );
204}
205
206void QgsAbstractFileContentSourceLineEdit::extractFile()
207{
208 QgsSettings s;
209 const QString file = QFileDialog::getSaveFileName( nullptr, extractFileTitle(), defaultPath(), fileFilter() );
210 // return dialog focus on Mac
211 activateWindow();
212 raise();
213 if ( file.isEmpty() )
214 {
215 return;
216 }
217
218 const QFileInfo fi( file );
219 s.setValue( settingsKey(), fi.absolutePath() );
220
221 // decode current base64 embedded file
222 const QByteArray base64 = mBase64.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix
223 const QByteArray decoded = QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
224
225 QFile fileOut( file );
226 fileOut.open( QIODevice::WriteOnly );
227 fileOut.write( decoded );
228 fileOut.close();
229
230 if ( mMessageBar )
231 {
232 mMessageBar->pushMessage( extractFileTitle(), tr( "Successfully extracted file to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( file ).toString(), QDir::toNativeSeparators( file ) ), Qgis::MessageLevel::Success, 0 );
233 }
234}
235
236void QgsAbstractFileContentSourceLineEdit::mFileLineEdit_textEdited( const QString &text )
237{
238 mFileLineEdit->setPlaceholderText( QString() );
239 mBase64.clear();
240 mMode = ModeFile;
241 if ( !text.isEmpty() && !QFileInfo::exists( text ) )
242 {
243 const QUrl url( text );
244 if ( !url.isValid() )
245 {
246 return;
247 }
248 }
249 emit sourceChanged( text );
250}
251
252QString QgsAbstractFileContentSourceLineEdit::defaultPath() const
253{
254 if ( QFileInfo::exists( source() ) )
255 return source();
256
257 return QgsSettings().value( settingsKey(), QDir::homePath() ).toString();
258}
259
260QString QgsAbstractFileContentSourceLineEdit::settingsKey() const
261{
262 return mLastPathKey.isEmpty() ? defaultSettingsKey() : mLastPathKey;
263}
264
266{
267 mMessageBar = bar;
268}
269
271{
272 return mMessageBar;
273}
274
275
276//
277// QgsPictureSourceLineEditBase
278//
279
281
282
283QString QgsPictureSourceLineEditBase::fileFilter() const
284{
285 switch ( mFormat )
286 {
287 case Svg:
288 return tr( "SVG files" ) + " (*.svg)";
289 case Image:
290 {
291 QStringList formatsFilter;
292 const QByteArrayList supportedFormats = QImageReader::supportedImageFormats();
293 for ( const auto &format : supportedFormats )
294 {
295 formatsFilter.append( QString( QStringLiteral( "*.%1" ) ).arg( QString( format ) ) );
296 }
297 return QString( "%1 (%2);;%3 (*.*)" ).arg( tr( "Images" ), formatsFilter.join( QLatin1Char( ' ' ) ), tr( "All files" ) );
298 }
299
300 case AnimatedImage:
301 {
302 QStringList formatsFilter;
303 const QByteArrayList supportedFormats = QMovie::supportedFormats();
304 for ( const auto &format : supportedFormats )
305 {
306 formatsFilter.append( QString( QStringLiteral( "*.%1" ) ).arg( QString( format ) ) );
307 }
308 return QString( "%1 (%2);;%3 (*.*)" ).arg( tr( "Animated Images" ), formatsFilter.join( QLatin1Char( ' ' ) ), tr( "All files" ) );
309 }
310 }
312}
313
314QString QgsPictureSourceLineEditBase::selectFileTitle() const
315{
316 switch ( mFormat )
317 {
318 case Svg:
319 return tr( "Select SVG File" );
320 case Image:
321 return tr( "Select Image File" );
322 case AnimatedImage:
323 return tr( "Select Animated Image File" );
324 }
326}
327
328QString QgsPictureSourceLineEditBase::fileFromUrlTitle() const
329{
330 switch ( mFormat )
331 {
332 case Svg:
333 return tr( "SVG From URL" );
334 case Image:
335 return tr( "Image From URL" );
336 case AnimatedImage:
337 return tr( "Animated Image From URL" );
338 }
340}
341
342QString QgsPictureSourceLineEditBase::fileFromUrlText() const
343{
344 switch ( mFormat )
345 {
346 case Svg:
347 return tr( "Enter SVG URL" );
348 case Image:
349 return tr( "Enter image URL" );
350 case AnimatedImage:
351 return tr( "Enter animated image URL" );
352 }
354}
355
356QString QgsPictureSourceLineEditBase::embedFileTitle() const
357{
358 switch ( mFormat )
359 {
360 case Svg:
361 return tr( "Embed SVG File" );
362 case Image:
363 return tr( "Embed Image File" );
364 case AnimatedImage:
365 return tr( "Embed Animated Image File" );
366 }
368}
369
370QString QgsPictureSourceLineEditBase::extractFileTitle() const
371{
372 switch ( mFormat )
373 {
374 case Svg:
375 return tr( "Extract SVG File" );
376 case Image:
377 return tr( "Extract Image File" );
378 case AnimatedImage:
379 return tr( "Extract Animated Image File" );
380 }
382}
383
384QString QgsPictureSourceLineEditBase::defaultSettingsKey() const
385{
386 switch ( mFormat )
387 {
388 case Svg:
389 return QStringLiteral( "/UI/lastSVGDir" );
390 case Image:
391 return QStringLiteral( "/UI/lastImageDir" );
392 case AnimatedImage:
393 return QStringLiteral( "/UI/lastAnimatedImageDir" );
394 }
396}
397
@ Success
Used for reporting a successful operation.
Definition qgis.h:158
void setLastPathSettingsKey(const QString &key)
Sets a specific settings key to use when storing the last used path for the file source.
void sourceChanged(const QString &source)
Emitted whenever the file source is changed in the widget.
void setMessageBar(QgsMessageBar *bar)
Sets the message bar associated with the widget.
void setPropertyOverrideToolButtonVisible(bool visible)
Sets the visibility of the property override tool button.
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
void setSource(const QString &source)
Sets a new source to show in the widget.
QgsAbstractFileContentSourceLineEdit(QWidget *parent=nullptr)
Constructor for QgsAbstractFileContentSourceLineEdit, with the specified parent widget.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
void setShowClearButton(bool visible)
Sets whether the widget's clear button is visible.
void cleared()
Emitted when the widget is cleared.
A bar for displaying non-blocking messages to the user.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
A button for controlling property overrides which may apply to a widget.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
#define BUILTIN_UNREACHABLE
Definition qgis.h:6720