QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 #include <QMenu>
22 #include <QLineEdit>
23 #include <QToolButton>
24 #include <QHBoxLayout>
25 #include <QFileDialog>
26 #include <QInputDialog>
27 #include <QUrl>
28 
29 //
30 // QgsAbstractFileContentSourceLineEdit
31 //
32 
34  : QWidget( parent )
35 {
36  QHBoxLayout *layout = new QHBoxLayout( this );
37  layout->setContentsMargins( 0, 0, 0, 0 );
38  mFileLineEdit = new QgsFilterLineEdit( this );
39  mFileLineEdit->setShowClearButton( true );
40  mFileToolButton = new QToolButton( this );
41  mFileToolButton->setText( QString( QChar( 0x2026 ) ) );
42  mPropertyOverrideButton = new QgsPropertyOverrideButton( this );
43  layout->addWidget( mFileLineEdit, 1 );
44  layout->addWidget( mFileToolButton );
45  layout->addWidget( mPropertyOverrideButton );
46  setLayout( layout );
47 
48  QMenu *sourceMenu = new QMenu( mFileToolButton );
49 
50  QAction *selectFileAction = new QAction( tr( "Select File…" ), sourceMenu );
51  connect( selectFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::selectFile );
52  sourceMenu->addAction( selectFileAction );
53 
54  QAction *embedFileAction = new QAction( tr( "Embed File…" ), sourceMenu );
55  connect( embedFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::embedFile );
56  sourceMenu->addAction( embedFileAction );
57 
58  QAction *extractFileAction = new QAction( tr( "Extract Embedded File…" ), sourceMenu );
59  connect( extractFileAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::extractFile );
60  sourceMenu->addAction( extractFileAction );
61 
62  connect( sourceMenu, &QMenu::aboutToShow, this, [this, extractFileAction]
63  {
64  extractFileAction->setEnabled( mMode == ModeBase64 );
65  } );
66 
67  QAction *enterUrlAction = new QAction( tr( "From URL…" ), sourceMenu );
68  connect( enterUrlAction, &QAction::triggered, this, &QgsAbstractFileContentSourceLineEdit::selectUrl );
69  sourceMenu->addAction( enterUrlAction );
70 
71  mFileToolButton->setMenu( sourceMenu );
72  mFileToolButton->setPopupMode( QToolButton::MenuButtonPopup );
73  connect( mFileToolButton, &QToolButton::clicked, this, &QgsAbstractFileContentSourceLineEdit::selectFile );
74 
75  connect( mFileLineEdit, &QLineEdit::textEdited, this, &QgsAbstractFileContentSourceLineEdit::mFileLineEdit_textEdited );
76  connect( mFileLineEdit, &QgsFilterLineEdit::cleared, this, [ = ]
77  {
78  mMode = ModeFile;
79  mFileLineEdit->setPlaceholderText( QString() );
80  mBase64.clear();
81  emit sourceChanged( QString() );
82  } );
83 
84  mPropertyOverrideButton->setVisible( mPropertyOverrideButtonVisible );
85 
86 }
87 
89 {
90  switch ( mMode )
91  {
92  case ModeFile:
93  return mFileLineEdit->text();
94 
95  case ModeBase64:
96  return mBase64;
97  }
98 
99  return QString();
100 }
101 
103 {
104  mLastPathKey = key;
105 }
106 
108 {
109  mPropertyOverrideButtonVisible = visible;
110  mPropertyOverrideButton->setVisible( visible );
111 }
112 
114 {
115  const bool isBase64 = source.startsWith( QLatin1String( "base64:" ), Qt::CaseInsensitive );
116 
117  if ( ( !isBase64 && source == mFileLineEdit->text() && mBase64.isEmpty() ) || ( isBase64 && source == mBase64 ) )
118  return;
119 
120  if ( isBase64 )
121  {
122  mMode = ModeBase64;
123  mBase64 = source;
124  mFileLineEdit->clear();
125  mFileLineEdit->setPlaceholderText( tr( "Embedded file" ) );
126  }
127  else
128  {
129  mMode = ModeFile;
130  mBase64.clear();
131  mFileLineEdit->setText( source );
132  mFileLineEdit->setPlaceholderText( QString() );
133  }
134 
135  emit sourceChanged( source );
136 }
137 
138 void QgsAbstractFileContentSourceLineEdit::selectFile()
139 {
140  QgsSettings s;
141  QString file = QFileDialog::getOpenFileName( nullptr,
142  selectFileTitle(),
143  defaultPath(),
144  fileFilter() );
145  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 
158 void 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 
172 void QgsAbstractFileContentSourceLineEdit::embedFile()
173 {
174  QgsSettings s;
175  QString file = QFileDialog::getOpenFileName( nullptr,
176  embedFileTitle(),
177  defaultPath(),
178  fileFilter() );
179  QFileInfo fi( file );
180  if ( file.isEmpty() || !fi.exists() )
181  {
182  return;
183  }
184 
185  s.setValue( settingsKey(), fi.absolutePath() );
186 
187  // encode file as base64
188  QFile fileSource( file );
189  if ( !fileSource.open( QIODevice::ReadOnly ) )
190  {
191  return;
192  }
193 
194  QByteArray blob = fileSource.readAll();
195  QByteArray encoded = blob.toBase64();
196 
197  QString path( encoded );
198  path.prepend( QLatin1String( "base64:" ) );
199  if ( path == source() )
200  return;
201 
202  mBase64 = path;
203  mMode = ModeBase64;
204 
205  mFileLineEdit->clear();
206  mFileLineEdit->setPlaceholderText( tr( "Embedded file" ) );
207 
208  emit sourceChanged( path );
209 }
210 
211 void QgsAbstractFileContentSourceLineEdit::extractFile()
212 {
213  QgsSettings s;
214  QString file = QFileDialog::getSaveFileName( nullptr,
215  extractFileTitle(),
216  defaultPath(),
217  fileFilter() );
218  if ( file.isEmpty() )
219  {
220  return;
221  }
222 
223  QFileInfo fi( file );
224  s.setValue( settingsKey(), fi.absolutePath() );
225 
226  // decode current base64 embedded file
227  QByteArray base64 = mBase64.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix
228  QByteArray decoded = QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
229 
230  QFile fileOut( file );
231  fileOut.open( QIODevice::WriteOnly );
232  fileOut.write( decoded );
233  fileOut.close();
234 
235  if ( mMessageBar )
236  {
237  mMessageBar->pushMessage( extractFileTitle(),
238  tr( "Successfully extracted file to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( file ).toString(), QDir::toNativeSeparators( file ) ),
239  Qgis::Success, 0 );
240  }
241 }
242 
243 void QgsAbstractFileContentSourceLineEdit::mFileLineEdit_textEdited( const QString &text )
244 {
245  mFileLineEdit->setPlaceholderText( QString() );
246  mBase64.clear();
247  mMode = ModeFile;
248  if ( !text.isEmpty() && !QFileInfo::exists( text ) )
249  {
250  QUrl url( text );
251  if ( !url.isValid() )
252  {
253  return;
254  }
255  }
256  emit sourceChanged( text );
257 }
258 
259 QString QgsAbstractFileContentSourceLineEdit::defaultPath() const
260 {
261  if ( QFileInfo::exists( source() ) )
262  return source();
263 
264  return QgsSettings().value( settingsKey(), QDir::homePath() ).toString();
265 }
266 
267 QString QgsAbstractFileContentSourceLineEdit::settingsKey() const
268 {
269  return mLastPathKey.isEmpty() ? defaultSettingsKey() : mLastPathKey;
270 }
271 
272 //
273 // QgsSvgSourceLineEdit
274 //
275 
277 
278 QString QgsSvgSourceLineEdit::fileFilter() const
279 {
280  return tr( "SVG files" ) + " (*.svg)";
281 }
282 
283 QString QgsSvgSourceLineEdit::selectFileTitle() const
284 {
285  return tr( "Select SVG File" );
286 }
287 
288 QString QgsSvgSourceLineEdit::fileFromUrlTitle() const
289 {
290  return tr( "SVG From URL" );
291 }
292 
293 QString QgsSvgSourceLineEdit::fileFromUrlText() const
294 {
295  return tr( "Enter SVG URL" );
296 }
297 
298 QString QgsSvgSourceLineEdit::embedFileTitle() const
299 {
300  return tr( "Embed SVG File" );
301 }
302 
303 QString QgsSvgSourceLineEdit::extractFileTitle() const
304 {
305  return tr( "Extract SVG File" );
306 }
307 
308 QString QgsSvgSourceLineEdit::defaultSettingsKey() const
309 {
310  return QStringLiteral( "/UI/lastSVGDir" );
311 }
313 
314 //
315 // QgsImageSourceLineEdit
316 //
317 
319 
320 QString QgsImageSourceLineEdit::fileFilter() const
321 {
322  return tr( "All files" ) + " (*.*)";
323 }
324 
325 QString QgsImageSourceLineEdit::selectFileTitle() const
326 {
327  return tr( "Select Image File" );
328 }
329 
330 QString QgsImageSourceLineEdit::fileFromUrlTitle() const
331 {
332  return tr( "Image From URL" );
333 }
334 
335 QString QgsImageSourceLineEdit::fileFromUrlText() const
336 {
337  return tr( "Enter image URL" );
338 }
339 
340 QString QgsImageSourceLineEdit::embedFileTitle() const
341 {
342  return tr( "Embed Image File" );
343 }
344 
345 QString QgsImageSourceLineEdit::extractFileTitle() const
346 {
347  return tr( "Extract Image File" );
348 }
349 
350 QString QgsImageSourceLineEdit::defaultSettingsKey() const
351 {
352  return QStringLiteral( "/UI/lastImageDir" );
353 }
354 
356 
358 {
359  mMessageBar = bar;
360 }
361 
363 {
364  return mMessageBar;
365 }
@ Success
Definition: qgis.h:93
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.
Definition: qgsmessagebar.h:61
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::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:62
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.