client: Fix settings upgrade missing some steps
[quassel.git] / src / common / settings.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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 <QStringList>
22
23 #include "settings.h"
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, SettingsChangeNotifier *> Settings::settingsChangeNotifier;
34
35 #ifdef Q_OS_MAC
36 #  define create_qsettings QSettings s(QCoreApplication::organizationDomain(), appName)
37 #else
38 #  define create_qsettings QSettings s(fileName(), format())
39 #endif
40
41 // Settings::Settings(QString group_, QString appName_)
42 //   : group(group_),
43 //     appName(appName_)
44 // {
45
46 // /* we need to call the constructor immediately in order to set the path...
47 // #ifndef Q_WS_QWS
48 //   QSettings(QCoreApplication::organizationName(), applicationName);
49 // #else
50 //   // FIXME sandboxDir() is not currently working correctly...
51 //   //if(Qtopia::sandboxDir().isEmpty()) QSettings();
52 //   //else QSettings(Qtopia::sandboxDir() + "/etc/QuasselIRC.conf", QSettings::NativeFormat);
53 //   // ...so we have to use a workaround:
54 //   QString appPath = QCoreApplication::applicationFilePath();
55 //   if(appPath.startsWith(Qtopia::packagePath())) {
56 //     QString sandboxPath = appPath.left(Qtopia::packagePath().length() + 32);
57 //     QSettings(sandboxPath + "/etc/QuasselIRC.conf", QSettings::IniFormat);
58 //     qDebug() << sandboxPath + "/etc/QuasselIRC.conf";
59 //   } else {
60 //     QSettings(QCoreApplication::organizationName(), applicationName);
61 //   }
62 // #endif
63 // */
64 // }
65
66 void Settings::notify(const QString &key, QObject *receiver, const char *slot)
67 {
68     QObject::connect(notifier(normalizedKey(group, key)), SIGNAL(valueChanged(const QVariant &)),
69         receiver, slot);
70 }
71
72
73 void Settings::initAndNotify(const QString &key, QObject *receiver, const char *slot, const QVariant &defaultValue)
74 {
75     notify(key, receiver, slot);
76     emit notifier(normalizedKey(group, key))->valueChanged(localValue(key, defaultValue));
77 }
78
79
80 uint Settings::version()
81 {
82     // we don't cache this value, and we ignore the group
83     create_qsettings;
84     uint ver = s.value("Config/Version", 0).toUInt();
85     if (!ver) {
86         // No version, so create one
87         s.setValue("Config/Version", VERSION);
88         return VERSION;
89     }
90     return ver;
91 }
92
93
94 uint Settings::versionMinor()
95 {
96     // Don't cache this value; ignore the group
97     create_qsettings;
98     // '0' means new configuration, anything else indicates an existing configuration.  Application
99     // initialization should check this value and manage upgrades/downgrades, e.g. in Core::Core()
100     // and QtUiApplication::init().
101     uint verMinor = s.value("Config/VersionMinor", 0).toUInt();
102
103     // As previous Quassel versions didn't implement this, we need to check if any settings other
104     // than Config/Version exist.  If so, assume it's version 1.
105     if (verMinor == 0 && s.allKeys().count() > 1) {
106         // More than 1 key exists, but version's never been set.  Assume and set version 1.
107         setVersionMinor(VERSION_MINOR_INITIAL);
108         return VERSION_MINOR_INITIAL;
109     } else {
110         return verMinor;
111     }
112 }
113
114
115 void Settings::setVersionMinor(const uint versionMinor)
116 {
117     // Don't cache this value; ignore the group
118     create_qsettings;
119     // Set the value directly.
120     s.setValue("Config/VersionMinor", versionMinor);
121 }
122
123
124 bool Settings::sync() {
125     create_qsettings;
126     s.sync();
127     switch (s.status()) {
128         case QSettings::NoError:
129             return true;
130         default:
131             return false;
132     }
133 }
134
135
136 bool Settings::isWritable() {
137     create_qsettings;
138     return s.isWritable();
139 }
140
141
142 QStringList Settings::allLocalKeys()
143 {
144     create_qsettings;
145     s.beginGroup(group);
146     QStringList res = s.allKeys();
147     s.endGroup();
148     return res;
149 }
150
151
152 QStringList Settings::localChildKeys(const QString &rootkey)
153 {
154     QString g;
155     if (rootkey.isEmpty())
156         g = group;
157     else
158         g = QString("%1/%2").arg(group, rootkey);
159
160     create_qsettings;
161     s.beginGroup(g);
162     QStringList res = s.childKeys();
163     s.endGroup();
164     return res;
165 }
166
167
168 QStringList Settings::localChildGroups(const QString &rootkey)
169 {
170     QString g;
171     if (rootkey.isEmpty())
172         g = group;
173     else
174         g = QString("%1/%2").arg(group, rootkey);
175
176     create_qsettings;
177     s.beginGroup(g);
178     QStringList res = s.childGroups();
179     s.endGroup();
180     return res;
181 }
182
183
184 void Settings::setLocalValue(const QString &key, const QVariant &data)
185 {
186     QString normKey = normalizedKey(group, key);
187     create_qsettings;
188     s.setValue(normKey, data);
189     setCacheValue(normKey, data);
190     if (hasNotifier(normKey)) {
191         emit notifier(normKey)->valueChanged(data);
192     }
193 }
194
195
196 const QVariant &Settings::localValue(const QString &key, const QVariant &def)
197 {
198     QString normKey = normalizedKey(group, key);
199     if (!isCached(normKey)) {
200         create_qsettings;
201         setCacheValue(normKey, s.value(normKey, def));
202     }
203     return cacheValue(normKey);
204 }
205
206
207 bool Settings::localKeyExists(const QString &key)
208 {
209     QString normKey = normalizedKey(group, key);
210     // Do NOT check the cache as default values get cached, too.  Otherwise loading a setting once
211     // will mark it as existing in settings, even when it only exists in cache (and not on disk).
212     create_qsettings;
213     return s.contains(normKey);
214 }
215
216
217 void Settings::removeLocalKey(const QString &key)
218 {
219     create_qsettings;
220     s.beginGroup(group);
221     s.remove(key);
222     s.endGroup();
223     QString normKey = normalizedKey(group, key);
224     if (isCached(normKey)) {
225         settingsCache.remove(normKey);
226     }
227     if (hasNotifier(normKey)) {
228         emit notifier(normKey)->valueChanged({});
229     }
230 }