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 QString Settings::keyForNotify(const QString &key) const
58 uint Settings::version() const
60 // we don't cache this value, and we ignore the group
62 uint ver = s.value("Config/Version", 0).toUInt();
64 // No version, so create one
65 s.setValue("Config/Version", VERSION);
72 uint Settings::versionMinor() const
74 // Don't cache this value; ignore the group
76 // '0' means new configuration, anything else indicates an existing configuration. Application
77 // initialization should check this value and manage upgrades/downgrades, e.g. in Core::Core()
78 // and QtUiApplication::init().
79 uint verMinor = s.value("Config/VersionMinor", 0).toUInt();
81 // As previous Quassel versions didn't implement this, we need to check if any settings other
82 // than Config/Version exist. If so, assume it's version 1.
83 if (verMinor == 0 && s.allKeys().count() > 1) {
84 // More than 1 key exists, but version's never been set. Assume and set version 1.
85 // const_cast is ok, because setVersionMinor() doesn't actually change this instance
86 const_cast<Settings*>(this)->setVersionMinor(VERSION_MINOR_INITIAL);
87 return VERSION_MINOR_INITIAL;
94 void Settings::setVersionMinor(const uint versionMinor)
96 // Don't cache this value; ignore the group
98 // Set the value directly.
99 s.setValue("Config/VersionMinor", versionMinor);
103 QSettings::Format Settings::format() const
106 return QSettings::IniFormat;
108 return QSettings::NativeFormat;
113 bool Settings::sync()
117 switch (s.status()) {
118 case QSettings::NoError:
126 bool Settings::isWritable() const
129 return s.isWritable();
133 QStringList Settings::allLocalKeys() const
136 s.beginGroup(_group);
137 QStringList res = s.allKeys();
143 QStringList Settings::localChildKeys(const QString &rootkey) const
146 if (rootkey.isEmpty())
149 g = QString("%1/%2").arg(_group, rootkey);
153 QStringList res = s.childKeys();
159 QStringList Settings::localChildGroups(const QString &rootkey) const
162 if (rootkey.isEmpty())
165 g = QString("%1/%2").arg(_group, rootkey);
169 QStringList res = s.childGroups();
175 void Settings::setLocalValue(const QString &key, const QVariant &data)
177 QString normKey = normalizedKey(_group, key);
179 s.setValue(normKey, data);
180 setCacheKeyPersisted(normKey, true);
181 setCacheValue(normKey, data);
182 if (hasNotifier(normKey)) {
183 emit notifier(normKey)->valueChanged(data);
188 QVariant Settings::localValue(const QString &key, const QVariant &def) const
190 QString normKey = normalizedKey(_group, key);
191 if (!isCached(normKey)) {
193 // Since we're loading from settings anyways, cache whether or not the key exists on disk
194 setCacheKeyPersisted(normKey, s.contains(normKey));
196 setCacheValue(normKey, s.value(normKey, def));
198 if (cacheKeyPersisted(normKey)) {
199 return cacheValue(normKey);
201 // Don't return possibly wrong cached values
202 // A key gets cached with the first default value requested and never changes afterwards
207 bool Settings::localKeyExists(const QString &key) const
209 QString normKey = normalizedKey(_group, key);
210 if (!isKeyPersistedCached(normKey)) {
212 // Cache whether or not key exists on disk
213 // We can't cache key value as we don't know the default
214 setCacheKeyPersisted(normKey, s.contains(normKey));
217 return cacheKeyPersisted(normKey);
221 void Settings::removeLocalKey(const QString &key)
224 s.beginGroup(_group);
227 QString normKey = normalizedKey(_group, key);
228 if (isCached(normKey)) {
229 _settingsCache.remove(normKey);
231 if (isKeyPersistedCached(normKey)) {
232 _settingsKeyPersistedCache.remove(normKey);
234 if (hasNotifier(normKey)) {
235 emit notifier(normKey)->valueChanged({});
240 QString Settings::fileName() const
242 return Quassel::configDirPath() + _appName
243 + ((format() == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini"));
247 QString Settings::normalizedKey(const QString &group, const QString &key) const
251 return group + '/' + key;
255 void Settings::setCacheKeyPersisted(const QString &normKey, bool exists) const
257 _settingsKeyPersistedCache[normKey] = exists;
261 bool Settings::cacheKeyPersisted(const QString &normKey) const
263 return _settingsKeyPersistedCache[normKey];
267 bool Settings::isKeyPersistedCached(const QString &normKey) const
269 return _settingsKeyPersistedCache.contains(normKey);
273 void Settings::setCacheValue(const QString &normKey, const QVariant &data) const
275 _settingsCache[normKey] = data;
279 QVariant Settings::cacheValue(const QString &normKey) const
281 return _settingsCache[normKey];
285 bool Settings::isCached(const QString &normKey) const
287 return _settingsCache.contains(normKey);
291 SettingsChangeNotifier *Settings::notifier(const QString &normKey) const
293 if (!hasNotifier(normKey))
294 _settingsChangeNotifier[normKey] = std::make_shared<SettingsChangeNotifier>();
295 return _settingsChangeNotifier[normKey].get();
299 bool Settings::hasNotifier(const QString &normKey) const
301 return _settingsChangeNotifier.contains(normKey);