QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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  QgsDebugMsgLevel( QStringLiteral( "Available Filters Map: " ), 2 );
105  for ( QMap<QString, QString>::iterator it = filterMap.begin(); it != filterMap.end(); ++it )
106  {
107  QgsDebugMsgLevel( it.key() + " : " + it.value(), 2 );
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 }
QgsTemporaryCursorRestoreOverride::~QgsTemporaryCursorRestoreOverride
~QgsTemporaryCursorRestoreOverride()
Definition: qgsguiutils.cpp:318
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsGuiUtils::getFont
QFont getFont(bool &ok, const QFont &initial, const QString &title)
Show font selection dialog.
Definition: qgsguiutils.cpp:208
QgsEncodingFileDialog::cancelAll
bool cancelAll()
Returns true if the user clicked 'Cancel All'.
Definition: qgsencodingfiledialog.cpp:95
qgis.h
QgsSettings
Definition: qgssettings.h:61
QgsGuiUtils::saveGeometry
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
Definition: qgsguiutils.cpp:224
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsGuiUtils::restoreGeometry
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
Definition: qgsguiutils.cpp:231
QgsTemporaryCursorOverride::~QgsTemporaryCursorOverride
~QgsTemporaryCursorOverride()
Definition: qgsguiutils.cpp:289
QgsGuiUtils::iconSize
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
Definition: qgsguiutils.cpp:264
QgsEncodingFileDialog
Definition: qgsencodingfiledialog.h:30
QgsGuiUtils
Definition: qgsguiutils.cpp:28
QgsEncodingFileDialog::addCancelAll
void addCancelAll()
Adds a 'Cancel All' button for the user to click.
Definition: qgsencodingfiledialog.cpp:85
QgsTemporaryCursorRestoreOverride::restore
void restore()
Restores the cursor override early (i.e.
Definition: qgsguiutils.cpp:323
Qgis::UI_SCALE_FACTOR
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:195
qgsencodingfiledialog.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:289
QgsGuiUtils::getSaveAsImageName
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:102
QgsGuiUtils::createWidgetKey
QString createWidgetKey(QWidget *widget, const QString &keyName)
Creates a key for the given widget that can be used to store related data in settings.
Definition: qgsguiutils.cpp:238
QgsTemporaryCursorOverride::release
void release()
Releases the cursor override early (i.e.
Definition: qgsguiutils.cpp:295
qgssettings.h
QgsTemporaryCursorRestoreOverride::QgsTemporaryCursorRestoreOverride
QgsTemporaryCursorRestoreOverride()
Constructor for QgsTemporaryCursorRestoreOverride.
Definition: qgsguiutils.cpp:309
QgsTemporaryCursorOverride::QgsTemporaryCursorOverride
QgsTemporaryCursorOverride(const QCursor &cursor)
Constructor for QgsTemporaryCursorOverride.
Definition: qgsguiutils.cpp:284
qgslogger.h
qgsguiutils.h
QgsGuiUtils::scaleIconSize
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
Definition: qgsguiutils.cpp:257
QgsGuiUtils::openFilesRememberingFilter
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:44
QgsGuiUtils::createFileFilter_
QString createFileFilter_(QString const &longName, QString const &glob)
Convenience function for readily creating file filters.
Definition: qgsguiutils.cpp:196
QgsGuiUtils::panelIconSize
QSize panelIconSize(QSize size)
Returns dockable panel toolbar icon width based on the provided window toolbar width.
Definition: qgsguiutils.cpp:278