1 /***************************************************************************
2 * Copyright (C) 2005-2019 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 ***************************************************************************/
23 #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))
44 , _appName(std::move(appName))
47 void Settings::setGroup(QString group)
49 _group = std::move(group);
52 QString Settings::keyForNotify(const QString& key) const
57 uint Settings::version() const
59 // we don't cache this value, and we ignore the group
61 uint ver = s.value("Config/Version", 0).toUInt();
63 // No version, so create one
64 s.setValue("Config/Version", VERSION);
70 uint Settings::versionMinor() const
72 // Don't cache this value; ignore the group
74 // '0' means new configuration, anything else indicates an existing configuration. Application
75 // initialization should check this value and manage upgrades/downgrades, e.g. in Core::Core()
76 // and QtUiApplication::init().
77 uint verMinor = s.value("Config/VersionMinor", 0).toUInt();
79 // As previous Quassel versions didn't implement this, we need to check if any settings other
80 // than Config/Version exist. If so, assume it's version 1.
81 if (verMinor == 0 && s.allKeys().count() > 1) {
82 // More than 1 key exists, but version's never been set. Assume and set version 1.
83 // const_cast is ok, because setVersionMinor() doesn't actually change this instance
84 const_cast<Settings*>(this)->setVersionMinor(VERSION_MINOR_INITIAL);
85 return VERSION_MINOR_INITIAL;
92 void Settings::setVersionMinor(const uint versionMinor)
94 // Don't cache this value; ignore the group
96 // Set the value directly.
97 s.setValue("Config/VersionMinor", versionMinor);
100 QSettings::Format Settings::format() const
103 return QSettings::IniFormat;
105 return QSettings::NativeFormat;
109 bool Settings::sync()
113 switch (s.status()) {
114 case QSettings::NoError:
121 bool Settings::isWritable() const
124 return s.isWritable();
127 QStringList Settings::allLocalKeys() const
130 s.beginGroup(_group);
131 QStringList res = s.allKeys();
136 QStringList Settings::localChildKeys(const QString& rootkey) const
139 if (rootkey.isEmpty())
142 g = QString("%1/%2").arg(_group, rootkey);
146 QStringList res = s.childKeys();
151 QStringList Settings::localChildGroups(const QString& rootkey) const
154 if (rootkey.isEmpty())
157 g = QString("%1/%2").arg(_group, rootkey);
161 QStringList res = s.childGroups();
166 void Settings::setLocalValue(const QString& key, const QVariant& data)
168 QString normKey = normalizedKey(_group, key);
170 s.setValue(normKey, data);
171 setCacheKeyPersisted(normKey, true);
172 setCacheValue(normKey, data);
173 if (hasNotifier(normKey)) {
174 emit notifier(normKey)->valueChanged(data);
178 QVariant Settings::localValue(const QString& key, const QVariant& def) const
180 QString normKey = normalizedKey(_group, key);
181 if (!isCached(normKey)) {
183 // Since we're loading from settings anyways, cache whether or not the key exists on disk
184 setCacheKeyPersisted(normKey, s.contains(normKey));
186 setCacheValue(normKey, s.value(normKey, def));
188 if (cacheKeyPersisted(normKey)) {
189 return cacheValue(normKey);
191 // Don't return possibly wrong cached values
192 // A key gets cached with the first default value requested and never changes afterwards
196 bool Settings::localKeyExists(const QString& key) const
198 QString normKey = normalizedKey(_group, key);
199 if (!isKeyPersistedCached(normKey)) {
201 // Cache whether or not key exists on disk
202 // We can't cache key value as we don't know the default
203 setCacheKeyPersisted(normKey, s.contains(normKey));
206 return cacheKeyPersisted(normKey);
209 void Settings::removeLocalKey(const QString& key)
212 s.beginGroup(_group);
215 QString normKey = normalizedKey(_group, key);
216 if (isCached(normKey)) {
217 _settingsCache.remove(normKey);
219 if (isKeyPersistedCached(normKey)) {
220 _settingsKeyPersistedCache.remove(normKey);
222 if (hasNotifier(normKey)) {
223 emit notifier(normKey)->valueChanged({});
227 QString Settings::fileName() const
229 return Quassel::configDirPath() + _appName + ((format() == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini"));
232 QString Settings::normalizedKey(const QString& group, const QString& key) const
236 return group + '/' + key;
239 void Settings::setCacheKeyPersisted(const QString& normKey, bool exists) const
241 _settingsKeyPersistedCache[normKey] = exists;
244 bool Settings::cacheKeyPersisted(const QString& normKey) const
246 return _settingsKeyPersistedCache[normKey];
249 bool Settings::isKeyPersistedCached(const QString& normKey) const
251 return _settingsKeyPersistedCache.contains(normKey);
254 void Settings::setCacheValue(const QString& normKey, const QVariant& data) const
256 _settingsCache[normKey] = data;
259 QVariant Settings::cacheValue(const QString& normKey) const
261 return _settingsCache[normKey];
264 bool Settings::isCached(const QString& normKey) const
266 return _settingsCache.contains(normKey);
269 SettingsChangeNotifier* Settings::notifier(const QString& normKey) const
271 if (!hasNotifier(normKey))
272 _settingsChangeNotifier[normKey] = std::make_shared<SettingsChangeNotifier>();
273 return _settingsChangeNotifier[normKey].get();
276 bool Settings::hasNotifier(const QString& normKey) const
278 return _settingsChangeNotifier.contains(normKey);