QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
18#include "qgsfilterlineedit.h"
19#include "qgsmessagebar.h"
21#include "qgssettings.h"
22
23#include <QFileDialog>
24#include <QHBoxLayout>
25#include <QImageReader>
26#include <QInputDialog>
27#include <QLineEdit>
28#include <QMenu>
29#include <QMovie>
30#include <QToolButton>
31#include <QUrl>
32
33#include "moc_qgsfilecontentsourcelineedit.cpp"
34
35//
36// QgsAbstractFileContentSourceLineEdit
37//
38
40 : QWidget( parent )
41{
42 QHBoxLayout *layout = new QHBoxLayout( this );
43 layout->setContentsMargins( 0, 0, 0, 0 );
44 mFileLineEdit = new QgsFilterLineEdit( this );
45 mFileLineEdit->setShowClearButton( true );
46 mFileToolButton = new QToolButton( this );
47 mFileToolButton->setText( QString( QChar( 0x2026 ) ) );
48 mPropertyOverrideButton = new QgsPropertyOverrideButton( this );
49 layout->addWidget( mFileLineEdit, 1 );
50 layout->addWidget( mFileToolButton );
51 layout->addWidget( mPropertyOverrideButton );
52 setLayout( layout );
53
54 QMenu *sourceMenu = new QMenu( mFileToolButton );
55
56 QAction *selectFileAction = new QAction( tr( "Select File…" ), sourceMenu );
57 connect( selectFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::selectFile );
58 sourceMenu->addAction( selectFileAction );
59
60 QAction *embedFileAction = new QAction( tr( "Embed File…" ), sourceMenu );
61 connect( embedFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::embedFile );
62 sourceMenu->addAction( embedFileAction );
63
64 QAction *extractFileAction = new QAction( tr( "Extract Embedded File…" ), sourceMenu );
65 connect( extractFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::extractFile );
66 sourceMenu->addAction( extractFileAction );
67
68 connect( sourceMenu, &QMenu::aboutToShow, this, [this, extractFileAction] {
69 extractFileAction->setEnabled( mMode == ModeBase64 );
70 } );
71
72 QAction *enterUrlAction = new QAction( tr( "From URL…" ), sourceMenu );
73 connect( enterUrlAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::selectUrl );
74 sourceMenu->addAction( enterUrlAction );
75
76 mFileToolButton->setMenu( sourceMenu );
77 mFileToolButton->setPopupMode( QToolButton::MenuButtonPopup );
78 connect( mFileToolButton, &QToolButton::clicked, this, &QgsAbstractFileContentSourceLineEdit::selectFile );
79
80 connect( mFileLineEdit, &QLineEdit::textEdited, this, &QgsAbstractFileContentSourceLineEdit::mFileLineEdit_textEdited );
81 connect( mFileLineEdit, &QgsFilterLineEdit::cleared, this, [this] {
82 mMode = ModeFile;
83 mFileLineEdit->setPlaceholderText( QString() );
84 mBase64.clear();
85 emit sourceChanged( QString() );
86 } );
87
88 mPropertyOverrideButton->setVisible( mPropertyOverrideButtonVisible );
89}
90
92{
93 switch ( mMode )
94 {
95 case ModeFile:
96 return mFileLineEdit->text();
97
98 case ModeBase64:
99 return mBase64;
100 }
101
102 return QString();
103}
104
106{
107 mLastPathKey = key;
108}
109
111{
112 mPropertyOverrideButtonVisible = visible;
113 mPropertyOverrideButton->setVisible( visible );
114}
115
117{
118 const bool isBase64 = source.startsWith( QLatin1String( "base64:" ), Qt::CaseInsensitive );
119
120 if ( ( !isBase64 && source == mFileLineEdit->text() && mBase64.isEmpty() ) || ( isBase64 && source == mBase64 ) )
121 return;
122
123 if ( isBase64 )
124 {
125 mMode = ModeBase64;
126 mBase64 = source;
127 mFileLineEdit->clear();
128 mFileLineEdit->setPlaceholderText( tr( "Embedded file" ) );
129 }
130 else
131 {
132 mMode = ModeFile;
133 mBase64.clear();
134 mFileLineEdit->setText( source );
135 mFileLineEdit->setPlaceholderText( QString() );
136 }
137
138 emit sourceChanged( source );
139}
140
141void QgsAbstractFileContentSourceLineEdit::selectFile()
142{
143 QgsSettings s;
144 const QString file = QFileDialog::getOpenFileName( nullptr, selectFileTitle(), defaultPath(), fileFilter() );
145 const QFileInfo fi( file );
146 if ( file.isEmpty() || !fi.exists() || file == source() )
147 {
148 return;
149 }
150 mMode = ModeFile;
151 mBase64.clear();
152 mFileLineEdit->setText( file );
153 mFileLineEdit->setPlaceholderText( QString() );
154 s.setValue( settingsKey(), fi.absolutePath() );
155 emit sourceChanged( mFileLineEdit->text() );
156}
157
158void QgsAbstractFileContentSourceLineEdit::selectUrl()
159{
160 bool ok = false;
161 const QString path = QInputDialog::getText( this, fileFromUrlTitle(), fileFromUrlText(), QLineEdit::Normal, mFileLineEdit->text(), &ok );
162 if ( ok && path != source() )
163 {
164 mMode = ModeFile;
165 mBase64.clear();
166 mFileLineEdit->setText( path );
167 mFileLineEdit->setPlaceholderText( QString() );
168 emit sourceChanged( mFileLineEdit->text() );
169 }
170}
171
172void QgsAbstractFileContentSourceLineEdit::embedFile()
173{
174 QgsSettings s;
175 const QString file = QFileDialog::getOpenFileName( nullptr, embedFileTitle(), defaultPath(), fileFilter() );
176 const QFileInfo fi( file );
177 if ( file.isEmpty() || !fi.exists() )
178 {
179 return;
180 }
181
182 s.setValue( settingsKey(), fi.absolutePath() );
183
184 // encode file as base64
185 QFile fileSource( file );
186 if ( !fileSource.open( QIODevice::ReadOnly ) )
187 {
188 return;
189 }
190
191 const QByteArray blob = fileSource.readAll();
192 const QByteArray encoded = blob.toBase64();
193
194 QString path( encoded );
195 path.prepend( QLatin1String( "base64:" ) );
196 if ( path == source() )
197 return;
198
199 mBase64 = path;
200 mMode = ModeBase64;
201
202 mFileLineEdit->clear();
203 mFileLineEdit->setPlaceholderText( tr( "Embedded file" ) );
204
205 emit sourceChanged( path );
206}
207
208void QgsAbstractFileContentSourceLineEdit::extractFile()
209{
210 QgsSettings s;
211 const QString file = QFileDialog::getSaveFileName( nullptr, extractFileTitle(), defaultPath(), fileFilter() );
212 // return dialog focus on Mac
213 activateWindow();
214 raise();
215 if ( file.isEmpty() )
216 {
217 return;
218 }
219
220 const QFileInfo fi( file );
221 s.setValue( settingsKey(), fi.absolutePath() );
222
223 // decode current base64 embedded file
224 const QByteArray base64 = mBase64.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix
225 const QByteArray decoded = QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
226
227 QFile fileOut( file );
228 if ( fileOut.open( QIODevice::WriteOnly ) )
229 {
230 fileOut.write( decoded );
231 fileOut.close();
232
233 if ( mMessageBar )
234 {
235 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 );
236 }
237 }
238 else if ( mMessageBar )
239 {
240 mMessageBar->pushMessage( extractFileTitle(), tr( "Error opening %1 for write" ).arg( QDir::toNativeSeparators( file ) ), Qgis::MessageLevel::Critical );
241 }
242}
243
244void QgsAbstractFileContentSourceLineEdit::mFileLineEdit_textEdited( const QString &text )
245{
246 mFileLineEdit->setPlaceholderText( QString() );
247 mBase64.clear();
248 mMode = ModeFile;
249 if ( !text.isEmpty() && !QFileInfo::exists( text ) )
250 {
251 const QUrl url( text );
252 if ( !url.isValid() )
253 {
254 return;
255 }
256 }
257 emit sourceChanged( text );
258}
259
260QString QgsAbstractFileContentSourceLineEdit::defaultPath() const
261{
262 if ( QFileInfo::exists( source() ) )
263 return source();
264
265 return QgsSettings().value( settingsKey(), QDir::homePath() ).toString();
266}
267
268QString QgsAbstractFileContentSourceLineEdit::settingsKey() const
269{
270 return mLastPathKey.isEmpty() ? defaultSettingsKey() : mLastPathKey;
271}
272
274{
275 mMessageBar = bar;
276}
277
279{
280 return mMessageBar;
281}
282
283
284//
285// QgsPictureSourceLineEditBase
286//
287
289
290
291QString QgsPictureSourceLineEditBase::fileFilter() const
292{
293 switch ( mFormat )
294 {
295 case Svg:
296 return tr( "SVG files" ) + " (*.svg)";
297 case Image:
298 {
299 QStringList formatsFilter;
300 const QByteArrayList supportedFormats = QImageReader::supportedImageFormats();
301 for ( const auto &format : supportedFormats )
302 {
303 formatsFilter.append( QString( QStringLiteral( "*.%1" ) ).arg( QString( format ) ) );
304 }
305 return QString( "%1 (%2);;%3 (*.*)" ).arg( tr( "Images" ), formatsFilter.join( QLatin1Char( ' ' ) ), tr( "All files" ) );
306 }
307
308 case AnimatedImage:
309 {
310 QStringList formatsFilter;
311 const QByteArrayList supportedFormats = QMovie::supportedFormats();
312 for ( const auto &format : supportedFormats )
313 {
314 formatsFilter.append( QString( QStringLiteral( "*.%1" ) ).arg( QString( format ) ) );
315 }
316 return QString( "%1 (%2);;%3 (*.*)" ).arg( tr( "Animated Images" ), formatsFilter.join( QLatin1Char( ' ' ) ), tr( "All files" ) );
317 }
318 }
320}
321
322QString QgsPictureSourceLineEditBase::selectFileTitle() const
323{
324 switch ( mFormat )
325 {
326 case Svg:
327 return tr( "Select SVG File" );
328 case Image:
329 return tr( "Select Image File" );
330 case AnimatedImage:
331 return tr( "Select Animated Image File" );
332 }
334}
335
336QString QgsPictureSourceLineEditBase::fileFromUrlTitle() const
337{
338 switch ( mFormat )
339 {
340 case Svg:
341 return tr( "SVG From URL" );
342 case Image:
343 return tr( "Image From URL" );
344 case AnimatedImage:
345 return tr( "Animated Image From URL" );
346 }
348}
349
350QString QgsPictureSourceLineEditBase::fileFromUrlText() const
351{
352 switch ( mFormat )
353 {
354 case Svg:
355 return tr( "Enter SVG URL" );
356 case Image:
357 return tr( "Enter image URL" );
358 case AnimatedImage:
359 return tr( "Enter animated image URL" );
360 }
362}
363
364QString QgsPictureSourceLineEditBase::embedFileTitle() const
365{
366 switch ( mFormat )
367 {
368 case Svg:
369 return tr( "Embed SVG File" );
370 case Image:
371 return tr( "Embed Image File" );
372 case AnimatedImage:
373 return tr( "Embed Animated Image File" );
374 }
376}
377
378QString QgsPictureSourceLineEditBase::extractFileTitle() const
379{
380 switch ( mFormat )
381 {
382 case Svg:
383 return tr( "Extract SVG File" );
384 case Image:
385 return tr( "Extract Image File" );
386 case AnimatedImage:
387 return tr( "Extract Animated Image File" );
388 }
390}
391
392QString QgsPictureSourceLineEditBase::defaultSettingsKey() const
393{
394 switch ( mFormat )
395 {
396 case Svg:
397 return QStringLiteral( "/UI/lastSVGDir" );
398 case Image:
399 return QStringLiteral( "/UI/lastImageDir" );
400 case AnimatedImage:
401 return QStringLiteral( "/UI/lastAnimatedImageDir" );
402 }
404}
405
@ Critical
Critical/error message.
Definition qgis.h:159
@ Success
Used for reporting a successful operation.
Definition qgis.h:160
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 cleared()
Emitted when the widget is cleared.
A bar for displaying non-blocking messages to the user.
A button for controlling property overrides which may apply to a widget.
Stores settings for use within QGIS.
Definition qgssettings.h:65
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:7208