Unity 8
windowstatestorage.cpp
1 /*
2  * Copyright 2015 Canonical Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include "windowstatestorage.h"
18 
19 #include <QtConcurrent>
20 #include <QDebug>
21 #include <QFutureSynchronizer>
22 #include <QSqlQuery>
23 #include <QSqlError>
24 #include <QSqlResult>
25 #include <QRect>
26 #include <unity/shell/application/ApplicationInfoInterface.h>
27 
28 QMutex WindowStateStorage::s_mutex;
29 
30 inline QString sanitiseString(QString string) {
31  return string.remove("\"").remove("'").remove("\\");
32 }
33 
34 WindowStateStorage::WindowStateStorage(QObject *parent):
35  QObject(parent)
36 {
37  QString dbPath = QDir::homePath() + "/.cache/unity8/";
38  m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"));
39  QDir dir;
40  dir.mkpath(dbPath);
41  m_db.setDatabaseName(dbPath + "windowstatestorage.sqlite");
42  initdb();
43 }
44 
45 WindowStateStorage::~WindowStateStorage()
46 {
47  QFutureSynchronizer<void> futureSync;
48  for (int i = 0; i < m_asyncQueries.count(); ++i) {
49  futureSync.addFuture(m_asyncQueries[i]);
50  }
51  futureSync.waitForFinished();
52  m_db.close();
53 }
54 
55 void WindowStateStorage::saveState(const QString &windowId, WindowStateStorage::WindowState state)
56 {
57  const QString queryString = QStringLiteral("INSERT OR REPLACE INTO state (windowId, state) values ('%1', '%2');")
58  .arg(sanitiseString(windowId))
59  .arg((int)state);
60 
61  saveValue(queryString);
62 }
63 
64 WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const
65 {
66  const QString queryString = QStringLiteral("SELECT * FROM state WHERE windowId = '%1';")
67  .arg(sanitiseString(windowId));
68 
69  QSqlQuery query = getValue(queryString);
70 
71  if (!query.first()) {
72  return defaultValue;
73  }
74  return (WindowState)query.value(QStringLiteral("state")).toInt();
75 }
76 
77 void WindowStateStorage::saveGeometry(const QString &windowId, const QRect rect)
78 {
79  const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
80  .arg(sanitiseString(windowId))
81  .arg(rect.x())
82  .arg(rect.y())
83  .arg(rect.width())
84  .arg(rect.height());
85 
86  saveValue(queryString);
87 }
88 
89 void WindowStateStorage::saveStage(const QString &appId, int stage)
90 {
91  const QString queryString = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values ('%1', '%2');")
92  .arg(sanitiseString(appId))
93  .arg((int)stage);
94 
95  saveValue(queryString);
96 }
97 
98 int WindowStateStorage::getStage(const QString &appId, int defaultValue) const
99 {
100  const QString queryString = QStringLiteral("SELECT * FROM stage WHERE appId = '%1';")
101  .arg(sanitiseString(appId));
102 
103  QSqlQuery query = getValue(queryString);
104 
105  if (!query.first()) {
106  return defaultValue;
107  }
108  return query.value("stage").toInt();
109 }
110 
111 void WindowStateStorage::executeAsyncQuery(const QString &queryString)
112 {
113  QMutexLocker l(&s_mutex);
114  QSqlQuery query;
115 
116  bool ok = query.exec(queryString);
117  if (!ok) {
118  qWarning() << "Error executing query" << queryString
119  << "Driver error:" << query.lastError().driverText()
120  << "Database error:" << query.lastError().databaseText();
121  }
122 }
123 
124 QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect defaultValue) const
125 {
126  QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
127  .arg(sanitiseString(windowId));
128 
129  QSqlQuery query = getValue(queryString);
130 
131  if (!query.first()) {
132  return defaultValue;
133  }
134  return QRect(query.value(QStringLiteral("x")).toInt(), query.value(QStringLiteral("y")).toInt(), query.value(QStringLiteral("width")).toInt(), query.value(QStringLiteral("height")).toInt());
135 }
136 
137 void WindowStateStorage::initdb()
138 {
139  m_db.open();
140  if (!m_db.open()) {
141  qWarning() << "Error opening state database:" << m_db.lastError().driverText() << m_db.lastError().databaseText();
142  return;
143  }
144 
145  if (!m_db.tables().contains(QStringLiteral("geometry"))) {
146  QSqlQuery query;
147  query.exec(QStringLiteral("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);"));
148  }
149 
150  if (!m_db.tables().contains(QStringLiteral("state"))) {
151  QSqlQuery query;
152  query.exec(QStringLiteral("CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);"));
153  }
154 
155  if (!m_db.tables().contains(QStringLiteral("stage"))) {
156  QSqlQuery query;
157  query.exec(QStringLiteral("CREATE TABLE stage(appId TEXT UNIQUE, stage INTEGER);"));
158  }
159 }
160 
161 void WindowStateStorage::saveValue(const QString &queryString)
162 {
163  QMutexLocker mutexLocker(&s_mutex);
164 
165  QFuture<void> future = QtConcurrent::run(executeAsyncQuery, queryString);
166  m_asyncQueries.append(future);
167 
168  QFutureWatcher<void> *futureWatcher = new QFutureWatcher<void>();
169  futureWatcher->setFuture(future);
170  connect(futureWatcher, &QFutureWatcher<void>::finished,
171  this,
172  [=](){ m_asyncQueries.removeAll(futureWatcher->future());
173  futureWatcher->deleteLater(); });
174 }
175 
176 QSqlQuery WindowStateStorage::getValue(const QString &queryString) const
177 {
178  QMutexLocker l(&s_mutex);
179  QSqlQuery query;
180 
181  bool ok = query.exec(queryString);
182  if (!ok) {
183  qWarning() << "Error retrieving database query:" << queryString
184  << "Driver error:" << query.lastError().driverText()
185  << "Database error:" << query.lastError().databaseText();
186  }
187  return query;
188 }