QGIS API Documentation 4.3.0-Master (e0370aa57ef)
Loading...
Searching...
No Matches
qgssettingsentry.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssettingsentry.cpp
3 --------------------------------------
4 Date : February 2021
5 Copyright : (C) 2021 by Damiano Lombardi
6 Email : damiano at opengis dot ch
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
16#include "qgssettingsentry.h"
17
18#include "qgslogger.h"
19#include "qgssettings.h"
20#include "qgssettingstreenode.h"
21#include "qgsthreadingutils.h"
22
23#include <QCoreApplication>
24#include <QDir>
25#include <QFile>
26#include <QRegularExpression>
27#include <QSettings>
28#include <QString>
29
30using namespace Qt::StringLiterals;
31
32/*
33 * Set to true by setupUserSettings() once QSettings::setDefaultFormat()
34 * and setPath() have been called. Each thread's QSettings instance is
35 * (re-)created on first access after this flag becomes true, so it
36 * picks up the correct IniFormat and profile path.
37 */
38static bool sSettingsInitialized = false;
39
40static QSettings &sUserSettings()
41{
42 thread_local std::unique_ptr<QSettings> sSettings;
43 thread_local bool sCreatedBeforeInit = true;
44
45 if ( !sSettings || ( sCreatedBeforeInit && sSettingsInitialized ) )
46 {
47 sSettings = std::make_unique< QSettings >();
48 sCreatedBeforeInit = !sSettingsInitialized;
49 }
50 return *sSettings;
51}
52
53void QgsSettingsEntryBase::setupUserSettings( const QString &profilePath )
54{
56
57 if ( sSettingsInitialized )
58 return;
59
60 QSettings::setDefaultFormat( QSettings::IniFormat );
61 QSettings::setPath( QSettings::IniFormat, QSettings::UserScope, profilePath );
62 sSettingsInitialized = true;
63}
64
66{
67 return sUserSettings();
68}
69
70QHash<QString, QVariant> QgsSettingsEntryBase::sGlobalDefaults;
71
73{
74 // if this is called before the application is created
75 if ( QCoreApplication::instance() )
76 {
78 }
79
80 static bool sGlobalDefaultsLoaded = false;
81 if ( sGlobalDefaultsLoaded )
82 QgsDebugMsgLevel( u"Global settings are being reloaded from %1"_s.arg( path ), 2 );
83 sGlobalDefaultsLoaded = true;
84
85 sGlobalDefaults.clear();
86 if ( path.isEmpty() || !QFile::exists( path ) )
87 return false;
88
89 const QSettings globalSettings( path, QSettings::IniFormat );
90 const QStringList keys = globalSettings.allKeys();
91 sGlobalDefaults.reserve( keys.size() );
92 for ( const QString &key : keys )
93 {
94 sGlobalDefaults.insert( key, globalSettings.value( key ) );
95 }
96
97 return true;
98}
99
101{
102 return sGlobalDefaults.contains( sanitizeGlobalKey( key ) );
103}
104
105QVariant QgsSettingsEntryBase::globalDefault( const QString &key )
106{
107 return sGlobalDefaults.value( sanitizeGlobalKey( key ) );
108}
109
110QStringList QgsSettingsEntryBase::globalChildGroups( const QString &prefix )
111{
112 QString normalizedPrefix = sanitizeGlobalKey( prefix );
113 if ( !normalizedPrefix.endsWith( '/' ) )
114 normalizedPrefix.append( '/' );
115
116 const int prefixLen = normalizedPrefix.size();
117 QStringList result;
118
119 for ( QHash<QString, QVariant>::const_iterator it = sGlobalDefaults.constBegin(); it != sGlobalDefaults.constEnd(); ++it )
120 {
121 if ( it.key().startsWith( normalizedPrefix ) )
122 {
123 const int slashPos = it.key().indexOf( '/', prefixLen );
124 const QString group = ( slashPos < 0 ) ? it.key().mid( prefixLen ) : it.key().mid( prefixLen, slashPos - prefixLen );
125 if ( !group.isEmpty() && !result.contains( group ) )
126 result.append( group );
127 }
128 }
129 return result;
130}
131
132QString QgsSettingsEntryBase::sanitizeGlobalKey( const QString &key )
133{
134 if ( key.startsWith( '/' ) )
135 return key.mid( 1 );
136 return key;
137}
138
139QVariant QgsSettingsEntryBase::valueFromSettingsWithGlobalDefault( const QString &resolvedKey, const QVariant &defaultValue ) const
140{
141 if ( sUserSettings().contains( resolvedKey ) )
142 return sUserSettings().value( resolvedKey );
143
144 // sGlobalDefaults is populated once on the main thread before any worker
145 // threads start, so concurrent reads are safe (no concurrent modification).
146 const QHash<QString, QVariant>::const_iterator it = sGlobalDefaults.constFind( sanitizeGlobalKey( resolvedKey ) );
147 if ( it != sGlobalDefaults.constEnd() )
148 return it.value();
149
150 return defaultValue;
151}
152
154 : mParentTreeElement( parent )
155 , mName( key )
156 , mDefaultValue( defaultValue )
157 , mDescription( description )
158 , mOptions( options )
159{
160 mKey = QDir::cleanPath( u"%1/%2"_s.arg( parent ? parent->completeKey() : QString(), key ) );
161
162 if ( parent )
163 {
164 parent->registerChildSetting( this, key );
165 }
166}
167
169{
170 if ( mParentTreeElement )
171 mParentTreeElement->unregisterChildSetting( this );
172}
173
175{
176 return QString::fromUtf8( sSettingsTypeMetaEnum.valueToKey( static_cast<int>( settingsType() ) ) );
177}
178
179
180QString QgsSettingsEntryBase::key( const QString &dynamicKeyPart ) const
181{
182 return key( dynamicKeyPartToList( dynamicKeyPart ) );
183}
184
185QString QgsSettingsEntryBase::key( const QStringList &dynamicKeyPartList ) const
186{
187 return completeKeyPrivate( mKey, dynamicKeyPartList );
188}
189
190QString QgsSettingsEntryBase::completeKeyPrivate( const QString &key, const QStringList &dynamicKeyPartList ) const
191{
192 QString completeKey = key;
193
194 if ( dynamicKeyPartList.isEmpty() )
195 {
196 if ( hasDynamicKey() )
197 QgsDebugError( u"Settings '%1' have a dynamic key but the dynamic key part was not provided"_s.arg( completeKey ) );
198
199 return completeKey;
200 }
201 else
202 {
203 if ( !hasDynamicKey() )
204 {
205 QgsDebugError( u"Settings '%1' don't have a dynamic key, the provided dynamic key part will be ignored"_s.arg( completeKey ) );
206 return completeKey;
207 }
208
209 for ( int i = 0; i < dynamicKeyPartList.size(); i++ )
210 {
211 completeKey.replace( u"%"_s.append( QString::number( i + 1 ) ), dynamicKeyPartList.at( i ) );
212 }
213 }
214 return completeKey;
215}
216
217bool QgsSettingsEntryBase::keyIsValid( const QString &key ) const
218{
219 if ( !hasDynamicKey() )
220 {
221 if ( !key.contains( definitionKey() ) )
222 return false;
223 else
224 return key == definitionKey();
225 }
226
227 const thread_local QRegularExpression digitRx( u"%\\d+"_s );
228 const QRegularExpression regularExpression( definitionKey().replace( digitRx, u".*"_s ) );
229 const QRegularExpressionMatch regularExpressionMatch = regularExpression.match( key );
230 return regularExpressionMatch.hasMatch();
231}
232
234{
235 return mKey;
236}
237
239{
240 const thread_local QRegularExpression regularExpression( u"%\\d+"_s );
241 return mKey.contains( regularExpression );
242}
243
244bool QgsSettingsEntryBase::exists( const QString &dynamicKeyPart ) const
245{
246 const QString resolvedKey = key( dynamicKeyPart );
247 return sUserSettings().contains( resolvedKey ) || sGlobalDefaults.contains( sanitizeGlobalKey( resolvedKey ) );
248}
249
250bool QgsSettingsEntryBase::exists( const QStringList &dynamicKeyPartList ) const
251{
252 const QString resolvedKey = key( dynamicKeyPartList );
253 return sUserSettings().contains( resolvedKey ) || sGlobalDefaults.contains( sanitizeGlobalKey( resolvedKey ) );
254}
255
256Qgis::SettingsOrigin QgsSettingsEntryBase::origin( const QStringList &dynamicKeyPartList ) const
257{
258 const QString resolvedKey = key( dynamicKeyPartList );
259
260 if ( sGlobalDefaults.contains( sanitizeGlobalKey( resolvedKey ) ) )
262
263 if ( sUserSettings().contains( resolvedKey ) )
265
267}
268
269void QgsSettingsEntryBase::remove( const QString &dynamicKeyPart ) const
270{
271 sUserSettings().remove( key( dynamicKeyPart ) );
272}
273
274void QgsSettingsEntryBase::remove( const QStringList &dynamicKeyPartList ) const
275{
276 sUserSettings().remove( key( dynamicKeyPartList ) );
277}
278
283
284bool QgsSettingsEntryBase::setVariantValue( const QVariant &value, const QString &dynamicKeyPart ) const
285{
286 return setVariantValue( value, dynamicKeyPartToList( dynamicKeyPart ) );
287}
288
289bool QgsSettingsEntryBase::setVariantValue( const QVariant &value, const QStringList &dynamicKeyPartList ) const
290{
291 mHasChanged = true;
292 const QString resolvedKey = key( dynamicKeyPartList );
293
294 if ( mOptions.testFlag( Qgis::SettingsOption::SaveFormerValue ) )
295 {
296 if ( exists( dynamicKeyPartList ) )
297 {
298 QVariant currentValue = valueAsVariant( dynamicKeyPartList );
299 if ( value != currentValue )
300 {
301 sUserSettings().setValue( formerValuekey( dynamicKeyPartList ), currentValue );
302 }
303 }
304 }
305
306 // If the new value matches a global default, remove the user override
307 const QString sanitizedKey = sanitizeGlobalKey( resolvedKey );
308 if ( sGlobalDefaults.contains( sanitizedKey ) && sGlobalDefaults.value( sanitizedKey ) == value )
309 {
310 sUserSettings().remove( resolvedKey );
311 }
312 else
313 {
314 sUserSettings().setValue( resolvedKey, value );
315 }
316 return true;
317}
318
319QStringList QgsSettingsEntryBase::dynamicKeyPartToList( const QString &dynamicKeyPart )
320{
321 QStringList dynamicKeyPartList;
322 if ( !dynamicKeyPart.isNull() )
323 dynamicKeyPartList.append( dynamicKeyPart );
324 return dynamicKeyPartList;
325}
326
327QVariant QgsSettingsEntryBase::valueAsVariant( const QString &dynamicKeyPart ) const
328{
329 return valueAsVariant( dynamicKeyPartToList( dynamicKeyPart ) );
330}
331
332QVariant QgsSettingsEntryBase::valueAsVariant( const QStringList &dynamicKeyPartList ) const
333{
334 return valueFromSettingsWithGlobalDefault( key( dynamicKeyPartList ), mDefaultValue );
335}
336
337QVariant QgsSettingsEntryBase::valueAsVariant( const QString &dynamicKeyPart, bool useDefaultValueOverride, const QVariant &defaultValueOverride ) const
338{
340 return valueAsVariant( dynamicKeyPartToList( dynamicKeyPart ), useDefaultValueOverride, defaultValueOverride );
342}
343
344QVariant QgsSettingsEntryBase::valueAsVariant( const QStringList &dynamicKeyPartList, bool useDefaultValueOverride, const QVariant &defaultValueOverride ) const
345{
346 if ( useDefaultValueOverride )
347 return valueFromSettingsWithGlobalDefault( key( dynamicKeyPartList ), defaultValueOverride );
348 else
349 return valueFromSettingsWithGlobalDefault( key( dynamicKeyPartList ), mDefaultValue );
350}
351
352QVariant QgsSettingsEntryBase::valueAsVariantWithDefaultOverride( const QVariant &defaultValueOverride, const QString &dynamicKeyPart ) const
353{
354 return valueFromSettingsWithGlobalDefault( key( dynamicKeyPart ), defaultValueOverride );
355}
356
357QVariant QgsSettingsEntryBase::valueAsVariantWithDefaultOverride( const QVariant &defaultValueOverride, const QStringList &dynamicKeyPartList ) const
358{
359 return valueFromSettingsWithGlobalDefault( key( dynamicKeyPartList ), defaultValueOverride );
360}
361
363{
364 return mDefaultValue;
365}
366
368{
369 return mDescription;
370}
371
372QVariant QgsSettingsEntryBase::formerValueAsVariant( const QString &dynamicKeyPart ) const
373{
374 return formerValueAsVariant( dynamicKeyPartToList( dynamicKeyPart ) );
375}
376
377QVariant QgsSettingsEntryBase::formerValueAsVariant( const QStringList &dynamicKeyPartList ) const
378{
379 Q_ASSERT( mOptions.testFlag( Qgis::SettingsOption::SaveFormerValue ) );
380 QVariant defaultValueOverride = valueAsVariant( dynamicKeyPartList );
381 return sUserSettings().value( formerValuekey( dynamicKeyPartList ), defaultValueOverride );
382}
383
384bool QgsSettingsEntryBase::copyValueFromKey( const QString &key, const QStringList &dynamicKeyPartList, bool removeSettingAtKey ) const
385{
386 QSettings &settings = sUserSettings();
387
388 const QString oldCompleteKey = completeKeyPrivate( key, dynamicKeyPartList );
389
390 if ( settings.contains( oldCompleteKey ) )
391 {
392 if ( !exists( dynamicKeyPartList ) )
393 {
394 QVariant oldValue = settings.value( oldCompleteKey, mDefaultValue );
395 // do not copy if it is equal to the default value
396 if ( oldValue != defaultValueAsVariant() )
397 setVariantValue( oldValue, dynamicKeyPartList );
398 }
399 if ( removeSettingAtKey )
400 settings.remove( oldCompleteKey );
401 return true;
402 }
403
404 return false;
405}
406
407void QgsSettingsEntryBase::copyValueToKey( const QString &key, const QStringList &dynamicKeyPartList ) const
408{
409 const QString completeKey = completeKeyPrivate( key, dynamicKeyPartList );
410 sUserSettings().setValue( completeKey, valueAsVariant( dynamicKeyPartList ) );
411}
412
413void QgsSettingsEntryBase::copyValueToKeyIfChanged( const QString &key, const QStringList &dynamicKeyPartList ) const
414{
415 if ( hasChanged() )
416 {
417 copyValueToKey( key, dynamicKeyPartList );
418 }
419}
420
421QString QgsSettingsEntryBase::formerValuekey( const QStringList &dynamicKeyPartList ) const
422{
423 return key( dynamicKeyPartList ) + u"_formervalue"_s;
424}
@ SaveFormerValue
Save the former value of the settings.
Definition qgis.h:786
QFlags< SettingsOption > SettingsOptions
Definition qgis.h:790
SettingsOrigin
The setting origin describes where a setting is stored.
Definition qgis.h:4977
@ Global
Global settings are stored in qgis_global_settings.ini.
Definition qgis.h:4979
@ Local
Local settings are stored in the user profile.
Definition qgis.h:4980
@ Any
From any origin.
Definition qgis.h:4978
static bool setGlobalSettingsPath(const QString &path)
Sets the path to the global settings INI file and loads all keys into an in-memory hash for use as de...
virtual QString typeId() const
Returns the id of the type of settings This can be re-implemented in a custom implementation of a set...
bool copyValueFromKey(const QString &key, bool removeSettingAtKey=false) const
Copies the value from a given key if it exists.
static QStringList globalChildGroups(const QString &prefix)
Returns the list of child group names found under prefix in the global defaults hash.
bool exists(const QString &dynamicKeyPart=QString()) const
Returns true if the settings is contained in the underlying QSettings.
Qgis::SettingsOptions options() const
Returns the settings options.
bool hasChanged() const
Returns true if the setting was changed during the current QGIS session.
QVariant defaultValueAsVariant() const
Returns settings default value.
bool keyIsValid(const QString &key) const
Returns true if the provided key match the settings entry.
QString description() const
Returns the settings entry description.
static QSettings & userSettings()
Returns a reference to the thread-local QSettings instance used internally by all QgsSettingsEntry op...
QgsSettingsTreeNode * parent() const
Returns the parent tree element.
void copyValueToKeyIfChanged(const QString &key, const QStringList &dynamicKeyPartList=QStringList()) const
Copies the settings to the given key, if it has changed during the current QGIS session (see hasChang...
Q_DECL_DEPRECATED int section() const
Returns settings section.
Qgis::SettingsOrigin origin(const QStringList &dynamicKeyPartList) const
Returns the origin of the setting if it exists.
bool hasDynamicKey() const
Returns true if a part of the settings key is built dynamically.
void remove(const QString &dynamicKeyPart=QString()) const
Removes the settings from the underlying QSettings.
static bool hasGlobalDefault(const QString &key)
Returns true if the global settings INI file contains a value for the given key.
bool setVariantValue(const QVariant &value, const QString &dynamicKeyPart=QString()) const
Set settings value.
static void setupUserSettings(const QString &profilePath)
Configures QSettings to use IniFormat at the given profilePath so that each thread's QSettings instan...
QString definitionKey() const
Returns settings entry defining key.
QVariant formerValueAsVariant(const QString &dynamicKeyPart) const
Returns the former value of the settings if it has been enabled in the options.
QVariant valueAsVariant(const QString &dynamicKeyPart=QString()) const
Returns settings value with.
void copyValueToKey(const QString &key, const QStringList &dynamicKeyPartList=QStringList()) const
Copies the settings to the given key.
QString key(const QString &dynamicKeyPart=QString()) const
Returns settings entry key.
static QVariant globalDefault(const QString &key)
Returns the global default value for the given key, or an invalid QVariant if not found.
static QStringList dynamicKeyPartToList(const QString &dynamicKeyPart)
Transforms a dynamic key part string to list.
QVariant valueAsVariantWithDefaultOverride(const QVariant &defaultValueOverride, const QString &dynamicKeyPart=QString()) const
Returns settings value with a defaultValueOverride.
virtual Qgis::SettingsType settingsType() const
Returns the settings entry type.
QgsSettingsEntryBase(const QString &key, const QString &section, const QVariant &defaultValue=QVariant(), const QString &description=QString(), Qgis::SettingsOptions options=Qgis::SettingsOptions())
Constructor for QgsSettingsEntryBase.
A tree node for the settings tree to help organizing and introspecting the tree.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:8431
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8430
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
#define QgsDebugError(str)
Definition qgslogger.h:71
#define QGIS_CHECK_MAIN_THREAD_ACCESS