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
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"
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
{
59 settings
.beginGroup("Registry");
60 registry
= settings
.childKeys();
68 settings
.beginGroup("Registry");
70 foreach(QString entry
, registry
) {
71 settings
.setValue(entry
, 1);
76 bool contains(QString
&entry
) const
78 return registry
.contains(key(entry
));
81 void add(QString
&entry
)
83 registry
.append(key(entry
));
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.";
112 template<typename Func
>
113 void applyToFactorySettings(Func func
)
115 foreach(QSettings
const *settings
, factorySettingsList
) {
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
®istry
, 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...
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
);
156 const_cast<QSettings
&>(from
).endGroup();
162 void mergeFactorySettings(Registry
®istry
, 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!";
203 // OS specific default
205 files
<< directory
.absoluteFilePath("default_macos.xml");
206 #elif defined(Q_OS_LINUX)
207 files
<< directory
.absoluteFilePath("default_linux.xml");
209 files
<< directory
.absoluteFilePath("default_windows.xml");
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
)
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
257 // merge all loaded factory defaults
258 applyToFactorySettings(
259 [&](const QSettings
&fromSettings
) { mergeFactorySettings(registry
, fromSettings
, toSettings
); }