qa: Avoid deprecation warnings for QList/QSet conversions
[quassel.git] / src / common / settings.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2019 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include "settings.h"
22
23 #include <QStringList>
24
25 const int VERSION = 1;  /// Settings version for backwords/forwards incompatible changes
26
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
31
32 QHash<QString, QVariant> Settings::_settingsCache;
33 QHash<QString, bool> Settings::_settingsKeyPersistedCache;
34 QHash<QString, std::shared_ptr<SettingsChangeNotifier>> Settings::_settingsChangeNotifier;
35
36 #ifdef Q_OS_MAC
37 #    define create_qsettings QSettings s(QCoreApplication::organizationDomain(), _appName)
38 #else
39 #    define create_qsettings QSettings s(fileName(), format())
40 #endif
41
42 Settings::Settings(QString group, QString appName)
43     : _group(std::move(group))
44     , _appName(std::move(appName))
45 {}
46
47 void Settings::setGroup(QString group)
48 {
49     _group = std::move(group);
50 }
51
52 QString Settings::keyForNotify(const QString& key) const
53 {
54     return key;
55 }
56
57 uint Settings::version() const
58 {
59     // we don't cache this value, and we ignore the group
60     create_qsettings;
61     uint ver = s.value("Config/Version", 0).toUInt();
62     if (!ver) {
63         // No version, so create one
64         s.setValue("Config/Version", VERSION);
65         return VERSION;
66     }
67     return ver;
68 }
69
70 uint Settings::versionMinor() const
71 {
72     // Don't cache this value; ignore the group
73     create_qsettings;
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();
78
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;
86     }
87     else {
88         return verMinor;
89     }
90 }
91
92 void Settings::setVersionMinor(const uint versionMinor)
93 {
94     // Don't cache this value; ignore the group
95     create_qsettings;
96     // Set the value directly.
97     s.setValue("Config/VersionMinor", versionMinor);
98 }
99
100 QSettings::Format Settings::format() const
101 {
102 #ifdef Q_OS_WIN
103     return QSettings::IniFormat;
104 #else
105     return QSettings::NativeFormat;
106 #endif
107 }
108
109 bool Settings::sync()
110 {
111     create_qsettings;
112     s.sync();
113     switch (s.status()) {
114     case QSettings::NoError:
115         return true;
116     default:
117         return false;
118     }
119 }
120
121 bool Settings::isWritable() const
122 {
123     create_qsettings;
124     return s.isWritable();
125 }
126
127 QStringList Settings::allLocalKeys() const
128 {
129     create_qsettings;
130     s.beginGroup(_group);
131     QStringList res = s.allKeys();
132     s.endGroup();
133     return res;
134 }
135
136 QStringList Settings::localChildKeys(const QString& rootkey) const
137 {
138     QString g;
139     if (rootkey.isEmpty())
140         g = _group;
141     else
142         g = QString("%1/%2").arg(_group, rootkey);
143
144     create_qsettings;
145     s.beginGroup(g);
146     QStringList res = s.childKeys();
147     s.endGroup();
148     return res;
149 }
150
151 QStringList Settings::localChildGroups(const QString& rootkey) const
152 {
153     QString g;
154     if (rootkey.isEmpty())
155         g = _group;
156     else
157         g = QString("%1/%2").arg(_group, rootkey);
158
159     create_qsettings;
160     s.beginGroup(g);
161     QStringList res = s.childGroups();
162     s.endGroup();
163     return res;
164 }
165
166 void Settings::setLocalValue(const QString& key, const QVariant& data)
167 {
168     QString normKey = normalizedKey(_group, key);
169     create_qsettings;
170     s.setValue(normKey, data);
171     setCacheKeyPersisted(normKey, true);
172     setCacheValue(normKey, data);
173     if (hasNotifier(normKey)) {
174         emit notifier(normKey)->valueChanged(data);
175     }
176 }
177
178 QVariant Settings::localValue(const QString& key, const QVariant& def) const
179 {
180     QString normKey = normalizedKey(_group, key);
181     if (!isCached(normKey)) {
182         create_qsettings;
183         // Since we're loading from settings anyways, cache whether or not the key exists on disk
184         setCacheKeyPersisted(normKey, s.contains(normKey));
185         // Cache key value
186         setCacheValue(normKey, s.value(normKey, def));
187     }
188     if (cacheKeyPersisted(normKey)) {
189         return cacheValue(normKey);
190     }
191     // Don't return possibly wrong cached values
192     // A key gets cached with the first default value requested and never changes afterwards
193     return def;
194 }
195
196 bool Settings::localKeyExists(const QString& key) const
197 {
198     QString normKey = normalizedKey(_group, key);
199     if (!isKeyPersistedCached(normKey)) {
200         create_qsettings;
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));
204     }
205
206     return cacheKeyPersisted(normKey);
207 }
208
209 void Settings::removeLocalKey(const QString& key)
210 {
211     create_qsettings;
212     s.beginGroup(_group);
213     s.remove(key);
214     s.endGroup();
215     QString normKey = normalizedKey(_group, key);
216     if (isCached(normKey)) {
217         _settingsCache.remove(normKey);
218     }
219     if (isKeyPersistedCached(normKey)) {
220         _settingsKeyPersistedCache.remove(normKey);
221     }
222     if (hasNotifier(normKey)) {
223         emit notifier(normKey)->valueChanged({});
224     }
225 }
226
227 QString Settings::fileName() const
228 {
229     return Quassel::configDirPath() + _appName + ((format() == QSettings::NativeFormat) ? QLatin1String(".conf") : QLatin1String(".ini"));
230 }
231
232 QString Settings::normalizedKey(const QString& group, const QString& key) const
233 {
234     if (group.isEmpty())
235         return key;
236     return group + '/' + key;
237 }
238
239 void Settings::setCacheKeyPersisted(const QString& normKey, bool exists) const
240 {
241     _settingsKeyPersistedCache[normKey] = exists;
242 }
243
244 bool Settings::cacheKeyPersisted(const QString& normKey) const
245 {
246     return _settingsKeyPersistedCache[normKey];
247 }
248
249 bool Settings::isKeyPersistedCached(const QString& normKey) const
250 {
251     return _settingsKeyPersistedCache.contains(normKey);
252 }
253
254 void Settings::setCacheValue(const QString& normKey, const QVariant& data) const
255 {
256     _settingsCache[normKey] = data;
257 }
258
259 QVariant Settings::cacheValue(const QString& normKey) const
260 {
261     return _settingsCache[normKey];
262 }
263
264 bool Settings::isCached(const QString& normKey) const
265 {
266     return _settingsCache.contains(normKey);
267 }
268
269 SettingsChangeNotifier* Settings::notifier(const QString& normKey) const
270 {
271     if (!hasNotifier(normKey))
272         _settingsChangeNotifier[normKey] = std::make_shared<SettingsChangeNotifier>();
273     return _settingsChangeNotifier[normKey].get();
274 }
275
276 bool Settings::hasNotifier(const QString& normKey) const
277 {
278     return _settingsChangeNotifier.contains(normKey);
279 }