QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 "qgssettings.h"
18 #include "qgsmessagebar.h"
19 #include "qgsfilterlineedit.h"
21 
22 #include <QFileDialog>
23 #include <QHBoxLayout>
24 #include <QImageReader>
25 #include <QInputDialog>
26 #include <QLineEdit>
27 #include <QMenu>
28 #include <QToolButton>
29 #include <QUrl>
30 #include <QMovie>
31 
32 //
33 // QgsAbstractFileContentSourceLineEdit
34 //
35 
37  : QWidget( parent )
38 {
39  QHBoxLayout *layout = new QHBoxLayout( this );
40  layout->setContentsMargins( 0, 0, 0, 0 );
41  mFileLineEdit = new QgsFilterLineEdit( this );
42  mFileLineEdit->setShowClearButton( true );
43  mFileToolButton = new QToolButton( this );
44  mFileToolButton->setText( QString( QChar( 0x2026 ) ) );
45  mPropertyOverrideButton = new QgsPropertyOverrideButton( this );
46  layout->addWidget( mFileLineEdit, 1 );
47  layout->addWidget( mFileToolButton );
48  layout->addWidget( mPropertyOverrideButton );
49  setLayout( layout );
50 
51  QMenu *sourceMenu = new QMenu( mFileToolButton );
52 
53  QAction *selectFileAction = new QAction( tr( "Select File…" ), sourceMenu );
54  connect( selectFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::selectFile );
55  sourceMenu->addAction( selectFileAction );
56 
57  QAction *embedFileAction = new QAction( tr( "Embed File…" ), sourceMenu );
58  connect( embedFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::embedFile );
59  sourceMenu->addAction( embedFileAction );
60 
61  QAction *extractFileAction = new QAction( tr( "Extract Embedded File…" ), sourceMenu );
62  connect( extractFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::extractFile );
63  sourceMenu->addAction( extractFileAction );
64 
65  connect( sourceMenu, &QMenu::aboutToShow, this, [this, extractFileAction]
66  {
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  {
81  mMode = ModeFile;
82  mFileLineEdit->setPlaceholderText( QString() );
83  mBase64.clear();
84  emit sourceChanged( QString() );
85  } );
86 
87  mPropertyOverrideButton->setVisible( mPropertyOverrideButtonVisible );
88 
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 
141 void QgsAbstractFileContentSourceLineEdit::selectFile()
142 {
143  QgsSettings s;
144  const QString file = QFileDialog::getOpenFileName( nullptr,
145  selectFileTitle(),
146  defaultPath(),
147  fileFilter() );
148  const QFileInfo fi( file );
149  if ( file.isEmpty() || !fi.exists() || file == source() )
150  {
151  return;
152  }
153  mMode = ModeFile;
154  mBase64.clear();
155  mFileLineEdit->setText( file );
156  mFileLineEdit->setPlaceholderText( QString() );
157  s.setValue( settingsKey(), fi.absolutePath() );
158  emit sourceChanged( mFileLineEdit->text() );
159 }
160 
161 void QgsAbstractFileContentSourceLineEdit::selectUrl()
162 {
163  bool ok = false;
164  const QString path = QInputDialog::getText( this, fileFromUrlTitle(), fileFromUrlText(), QLineEdit::Normal, mFileLineEdit->text(), &ok );
165  if ( ok && path != source() )
166  {
167  mMode = ModeFile;
168  mBase64.clear();
169  mFileLineEdit->setText( path );
170  mFileLineEdit->setPlaceholderText( QString() );
171  emit sourceChanged( mFileLineEdit->text() );
172  }
173 }
174 
175 void QgsAbstractFileContentSourceLineEdit::embedFile()
176 {
177  QgsSettings s;
178  const QString file = QFileDialog::getOpenFileName( nullptr,
179  embedFileTitle(),
180  defaultPath(),
181  fileFilter() );
182  const QFileInfo fi( file );
183  if ( file.isEmpty() || !fi.exists() )
184  {
185  return;
186  }
187 
188  s.setValue( settingsKey(), fi.absolutePath() );
189 
190  // encode file as base64
191  QFile fileSource( file );
192  if ( !fileSource.open( QIODevice::ReadOnly ) )
193  {
194  return;
195  }
196 
197  const QByteArray blob = fileSource.readAll();
198  const QByteArray encoded = blob.toBase64();
199 
200  QString path( encoded );
201  path.prepend( QLatin1String( "base64:" ) );
202  if ( path == source() )
203  return;
204 
205  mBase64 = path;
206  mMode = ModeBase64;
207 
208  mFileLineEdit->clear();
209  mFileLineEdit->setPlaceholderText( tr( "Embedded file" ) );
210 
211  emit sourceChanged( path );
212 }
213 
214 void QgsAbstractFileContentSourceLineEdit::extractFile()
215 {
216  QgsSettings s;
217  const QString file = QFileDialog::getSaveFileName( nullptr,
218  extractFileTitle(),
219  defaultPath(),
220  fileFilter() );
221  if ( file.isEmpty() )
222  {
223  return;
224  }
225 
226  const QFileInfo fi( file );
227  s.setValue( settingsKey(), fi.absolutePath() );
228 
229  // decode current base64 embedded file
230  const QByteArray base64 = mBase64.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix
231  const QByteArray decoded = QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
232 
233  QFile fileOut( file );
234  fileOut.open( QIODevice::WriteOnly );
235  fileOut.write( decoded );
236  fileOut.close();
237 
238  if ( mMessageBar )
239  {
240  mMessageBar->pushMessage( extractFileTitle(),
241  tr( "Successfully extracted file to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( file ).toString(), QDir::toNativeSeparators( file ) ),
242  Qgis::MessageLevel::Success, 0 );
243  }
244 }
245 
246 void QgsAbstractFileContentSourceLineEdit::mFileLineEdit_textEdited( const QString &text )
247 {
248  mFileLineEdit->setPlaceholderText( QString() );
249  mBase64.clear();
250  mMode = ModeFile;
251  if ( !text.isEmpty() && !QFileInfo::exists( text ) )
252  {
253  const QUrl url( text );
254  if ( !url.isValid() )
255  {
256  return;
257  }
258  }
259  emit sourceChanged( text );
260 }
261 
262 QString QgsAbstractFileContentSourceLineEdit::defaultPath() const
263 {
264  if ( QFileInfo::exists( source() ) )
265  return source();
266 
267  return QgsSettings().value( settingsKey(), QDir::homePath() ).toString();
268 }
269 
270 QString QgsAbstractFileContentSourceLineEdit::settingsKey() const
271 {
272  return mLastPathKey.isEmpty() ? defaultSettingsKey() : mLastPathKey;
273 }
274 
276 {
277  mMessageBar = bar;
278 }
279 
281 {
282  return mMessageBar;
283 }
284 
285 
286 
287 //
288 // QgsPictureSourceLineEditBase
289 //
290 
292 
293 
294 QString QgsPictureSourceLineEditBase::fileFilter() const
295 {
296  switch ( mFormat )
297  {
298  case Svg:
299  return tr( "SVG files" ) + " (*.svg)";
300  case Image:
301  {
302  QStringList formatsFilter;
303  const QByteArrayList supportedFormats = QImageReader::supportedImageFormats();
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( "Images" ), formatsFilter.join( QLatin1Char( ' ' ) ), tr( "All files" ) );
309  }
310 
311  case AnimatedImage:
312  {
313  QStringList formatsFilter;
314  const QByteArrayList supportedFormats = QMovie::supportedFormats();
315  for ( const auto &format : supportedFormats )
316  {
317  formatsFilter.append( QString( QStringLiteral( "*.%1" ) ).arg( QString( format ) ) );
318  }
319  return QString( "%1 (%2);;%3 (*.*)" ).arg( tr( "Animated Images" ), formatsFilter.join( QLatin1Char( ' ' ) ), tr( "All files" ) );
320  }
321  }
323 }
324 
325 QString QgsPictureSourceLineEditBase::selectFileTitle() const
326 {
327  switch ( mFormat )
328  {
329  case Svg:
330  return tr( "Select SVG File" );
331  case Image:
332  return tr( "Select Image File" );
333  case AnimatedImage:
334  return tr( "Select Animated Image File" );
335  }
337 }
338 
339 QString QgsPictureSourceLineEditBase::fileFromUrlTitle() const
340 {
341  switch ( mFormat )
342  {
343  case Svg:
344  return tr( "SVG From URL" );
345  case Image:
346  return tr( "Image From URL" );
347  case AnimatedImage:
348  return tr( "Animated Image From URL" );
349  }
351 }
352 
353 QString QgsPictureSourceLineEditBase::fileFromUrlText() const
354 {
355  switch ( mFormat )
356  {
357  case Svg:
358  return tr( "Enter SVG URL" );
359  case Image:
360  return tr( "Enter image URL" );
361  case AnimatedImage:
362  return tr( "Enter animated image URL" );
363  }
365 }
366 
367 QString QgsPictureSourceLineEditBase::embedFileTitle() const
368 {
369  switch ( mFormat )
370  {
371  case Svg:
372  return tr( "Embed SVG File" );
373  case Image:
374  return tr( "Embed Image File" );
375  case AnimatedImage:
376  return tr( "Embed Animated Image File" );
377  }
379 }
380 
381 QString QgsPictureSourceLineEditBase::extractFileTitle() const
382 {
383  switch ( mFormat )
384  {
385  case Svg:
386  return tr( "Extract SVG File" );
387  case Image:
388  return tr( "Extract Image File" );
389  case AnimatedImage:
390  return tr( "Extract Animated Image File" );
391  }
393 }
394 
395 QString QgsPictureSourceLineEditBase::defaultSettingsKey() const
396 {
397  switch ( mFormat )
398  {
399  case Svg:
400  return QStringLiteral( "/UI/lastSVGDir" );
401  case Image:
402  return QStringLiteral( "/UI/lastImageDir" );
403  case AnimatedImage:
404  return QStringLiteral( "/UI/lastAnimatedImageDir" );
405  }
407 }
408 
410 
411 
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
QgsAbstractFileContentSourceLineEdit::messageBar
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
Definition: qgsfilecontentsourcelineedit.cpp:280
qgsfilterlineedit.h
QgsFilterLineEdit
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
Definition: qgsfilterlineedit.h:39
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsPropertyOverrideButton
A button for controlling property overrides which may apply to a widget.
Definition: qgspropertyoverridebutton.h:50
QgsPictureSourceLineEditBase::AnimatedImage
@ AnimatedImage
Animated image (since QGIS 3.26)
Definition: qgsfilecontentsourcelineedit.h:202
QgsPictureSourceLineEditBase::Svg
@ Svg
SVG image.
Definition: qgsfilecontentsourcelineedit.h:200
QgsPictureSourceLineEditBase::Image
@ Image
Raster image.
Definition: qgsfilecontentsourcelineedit.h:201
QgsAbstractFileContentSourceLineEdit::QgsAbstractFileContentSourceLineEdit
QgsAbstractFileContentSourceLineEdit(QWidget *parent=nullptr)
Constructor for QgsAbstractFileContentSourceLineEdit, with the specified parent widget.
Definition: qgsfilecontentsourcelineedit.cpp:36
QgsMessageBar
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:60
BUILTIN_UNREACHABLE
#define BUILTIN_UNREACHABLE
Definition: qgis.h:2907
qgsmessagebar.h
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:279
QgsAbstractFileContentSourceLineEdit::setLastPathSettingsKey
void setLastPathSettingsKey(const QString &key)
Sets a specific settings key to use when storing the last used path for the file source.
Definition: qgsfilecontentsourcelineedit.cpp:105
qgsfilecontentsourcelineedit.h
QgsAbstractFileContentSourceLineEdit::setPropertyOverrideToolButtonVisible
void setPropertyOverrideToolButtonVisible(bool visible)
Sets the visibility of the property override tool button.
Definition: qgsfilecontentsourcelineedit.cpp:110
QgsAbstractFileContentSourceLineEdit::setSource
void setSource(const QString &source)
Sets a new source to show in the widget.
Definition: qgsfilecontentsourcelineedit.cpp:116
qgssettings.h
QgsFilterLineEdit::cleared
void cleared()
Emitted when the widget is cleared.
QgsAbstractFileContentSourceLineEdit::setMessageBar
void setMessageBar(QgsMessageBar *bar)
Sets the message bar associated with the widget.
Definition: qgsfilecontentsourcelineedit.cpp:275
QgsFilterLineEdit::setShowClearButton
void setShowClearButton(bool visible)
Sets whether the widget's clear button is visible.
Definition: qgsfilterlineedit.cpp:43
QgsAbstractFileContentSourceLineEdit::sourceChanged
void sourceChanged(const QString &source)
Emitted whenever the file source is changed in the widget.
qgspropertyoverridebutton.h
QgsMessageBar::pushMessage
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.
Definition: qgsmessagebar.cpp:405
QgsAbstractFileContentSourceLineEdit::source
QString source
Definition: qgsfilecontentsourcelineedit.h:43