QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 
22 #include <QImageWriter>
23 #include <QFontDialog>
24 #include <QApplication>
25 
26 
27 namespace QgsGuiUtils
28 {
29 
30  bool GUI_EXPORT openFilesRememberingFilter( QString const &filterName,
31  QString const &filters, QStringList &selectedFiles, QString &enc, QString &title,
32  bool cancelAll )
33  {
34  Q_UNUSED( enc );
35 
36  QgsSettings settings;
37  QString lastUsedFilter = settings.value( "/UI/" + filterName, "" ).toString();
38  QString lastUsedDir = settings.value( "/UI/" + filterName + "Dir", QDir::homePath() ).toString();
39 
40  QgsDebugMsg( "Opening file dialog with filters: " + filters );
41  if ( !cancelAll )
42  {
43  selectedFiles = QFileDialog::getOpenFileNames( nullptr, title, lastUsedDir, filters, &lastUsedFilter );
44  }
45  else //we have to use non-native dialog to add cancel all button
46  {
47  QgsEncodingFileDialog *openFileDialog = new QgsEncodingFileDialog( nullptr, title, lastUsedDir, filters, QString() );
48 
49  // allow for selection of more than one file
50  openFileDialog->setFileMode( QFileDialog::ExistingFiles );
51 
52  if ( !lastUsedFilter.isEmpty() )
53  {
54  openFileDialog->selectNameFilter( lastUsedFilter );
55  }
56  openFileDialog->addCancelAll();
57  if ( openFileDialog->exec() == QDialog::Accepted )
58  {
59  selectedFiles = openFileDialog->selectedFiles();
60  }
61  else
62  {
63  //cancel or cancel all?
64  if ( openFileDialog->cancelAll() )
65  {
66  return true;
67  }
68  }
69  }
70 
71  if ( !selectedFiles.isEmpty() )
72  {
73  // Fix by Tim - getting the dirPath from the dialog
74  // directly truncates the last node in the dir path.
75  // This is a workaround for that
76  QString firstFileName = selectedFiles.first();
77  QFileInfo fi( firstFileName );
78  QString path = fi.path();
79 
80  QgsDebugMsg( "Writing last used dir: " + path );
81 
82  settings.setValue( "/UI/" + filterName, lastUsedFilter );
83  settings.setValue( "/UI/" + filterName + "Dir", path );
84  }
85  return false;
86  }
87 
88  QPair<QString, QString> GUI_EXPORT getSaveAsImageName( QWidget *parent, const QString &message, const QString &defaultFilename )
89  {
90  // get a list of supported output image types
91  QMap<QString, QString> filterMap;
92  Q_FOREACH ( const QByteArray &format, QImageWriter::supportedImageFormats() )
93  {
94  //svg doesn't work so skip it
95  if ( format == "svg" )
96  continue;
97 
98  filterMap.insert( createFileFilter_( format ), format );
99  }
100 
101 #ifdef QGISDEBUG
102  QgsDebugMsg( QStringLiteral( "Available Filters Map: " ) );
103  for ( QMap<QString, QString>::iterator it = filterMap.begin(); it != filterMap.end(); ++it )
104  {
105  QgsDebugMsg( it.key() + " : " + it.value() );
106  }
107 #endif
108 
109  QgsSettings settings; // where we keep last used filter in persistent state
110  QString lastUsedDir = settings.value( QStringLiteral( "UI/lastSaveAsImageDir" ), QDir::homePath() ).toString();
111 
112  // Prefer "png" format unless the user previously chose a different format
113  QString pngExtension = QStringLiteral( "png" );
114  QString pngFilter = createFileFilter_( pngExtension );
115  QString selectedFilter = settings.value( QStringLiteral( "UI/lastSaveAsImageFilter" ), pngFilter ).toString();
116 
117  QString initialPath;
118  if ( defaultFilename.isNull() )
119  {
120  //no default filename provided, just use last directory
121  initialPath = lastUsedDir;
122  }
123  else
124  {
125  //a default filename was provided, so use it to build the initial path
126  initialPath = QDir( lastUsedDir ).filePath( defaultFilename );
127  }
128 
129  QString outputFileName;
130  QString ext;
131 #if defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_LINUX)
132  outputFileName = QFileDialog::getSaveFileName( parent, message, initialPath, QStringList( filterMap.keys() ).join( QStringLiteral( ";;" ) ), &selectedFilter );
133 
134  if ( !outputFileName.isNull() )
135  {
136  ext = filterMap.value( selectedFilter, QString() );
137  if ( !ext.isNull() )
138  settings.setValue( QStringLiteral( "UI/lastSaveAsImageFilter" ), selectedFilter );
139  settings.setValue( QStringLiteral( "UI/lastSaveAsImageDir" ), QFileInfo( outputFileName ).absolutePath() );
140  }
141 #else
142 
143  //create a file dialog using the filter list generated above
144  std::unique_ptr<QFileDialog> fileDialog( new QFileDialog( parent, message, initialPath, QStringList( filterMap.keys() ).join( ";;" ) ) );
145 
146  // allow for selection of more than one file
147  fileDialog->setFileMode( QFileDialog::AnyFile );
148  fileDialog->setAcceptMode( QFileDialog::AcceptSave );
149  fileDialog->setOption( QFileDialog::DontConfirmOverwrite, false );
150 
151  if ( !selectedFilter.isEmpty() ) // set the filter to the last one used
152  {
153  fileDialog->selectNameFilter( selectedFilter );
154  }
155 
156  //prompt the user for a fileName
157  if ( fileDialog->exec() == QDialog::Accepted )
158  {
159  outputFileName = fileDialog->selectedFiles().first();
160  }
161 
162  selectedFilter = fileDialog->selectedNameFilter();
163  QgsDebugMsg( "Selected filter: " + selectedFilter );
164  ext = filterMap.value( selectedFilter, QString() );
165 
166  if ( !ext.isNull() )
167  settings.setValue( "/UI/lastSaveAsImageFilter", selectedFilter );
168 
169  settings.setValue( "/UI/lastSaveAsImageDir", fileDialog->directory().absolutePath() );
170 #endif
171 
172  // Add the file type suffix to the fileName if required
173  if ( !ext.isNull() && !outputFileName.endsWith( '.' + ext.toLower(), Qt::CaseInsensitive ) )
174  {
175  outputFileName += '.' + ext;
176  }
177 
178  return qMakePair<QString, QString>( outputFileName, ext );
179  }
180 
181  QString createFileFilter_( QString const &longName, QString const &glob )
182  {
183  return QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() );
184  }
185 
186  QString createFileFilter_( QString const &format )
187  {
188  QString longName = format.toUpper() + " format";
189  QString glob = "*." + format;
190  return createFileFilter_( longName, glob );
191  }
192 
193  QFont getFont( bool &ok, const QFont &initial, const QString &title )
194  {
195  // parent is intentionally not set to 'this' as
196  // that would make it follow the style sheet font
197  // see also #12233 and #4937
198 #if defined(Q_OS_MAC)
199  // Native dialog broken on macOS with Qt5
200  // probably only broken in Qt5.11.1 and .2
201  // (see https://successfulsoftware.net/2018/11/02/qt-is-broken-on-macos-right-now/ )
202  // possible upstream bug: https://bugreports.qt.io/browse/QTBUG-69878 (fixed in Qt 5.12 ?)
203  return QFontDialog::getFont( &ok, initial, nullptr, title, QFontDialog::DontUseNativeDialog );
204 #else
205  return QFontDialog::getFont( &ok, initial, nullptr, title );
206 #endif
207  }
208 
209  void saveGeometry( QWidget *widget, const QString &keyName )
210  {
211  QgsSettings settings;
212  QString key = createWidgetKey( widget, keyName );
213  settings.setValue( key, widget->saveGeometry() );
214  }
215 
216  bool restoreGeometry( QWidget *widget, const QString &keyName )
217  {
218  QgsSettings settings;
219  QString key = createWidgetKey( widget, keyName );
220  return widget->restoreGeometry( settings.value( key ).toByteArray() );
221  }
222 
223  QString createWidgetKey( QWidget *widget, const QString &keyName )
224  {
225  QString subKey;
226  if ( !keyName.isEmpty() )
227  {
228  subKey = keyName;
229  }
230  else if ( widget->objectName().isEmpty() )
231  {
232  subKey = QString( widget->metaObject()->className() );
233  }
234  else
235  {
236  subKey = widget->objectName();
237  }
238  QString key = QStringLiteral( "Windows/%1/geometry" ).arg( subKey );
239  return key;
240  }
241 
242  int scaleIconSize( int standardSize )
243  {
244  QFontMetrics fm( ( QFont() ) );
245  const double scale = 1.1 * standardSize / 24;
246  return static_cast< int >( std::floor( std::max( Qgis::UI_SCALE_FACTOR * fm.height() * scale, static_cast< double >( standardSize ) ) ) );
247  }
248 
249 }
250 
251 //
252 // QgsTemporaryCursorOverride
253 //
254 
256 {
257  QApplication::setOverrideCursor( cursor );
258 }
259 
261 {
262  if ( mHasOverride )
263  QApplication::restoreOverrideCursor();
264 }
265 
267 {
268  if ( !mHasOverride )
269  return;
270 
271  mHasOverride = false;
272  QApplication::restoreOverrideCursor();
273 }
A file dialog which lets the user select the preferred encoding type for a data provider.
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:152
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
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
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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:88
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.
The QgsGuiUtils namespace contains constants and helper functions used throughout the QGIS GUI...
Definition: qgsguiutils.cpp:27
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 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:30
QFont getFont(bool &ok, const QFont &initial, const QString &title)
Show font selection dialog.