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