LP-56 - Better txpid option namings, fix tabs-spaces, tooltips. headers, variable...
[librepilot.git] / ground / openpilotgcs / src / plugins / coreplugin / settingsdatabase.cpp
blob365c27479c0e3b64d1a5ba17ea194fcb0f214123
1 /**
2 ******************************************************************************
4 * @file settingsdatabase.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
7 * @addtogroup GCSPlugins GCS Plugins
8 * @{
9 * @addtogroup CorePlugin Core Plugin
10 * @{
11 * @brief The Core GCS plugin
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "settingsdatabase.h"
31 #include <QtCore/QDir>
32 #include <QtCore/QMap>
33 #include <QtCore/QString>
34 #include <QtCore/QStringList>
35 #include <QtCore/QVariant>
37 #include <QtSql/QSqlDatabase>
38 #include <QtSql/QSqlError>
39 #include <QtSql/QSqlQuery>
40 #include <QDebug>
42 /*!
43 \class Core::SettingsDatabase
44 \brief An alternative to the application-wide QSettings that is more
45 suitable for storing large amounts of data.
47 The settings database is SQLite based, and lazily retrieves data when it
48 is asked for. It also does incremental updates of the database rather than
49 rewriting the whole file each time one of the settings change.
51 The SettingsDatabase API mimics that of QSettings.
54 using namespace Core;
55 using namespace Core::Internal;
57 enum { debug_settings = 0 };
59 namespace Core {
60 namespace Internal {
61 typedef QMap<QString, QVariant> SettingsMap;
63 class SettingsDatabasePrivate {
64 public:
65 QString effectiveGroup() const
67 return m_groups.join(QLatin1String("/"));
70 QString effectiveKey(const QString &key) const
72 QString g = effectiveGroup();
74 if (!g.isEmpty() && !key.isEmpty()) {
75 g += QLatin1Char('/');
77 g += key;
78 return g;
81 SettingsMap m_settings;
83 QStringList m_groups;
84 QStringList m_dirtyKeys;
86 QSqlDatabase m_db;
88 } // namespace Internal
89 } // namespace Core
91 SettingsDatabase::SettingsDatabase(const QString &path,
92 const QString &application,
93 QObject *parent)
94 : QObject(parent)
95 , d(new SettingsDatabasePrivate)
97 const QLatin1Char slash('/');
99 // TODO: Don't rely on a path, but determine automatically
100 QDir pathDir(path);
102 if (!pathDir.exists()) {
103 pathDir.mkpath(pathDir.absolutePath());
106 QString fileName = path;
107 if (!fileName.endsWith(slash)) {
108 fileName += slash;
110 fileName += application;
111 fileName += QLatin1String(".db");
113 d->m_db = QSqlDatabase::addDatabase("QSQLITE", QLatin1String("settings"));
114 d->m_db.setDatabaseName(fileName);
115 if (!d->m_db.open()) {
116 qWarning().nospace() << "Warning: Failed to open settings database at " << fileName << " ("
117 << d->m_db.lastError().driverText() << ")";
118 } else {
119 // Create the settings table if it doesn't exist yet
120 QSqlQuery query(d->m_db);
121 query.prepare(QLatin1String("CREATE TABLE IF NOT EXISTS settings ("
122 "key PRIMARY KEY ON CONFLICT REPLACE, "
123 "value)"));
124 if (!query.exec()) {
125 qWarning().nospace() << "Warning: Failed to prepare settings database! ("
126 << query.lastError().driverText() << ")";
129 // Retrieve all available keys (values are retrieved lazily)
130 if (query.exec(QLatin1String("SELECT key FROM settings"))) {
131 while (query.next()) {
132 d->m_settings.insert(query.value(0).toString(), QVariant());
138 SettingsDatabase::~SettingsDatabase()
140 sync();
142 delete d;
143 QSqlDatabase::removeDatabase(QLatin1String("settings"));
146 void SettingsDatabase::setValue(const QString &key, const QVariant &value)
148 const QString effectiveKey = d->effectiveKey(key);
150 // Add to cache
151 d->m_settings.insert(effectiveKey, value);
153 if (!d->m_db.isOpen()) {
154 return;
157 // Instant apply (TODO: Delay writing out settings)
158 QSqlQuery query(d->m_db);
159 query.prepare(QLatin1String("INSERT INTO settings VALUES (?, ?)"));
160 query.addBindValue(effectiveKey);
161 query.addBindValue(value);
162 query.exec();
164 if (debug_settings) {
165 qDebug() << "Stored:" << effectiveKey << "=" << value;
169 QVariant SettingsDatabase::value(const QString &key, const QVariant &defaultValue) const
171 const QString effectiveKey = d->effectiveKey(key);
172 QVariant value = defaultValue;
174 SettingsMap::const_iterator i = d->m_settings.constFind(effectiveKey);
176 if (i != d->m_settings.constEnd() && i.value().isValid()) {
177 value = i.value();
178 } else if (d->m_db.isOpen()) {
179 // Try to read the value from the database
180 QSqlQuery query(d->m_db);
181 query.prepare(QLatin1String("SELECT value FROM settings WHERE key = ?"));
182 query.addBindValue(effectiveKey);
183 query.exec();
184 if (query.next()) {
185 value = query.value(0);
187 if (debug_settings) {
188 qDebug() << "Retrieved:" << effectiveKey << "=" << value;
192 // Cache the result
193 d->m_settings.insert(effectiveKey, value);
196 return value;
199 bool SettingsDatabase::contains(const QString &key) const
201 return d->m_settings.contains(d->effectiveKey(key));
204 void SettingsDatabase::remove(const QString &key)
206 const QString effectiveKey = d->effectiveKey(key);
208 // Remove keys from the cache
209 foreach(const QString &k, d->m_settings.keys()) {
210 // Either it's an exact match, or it matches up to a /
211 if (k.startsWith(effectiveKey)
212 && (k.length() == effectiveKey.length()
213 || k.at(effectiveKey.length()) == QLatin1Char('/'))) {
214 d->m_settings.remove(k);
218 if (!d->m_db.isOpen()) {
219 return;
222 // Delete keys from the database
223 QSqlQuery query(d->m_db);
224 query.prepare(QLatin1String("DELETE FROM settings WHERE key = ? OR key LIKE ?"));
225 query.addBindValue(effectiveKey);
226 query.addBindValue(QString(effectiveKey + QLatin1String("/%")));
227 query.exec();
230 void SettingsDatabase::beginGroup(const QString &prefix)
232 d->m_groups.append(prefix);
235 void SettingsDatabase::endGroup()
237 d->m_groups.removeLast();
240 QString SettingsDatabase::group() const
242 return d->effectiveGroup();
245 QStringList SettingsDatabase::childKeys() const
247 QStringList childs;
249 const QString g = group();
251 QMapIterator<QString, QVariant> i(d->m_settings);
252 while (i.hasNext()) {
253 const QString &key = i.next().key();
254 if (key.startsWith(g) && key.indexOf(QLatin1Char('/'), g.length() + 1) == -1) {
255 childs.append(key.mid(g.length() + 1));
259 return childs;
262 void SettingsDatabase::sync()
264 // TODO: Delay writing of dirty keys and save them here