QGIS API Documentation  3.9.0-Master (ff24ab53ef)
qgsguiutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsguiutils.cpp - Constants used throughout the QGIS GUI.
3  --------------------------------------
4  Date : 11-Jan-2006
5  Copyright : (C) 2006 by Tom Elwertowski
6  Email : telwertowski at users dot sourceforge dot net
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 #include "qgsguiutils.h"
16 
17 #include "qgssettings.h"
18 #include "qgsencodingfiledialog.h"
19 #include "qgslogger.h"
20 #include "qgis_gui.h"
21 #include "qgis.h"
22 
23 #include <QImageWriter>
24 #include <QFontDialog>
25 #include <QApplication>
26 
27 
28 namespace QgsGuiUtils
29 {
30 
31  bool GUI_EXPORT openFilesRememberingFilter( QString const &filterName,
32  QString const &filters, QStringList &selectedFiles, QString &enc, QString &title,
33  bool cancelAll )
34  {
35  Q_UNUSED( enc )
36 
37  QgsSettings settings;
38  QString lastUsedFilter = settings.value( "/UI/" + filterName, "" ).toString();
39  QString lastUsedDir = settings.value( "/UI/" + filterName + "Dir", QDir::homePath() ).toString();
40 
41  QgsDebugMsg( "Opening file dialog with filters: " + filters );
42  if ( !cancelAll )
43  {
44  selectedFiles = QFileDialog::getOpenFileNames( nullptr, title, lastUsedDir, filters, &lastUsedFilter );
45  }
46  else //we have to use non-native dialog to add cancel all button
47  {
48  QgsEncodingFileDialog *openFileDialog = new QgsEncodingFileDialog( nullptr, title, lastUsedDir, filters, QString() );
49 
50  // allow for selection of more than one file
51  openFileDialog->setFileMode( QFileDialog::ExistingFiles );
52 
53  if ( !lastUsedFilter.isEmpty() )
54  {
55  openFileDialog->selectNameFilter( lastUsedFilter );
56  }
57  openFileDialog->addCancelAll();
58  if ( openFileDialog->exec() == QDialog::Accepted )
59  {
60  selectedFiles = openFileDialog->selectedFiles();
61  }
62  else
63  {
64  //cancel or cancel all?
65  if ( openFileDialog->cancelAll() )
66  {
67  return true;
68  }
69  }
70  }
71 
72  if ( !selectedFiles.isEmpty() )
73  {
74  // Fix by Tim - getting the dirPath from the dialog
75  // directly truncates the last node in the dir path.
76  // This is a workaround for that
77  QString firstFileName = selectedFiles.first();
78  QFileInfo fi( firstFileName );
79  QString path = fi.path();
80 
81  QgsDebugMsg( "Writing last used dir: " + path );
82 
83  settings.setValue( "/UI/" + filterName, lastUsedFilter );
84  settings.setValue( "/UI/" + filterName + "Dir", path );
85  }
86  return false;
87  }
88 
89  QPair<QString, QString> GUI_EXPORT getSaveAsImageName( QWidget *parent, const QString &message, const QString &defaultFilename )
90  {
91  // get a list of supported output image types
92  QMap<QString, QString> filterMap;
93  const auto supportedImageFormats { QImageWriter::supportedImageFormats() };
94  for ( const QByteArray &format : supportedImageFormats )
95  {
96  //svg doesn't work so skip it
97  if ( format == "svg" )
98  continue;
99 
100  filterMap.insert( createFileFilter_( format ), format );
101  }
102 
103 #ifdef QGISDEBUG
104  QgsDebugMsg( QStringLiteral( "Available Filters Map: " ) );
105  for ( QMap<QString, QString>::iterator it = filterMap.begin(); it != filterMap.end(); ++it )
106  {
107  QgsDebugMsg( it.key() + " : " + it.value() );
108  }
109 #endif
110 
111  QgsSettings settings; // where we keep last used filter in persistent state
112  QString lastUsedDir = settings.value( QStringLiteral( "UI/lastSaveAsImageDir" ), QDir::homePath() ).toString();
113 
114  // Prefer "png" format unless the user previously chose a different format
115  QString pngExtension = QStringLiteral( "png" );
116  QString pngFilter = createFileFilter_( pngExtension );
117  QString selectedFilter = settings.value( QStringLiteral( "UI/lastSaveAsImageFilter" ), pngFilter ).toString();
118 
119  QString initialPath;
120  if ( defaultFilename.isNull() )
121  {
122  //no default filename provided, just use last directory
123  initialPath = lastUsedDir;
124  }
125  else
126  {
127  //a default filename was provided, so use it to build the initial path
128  initialPath = QDir( lastUsedDir ).filePath( defaultFilename );
129  }
130 
131  QString outputFileName;
132  QString ext;
133 #if defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_LINUX)
134  outputFileName = QFileDialog::getSaveFileName( parent, message, initialPath, QStringList( filterMap.keys() ).join( QStringLiteral( ";;" ) ), &selectedFilter );
135 
136  if ( !outputFileName.isNull() )
137  {
138  ext = filterMap.value( selectedFilter, QString() );
139  if ( !ext.isNull() )
140  settings.setValue( QStringLiteral( "UI/lastSaveAsImageFilter" ), selectedFilter );
141  settings.setValue( QStringLiteral( "UI/lastSaveAsImageDir" ), QFileInfo( outputFileName ).absolutePath() );
142  }
143 #else
144 
145  //create a file dialog using the filter list generated above
146  std::unique_ptr<QFileDialog> fileDialog( new QFileDialog( parent, message, initialPath, QStringList( filterMap.keys() ).join( ";;" ) ) );
147 
148  // allow for selection of more than one file
149  fileDialog->setFileMode( QFileDialog::AnyFile );
150  fileDialog->setAcceptMode( QFileDialog::AcceptSave );
151  fileDialog->setOption( QFileDialog::DontConfirmOverwrite, false );
152 
153  if ( !selectedFilter.isEmpty() ) // set the filter to the last one used
154  {
155  fileDialog->selectNameFilter( selectedFilter );
156  }
157 
158  //prompt the user for a fileName
159  if ( fileDialog->exec() == QDialog::Accepted )
160  {
161  outputFileName = fileDialog->selectedFiles().first();
162  }
163 
164  selectedFilter = fileDialog->selectedNameFilter();
165  QgsDebugMsg( "Selected filter: " + selectedFilter );
166  ext = filterMap.value( selectedFilter, QString() );
167 
168  if ( !ext.isNull() )
169  settings.setValue( "/UI/lastSaveAsImageFilter", selectedFilter );
170 
171  settings.setValue( "/UI/lastSaveAsImageDir", fileDialog->directory().absolutePath() );
172 #endif
173 
174  // Add the file type suffix to the fileName if required
175  if ( !ext.isNull() && !outputFileName.endsWith( '.' + ext.toLower(), Qt::CaseInsensitive ) )
176  {
177  outputFileName += '.' + ext;
178  }
179 
180  return qMakePair<QString, QString>( outputFileName, ext );
181  }
182 
183  QString createFileFilter_( QString const &longName, QString const &glob )
184  {
185  return QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() );
186  }
187 
188  QString createFileFilter_( QString const &format )
189  {
190  QString longName = format.toUpper() + " format";
191  QString glob = "*." + format;
192  return createFileFilter_( longName, glob );
193  }
194 
195  QFont getFont( bool &ok, const QFont &initial, const QString &title )
196  {
197  // parent is intentionally not set to 'this' as
198  // that would make it follow the style sheet font
199  // see also #12233 and #4937
200 #if defined(Q_OS_MAC)
201  // Native dialog broken on macOS with Qt5
202  // probably only broken in Qt5.11.1 and .2
203  // (see https://successfulsoftware.net/2018/11/02/qt-is-broken-on-macos-right-now/ )
204  // possible upstream bug: https://bugreports.qt.io/browse/QTBUG-69878 (fixed in Qt 5.12 ?)
205  return QFontDialog::getFont( &ok, initial, nullptr, title, QFontDialog::DontUseNativeDialog );
206 #else
207  return QFontDialog::getFont( &ok, initial, nullptr, title );
208 #endif
209  }
210 
211  void saveGeometry( QWidget *widget, const QString &keyName )
212  {
213  QgsSettings settings;
214  QString key = createWidgetKey( widget, keyName );
215  settings.setValue( key, widget->saveGeometry() );
216  }
217 
218  bool restoreGeometry( QWidget *widget, const QString &keyName )
219  {
220  QgsSettings settings;
221  QString key = createWidgetKey( widget, keyName );
222  return widget->restoreGeometry( settings.value( key ).toByteArray() );
223  }
224 
225  QString createWidgetKey( QWidget *widget, const QString &keyName )
226  {
227  QString subKey;
228  if ( !keyName.isEmpty() )
229  {
230  subKey = keyName;
231  }
232  else if ( widget->objectName().isEmpty() )
233  {
234  subKey = QString( widget->metaObject()->className() );
235  }
236  else
237  {
238  subKey = widget->objectName();
239  }
240  QString key = QStringLiteral( "Windows/%1/geometry" ).arg( subKey );
241  return key;
242  }
243 
244  int scaleIconSize( int standardSize )
245  {
246  QFontMetrics fm( ( QFont() ) );
247  const double scale = 1.1 * standardSize / 24;
248  return static_cast< int >( std::floor( std::max( Qgis::UI_SCALE_FACTOR * fm.height() * scale, static_cast< double >( standardSize ) ) ) );
249  }
250 
251  QSize iconSize( bool dockableToolbar )
252  {
253  QgsSettings s;
254  int w = s.value( QStringLiteral( "/qgis/iconSize" ), 32 ).toInt();
255  QSize size( w, w );
256 
257  if ( dockableToolbar )
258  {
259  size = panelIconSize( size );
260  }
261 
262  return size;
263  }
264 
265  QSize panelIconSize( QSize size )
266  {
267  int adjustedSize = 16;
268  if ( size.width() > 32 )
269  {
270  adjustedSize = size.width() - 16;
271  }
272  else if ( size.width() == 32 )
273  {
274  adjustedSize = 24;
275  }
276  return QSize( adjustedSize, adjustedSize );
277  }
278 }
279 
280 //
281 // QgsTemporaryCursorOverride
282 //
283 
285 {
286  QApplication::setOverrideCursor( cursor );
287 }
288 
290 {
291  if ( mHasOverride )
292  QApplication::restoreOverrideCursor();
293 }
294 
296 {
297  if ( !mHasOverride )
298  return;
299 
300  mHasOverride = false;
301  QApplication::restoreOverrideCursor();
302 }
303 
304 
305 //
306 // QgsTemporaryCursorRestoreOverride
307 //
308 
310 {
311  while ( QApplication::overrideCursor() )
312  {
313  mCursors.emplace_back( QCursor( *QApplication::overrideCursor() ) );
314  QApplication::restoreOverrideCursor();
315  }
316 }
317 
319 {
320  restore();
321 }
322 
324 {
325  for ( auto it = mCursors.rbegin(); it != mCursors.rend(); ++it )
326  {
327  QApplication::setOverrideCursor( *it );
328  }
329  mCursors.clear();
330 }
A file dialog which lets the user select the preferred encoding type for a data provider.
QgsTemporaryCursorRestoreOverride()
Constructor for QgsTemporaryCursorRestoreOverride.
QString createFileFilter_(QString const &longName, QString const &glob)
Convenience function for readily creating file filters.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:139
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QPair< QString, QString > GUI_EXPORT getSaveAsImageName(QWidget *parent, const QString &message, const QString &defaultFilename)
A helper function to get an image name from the user.
Definition: qgsguiutils.cpp:89
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
QSize panelIconSize(QSize size)
Returns dockable panel toolbar icon width based on the provided window toolbar width.
The QgsGuiUtils namespace contains constants and helper functions used throughout the QGIS GUI...
Definition: qgsguiutils.cpp:28
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window&#39;s toolbar icons.
void addCancelAll()
Adds a &#39;Cancel All&#39; button for the user to click.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
void restore()
Restores the cursor override early (i.e.
void release()
Releases the cursor override early (i.e.
bool cancelAll()
Returns true if the user clicked &#39;Cancel All&#39;.
QString createWidgetKey(QWidget *widget, const QString &keyName)
Creates a key for the given widget that can be used to store related data in settings.
QgsTemporaryCursorOverride(const QCursor &cursor)
Constructor for QgsTemporaryCursorOverride.
bool GUI_EXPORT openFilesRememberingFilter(QString const &filterName, QString const &filters, QStringList &selectedFiles, QString &enc, QString &title, bool cancelAll)
Open files, preferring to have the default file selector be the last one used, if any; also...
Definition: qgsguiutils.cpp:31
QFont getFont(bool &ok, const QFont &initial, const QString &title)
Show font selection dialog.