1 /***************************************************************************
2 * Copyright (C) 2005-2018 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <QStringList>
25 const int VERSION = 1; /// Settings version for backwords/forwards incompatible changes
27 // This is used if no VersionMinor key exists, e.g. upgrading from a Quassel version before this
28 // change. This shouldn't be increased from 1; instead, change the logic in Core::Core() and
29 // QtUiApplication::init() to handle upgrading and downgrading.
30 const int VERSION_MINOR_INITIAL = 1; /// Initial settings version for compatible changes
32 QHash<QString, QVariant> Settings::_settingsCache;
33 QHash<QString, bool> Settings::_settingsKeyPersistedCache;
34 QHash<QString, std::shared_ptr<SettingsChangeNotifier>> Settings::_settingsChangeNotifier;
37 # define create_qsettings QSettings s(QCoreApplication::organizationDomain(), _appName)
39 # define create_qsettings QSettings s(fileName(), format())
42 Settings::Settings(QString group, QString appName)
43 : _group(std::move(group)), _appName(std::move(appName))
47 void Settings::setGroup(QString group) {
48 _group = std::move(group);
52 void Settings::notify(const QString &key, QObject *receiver, const char *slot) const
54 QObject::connect(notifier(normalizedKey(_group, key)), SIGNAL(valueChanged(const QVariant &)),
59 void Settings::initAndNotify(const QString &key, QObject *receiver, const char *slot, const QVariant &defaultValue) const
61 notify(key, receiver, slot);
62 emit notifier(normalizedKey(_group, key))->valueChanged(localValue(key, defaultValue));
66 uint Settings::version() const
68 // we don't cache this value, and we ignore the group
70 uint ver = s.value("Config/Version", 0).toUInt();
72 // No version, so create one
73 s.setValue("Config/Version", VERSION);
80 uint Settings::versionMinor() const
82 // Don't cache this value; ignore the group
84 // '0' means new configuration, anything else indicates an existing configuration. Application
85 // initialization should check this value and manage upgrades/downgrades, e.g. in Core::Core()
86 // and QtUiApplication::init().
87 uint verMinor = s.value("Config/VersionMinor", 0).toUInt();
89 // As previous Quassel versions didn't implement this, we need to check if any settings other
90 // than Config/Version exist. If so, assume it's version 1.
91 if (verMinor == 0 && s.allKeys().count() > 1) {
92 // More than 1 key exists, but version's never been set. Assume and set version 1.
93 // const_cast is ok, because setVersionMinor() doesn't actually change this instance
94 const_cast<Settings*>(this)->setVersionMinor(VERSION_MINOR_INITIAL);
95 return VERSION_MINOR_INITIAL;
102 void Settings::setVersionMinor(const uint versionMinor)
104 // Don't cache this value; ignore the group
106 // Set the value directly.
107 s.setValue("Config/VersionMinor", versionMinor);
111 QSettings::Format Settings::format() const
114 return QSettings::IniFormat;
116 return QSettings::NativeFormat;
121 bool Settings::sync()
125 switch (s.status()) {
126 case QSettings::NoError:
134 bool Settings::isWritable() const
137 return s.isWritable();
141 QStringList Settings::allLocalKeys() const
144 s.beginGroup(_group);
145 QStringList res = s.allKeys();
151 QStringList Settings::localChildKeys(const QString &rootkey) const
154 if (rootkey.isEmpty())
157 g = QString("%1/%2").arg(_group, rootkey);
161 QStringList res = s.childKeys();
167 QStringList Settings::localChildGroups(const QString &rootkey) const
170 if (rootkey.isEmpty())
173 g = QString("%1/%2").arg(_group, rootkey);
177 QStringList res = s.childGroups();
183 void Settings::setLocalValue(const QString &key, const QVariant &data)
185 QString normKey = normalizedKey(_group, key);
187 s.setValue(normKey, data);
188 setCacheKeyPersisted(normKey, true);
189 setCacheValue(normKey, data);
190 if (hasNotifier(normKey)) {
191 emit notifier(normKey)->valueChanged(data);
196 QVariant Settings::localValue(const QString &key, const QVariant &def) const
198 QString normKey = normalizedKey(_group, key);
199 if (!isCached(normKey)) {
201 // Since we're loading from settings anyways, cache whether or not the key exists on disk
202 setCacheKeyPersisted(normKey, s.contains(normKey));
204 setCacheValue(normKey, s.value(normKey, def));
206 if (cacheKeyPersisted(normKey)) {
207 return cacheValue(normKey);
209 // Don't return possibly wrong cached values
210 // A key gets cached with the first default value requested and never changes afterwards
215 bool Settings::localKeyExists(const QString &key) const
217 QString normKey = normalizedKey(_group, key);
218 if (!isKeyPersistedCached(normKey)) {
220 // Cache whether or not key exists on disk
221 // We can't cache key value as we don't know the default
222 setCacheKeyPersisted(normKey, s.contains(normKey));
225 return cacheKeyPersisted(normKey);
229 void Settings::removeLocalKey(const QString &key)
232 s.beginGroup(_group);
235 QString normKey = normalizedKey(_group, key);
236 if (isCached(normKey)) {
237 _settingsCache.remove(normKey);
239 if (isKeyPersistedCached(normKey)) {
240 _settingsKeyPersistedCache.remove(normKey);
242 if (hasNotifier(normKey)) {
243 emit notifier(normKey)->valueChanged({});
248 QString Settings::fileName() const
250 return Quassel::configDirPath() + _appName
251 + ((format() == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini"));
255 QString Settings::normalizedKey(const QString &group, const QString &key) const
259 return group + '/' + key;
263 void Settings::setCacheKeyPersisted(const QString &normKey, bool exists) const
265 _settingsKeyPersistedCache[normKey] = exists;
269 bool Settings::cacheKeyPersisted(const QString &normKey) const
271 return _settingsKeyPersistedCache[normKey];
275 bool Settings::isKeyPersistedCached(const QString &normKey) const
277 return _settingsKeyPersistedCache.contains(normKey);
281 void Settings::setCacheValue(const QString &normKey, const QVariant &data) const
283 _settingsCache[normKey] = data;
287 QVariant Settings::cacheValue(const QString &normKey) const
289 return _settingsCache[normKey];
293 bool Settings::isCached(const QString &normKey) const
295 return _settingsCache.contains(normKey);
299 SettingsChangeNotifier *Settings::notifier(const QString &normKey) const
301 if (!hasNotifier(normKey))
302 _settingsChangeNotifier[normKey] = std::make_shared<SettingsChangeNotifier>();
303 return _settingsChangeNotifier[normKey].get();
307 bool Settings::hasNotifier(const QString &normKey) const
309 return _settingsChangeNotifier.contains(normKey);