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
9 * @addtogroup CorePlugin Core Plugin
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
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>
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.
55 using namespace Core::Internal
;
57 enum { debug_settings
= 0 };
61 typedef QMap
<QString
, QVariant
> SettingsMap
;
63 class SettingsDatabasePrivate
{
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('/');
81 SettingsMap m_settings
;
84 QStringList m_dirtyKeys
;
88 } // namespace Internal
91 SettingsDatabase::SettingsDatabase(const QString
&path
,
92 const QString
&application
,
95 , d(new SettingsDatabasePrivate
)
97 const QLatin1Char
slash('/');
99 // TODO: Don't rely on a path, but determine automatically
102 if (!pathDir
.exists()) {
103 pathDir
.mkpath(pathDir
.absolutePath());
106 QString fileName
= path
;
107 if (!fileName
.endsWith(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() << ")";
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, "
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()
143 QSqlDatabase::removeDatabase(QLatin1String("settings"));
146 void SettingsDatabase::setValue(const QString
&key
, const QVariant
&value
)
148 const QString effectiveKey
= d
->effectiveKey(key
);
151 d
->m_settings
.insert(effectiveKey
, value
);
153 if (!d
->m_db
.isOpen()) {
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
);
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()) {
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
);
185 value
= query
.value(0);
187 if (debug_settings
) {
188 qDebug() << "Retrieved:" << effectiveKey
<< "=" << value
;
193 d
->m_settings
.insert(effectiveKey
, 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()) {
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("/%")));
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
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));
262 void SettingsDatabase::sync()
264 // TODO: Delay writing of dirty keys and save them here