Merged in f5soh/librepilot/LP-575_fedora_package (pull request #491)
[librepilot.git] / ground / gcs / src / libs / utils / settingsutils.cpp
blob0ecb71f386006c41239dd1577f1d3a6a3359d688
1 /**
2 ******************************************************************************
4 * @file settingsutils.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017.
6 * @brief Settings utilities
8 * @see The GNU Public License (GPL) Version 3
10 *****************************************************************************/
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "settingsutils.h"
29 #include "pathutils.h"
30 #include "xmlconfig.h"
32 #include <QFileInfo>
33 #include <QDir>
34 #include <QDebug>
35 #include <QSettings>
37 namespace Utils {
38 static const QString DEFAULT_CONFIG_DIRNAME = "configurations";
40 static const QString DEFAULT_CONFIG_FILENAME = "default.xml";
42 const QLatin1String CONFIG_OPTION("-D");
44 // list of read only QSettings objects containing factory defaults
45 QList<QSettings const *> factorySettingsList;
48 * Helper class to manage a simple registry
49 * Each entry in the registry is a configuration path
50 * For example : UAVGadgetConfigurations/DialGadget/Attitude
51 * Note that entries are base64 encoded so QSettings does not mess with them
53 class QTCREATOR_UTILS_EXPORT Registry {
54 public:
55 Registry()
57 QSettings settings;
59 settings.beginGroup("Registry");
60 registry = settings.childKeys();
61 settings.endGroup();
64 void save() const
66 QSettings settings;
68 settings.beginGroup("Registry");
69 settings.remove("");
70 foreach(QString entry, registry) {
71 settings.setValue(entry, 1);
73 settings.endGroup();
76 bool contains(QString &entry) const
78 return registry.contains(key(entry));
81 void add(QString &entry)
83 registry.append(key(entry));
86 private:
87 QStringList registry;
89 QString key(QString &entry) const
91 return entry.toUtf8().toBase64();
95 QString checkFile(QString fileName)
97 if (!fileName.isEmpty()) {
98 QFileInfo fi(fileName);
99 if (fi.isRelative()) {
100 // file name has a relative path
101 QDir directory(Utils::GetDataPath() + QString("configurations"));
102 fileName = directory.absoluteFilePath(fileName);
104 if (!QFile::exists(fileName)) {
105 qWarning() << "Configuration file" << fileName << "does not exist.";
106 fileName = "";
109 return fileName;
112 template<typename Func>
113 void applyToFactorySettings(Func func)
115 foreach(QSettings const *settings, factorySettingsList) {
116 func(*settings);
120 void copySettings(const QSettings &from, QSettings &to)
122 foreach(QString key, from.allKeys()) {
123 if (!to.contains(key)) {
124 // qDebug() << "++" << key << from.value(key);
125 to.setValue(key, from.value(key));
126 } else if (from.value(key) != to.value(key)) {
127 // qDebug() << ">>" << key << from.value(key) << to.value(key);
128 to.setValue(key, from.value(key));
133 void mergeSettings(Registry &registry, const QSettings &from, QSettings &to)
135 to.beginGroup(from.group());
137 // iterate over factory defaults groups
138 // note that merging could be done in smarter way
139 // currently we do a "all or nothing" merge but we could do something more granular
140 // this, would allow, new configuration options to be added to existing configurations
141 foreach(QString group, from.childGroups()) {
142 const_cast<QSettings &>(from).beginGroup(group);
143 to.beginGroup(group);
144 QString id = from.group();
145 if (!registry.contains(id)) {
146 // registry keeps growing...
147 registry.add(id);
148 // copy settings only if destination group is totally empty (see comment above)
149 if (to.allKeys().count() <= 0 && to.childGroups().count() <= 0) {
150 // found new group, copy it to the destination
151 qDebug() << "settings - adding new configuration" << id;
152 copySettings(from, to);
155 to.endGroup();
156 const_cast<QSettings &>(from).endGroup();
159 to.endGroup();
162 void mergeFactorySettings(Registry &registry, const QSettings &from, QSettings &to)
164 const_cast<QSettings &>(from).beginGroup("Plugins");
165 mergeSettings(registry, from, to);
166 const_cast<QSettings &>(from).endGroup();
168 const_cast<QSettings &>(from).beginGroup("UAVGadgetConfigurations");
169 foreach(QString childGroup, from.childGroups()) {
170 const_cast<QSettings &>(from).beginGroup(childGroup);
171 mergeSettings(registry, from, to);
172 const_cast<QSettings &>(from).endGroup();
174 const_cast<QSettings &>(from).endGroup();
177 void initSettings(const QString &factoryDefaultsFileName)
179 QSettings::setDefaultFormat(XmlConfig::XmlFormat);
181 QDir directory(Utils::GetDataPath() + DEFAULT_CONFIG_DIRNAME);
183 // check if command line option -config-file contains a file name
184 QString fileName = checkFile(factoryDefaultsFileName);
186 if (fileName.isEmpty()) {
187 // check default file
188 qDebug() << "settings - looking for factory defaults configuration files in" << directory.absolutePath();
189 fileName = checkFile(directory.absoluteFilePath(DEFAULT_CONFIG_FILENAME));
192 if (fileName.isEmpty()) {
193 // TODO should we exit violently?
194 qCritical() << "No default configuration file found!";
195 return;
198 QStringList files;
200 // common default
201 files << fileName;
203 // OS specific default
204 #ifdef Q_OS_MAC
205 files << directory.absoluteFilePath("default_macos.xml");
206 #elif defined(Q_OS_LINUX)
207 files << directory.absoluteFilePath("default_linux.xml");
208 #else
209 files << directory.absoluteFilePath("default_windows.xml");
210 #endif
212 foreach(QString file, files) {
213 file = checkFile(file);
214 if (!file.isEmpty()) {
215 QSettings const *settings = new QSettings(file, XmlConfig::XmlFormat);
216 qDebug() << "settings - loaded factory defaults" << file;
217 factorySettingsList.append(settings);
222 void overrideSettings(QSettings &settings, int argc, char * *argv)
224 // Options like -D My/setting=test
225 QRegExp rx("([^=]+)=(.*)");
227 for (int i = 0; i < argc; ++i) {
228 if (CONFIG_OPTION == QString(argv[i])) {
229 if (rx.indexIn(argv[++i]) > -1) {
230 QString key = rx.cap(1);
231 QString value = rx.cap(2);
232 qDebug() << "settings - user setting" << key << "set to value" << value;
233 settings.setValue(key, value);
239 void resetToFactoryDefaults(QSettings &toSettings)
241 toSettings.clear();
242 applyToFactorySettings(
243 [&](const QSettings &fromSettings) { copySettings(fromSettings, toSettings); }
248 * Merge factory defaults into user settings.
249 * Currently only Plugins and UAVGadgetConfigurations are merged
251 void mergeFactoryDefaults(QSettings &toSettings)
253 // registry of all configuration groups ever seen
254 // used to avoid re-adding a group that was deleted by the user
255 Registry registry;
257 // merge all loaded factory defaults
258 applyToFactorySettings(
259 [&](const QSettings &fromSettings) { mergeFactorySettings(registry, fromSettings, toSettings); }
262 registry.save();