Unity 8
windowstatestorage.cpp
1 /*
2  * Copyright 2015-2016 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 <QSqlQuery>
22 #include <QSqlError>
23 #include <QSqlResult>
24 #include <QRect>
25 #include <unity/shell/application/ApplicationInfoInterface.h>
26 
27 QMutex WindowStateStorage::s_mutex;
28 
29 inline QString sanitiseString(QString string) {
30  return string.remove(QLatin1Char('\"'))
31  .remove(QLatin1Char('\''))
32  .remove(QLatin1Char('\\'));
33 }
34 
35 WindowStateStorage::WindowStateStorage(const QString& dbName, QObject *parent):
36  QObject(parent)
37 {
38  const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/unity8/");
39  m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"));
40  QDir dir;
41  dir.mkpath(dbPath);
42  if (dbName != nullptr) {
43  m_db.setDatabaseName(dbName);
44  } else {
45  m_db.setDatabaseName(dbPath + "windowstatestorage.sqlite");
46  }
47  initdb();
48 }
49 
50 WindowStateStorage::~WindowStateStorage()
51 {
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 state 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(stage);
94 
95  saveValue(queryString);
96 }
97 
98 int WindowStateStorage::getStage(const QString &appId, int defaultValue) const
99 {
100  const QString queryString = QStringLiteral("SELECT stage 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 QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
112 {
113  QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
114  .arg(sanitiseString(windowId));
115 
116  QSqlQuery query = getValue(queryString);
117 
118  if (!query.first()) {
119  return defaultValue;
120  }
121 
122  const QRect result(query.value(QStringLiteral("x")).toInt(), query.value(QStringLiteral("y")).toInt(),
123  query.value(QStringLiteral("width")).toInt(), query.value(QStringLiteral("height")).toInt());
124 
125  if (result.isValid()) {
126  return result;
127  }
128 
129  return defaultValue;
130 }
131 
132 void WindowStateStorage::initdb()
133 {
134  m_db.open();
135  if (!m_db.open()) {
136  qWarning() << "Error opening state database:" << m_db.lastError().driverText() << m_db.lastError().databaseText();
137  return;
138  }
139 
140  if (!m_db.tables().contains(QStringLiteral("geometry"))) {
141  QSqlQuery query;
142  query.exec(QStringLiteral("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);"));
143  }
144 
145  if (!m_db.tables().contains(QStringLiteral("state"))) {
146  QSqlQuery query;
147  query.exec(QStringLiteral("CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);"));
148  }
149 
150  if (!m_db.tables().contains(QStringLiteral("stage"))) {
151  QSqlQuery query;
152  query.exec(QStringLiteral("CREATE TABLE stage(appId TEXT UNIQUE, stage INTEGER);"));
153  }
154 }
155 
156 void WindowStateStorage::saveValue(const QString &queryString)
157 {
158  QMutexLocker mutexLocker(&s_mutex);
159  QSqlQuery query;
160  auto ok = query.exec(queryString);
161  if (!ok) {
162  qWarning() << "Error executing query" << queryString
163  << "Driver error:" << query.lastError().driverText()
164  << "Database error:" << query.lastError().databaseText();
165  }
166 }
167 
168 QSqlQuery WindowStateStorage::getValue(const QString &queryString) const
169 {
170  QMutexLocker l(&s_mutex);
171  QSqlQuery query;
172 
173  bool ok = query.exec(queryString);
174  if (!ok) {
175  qWarning() << "Error retrieving database query:" << queryString
176  << "Driver error:" << query.lastError().driverText()
177  << "Database error:" << query.lastError().databaseText();
178  }
179  return query;
180 }
181 
182 Mir::State WindowStateStorage::toMirState(WindowState state) const
183 {
184  // assumes a single state (not an OR of several)
185  switch (state) {
186  case WindowStateMaximized: return Mir::MaximizedState;
187  case WindowStateMinimized: return Mir::MinimizedState;
188  case WindowStateFullscreen: return Mir::FullscreenState;
189  case WindowStateMaximizedLeft: return Mir::MaximizedLeftState;
190  case WindowStateMaximizedRight: return Mir::MaximizedRightState;
191  case WindowStateMaximizedHorizontally: return Mir::HorizMaximizedState;
192  case WindowStateMaximizedVertically: return Mir::VertMaximizedState;
193  case WindowStateMaximizedTopLeft: return Mir::MaximizedTopLeftState;
194  case WindowStateMaximizedTopRight: return Mir::MaximizedTopRightState;
195  case WindowStateMaximizedBottomLeft: return Mir::MaximizedBottomLeftState;
196  case WindowStateMaximizedBottomRight: return Mir::MaximizedBottomRightState;
197 
198  case WindowStateNormal:
199  case WindowStateRestored:
200  default:
201  return Mir::RestoredState;
202  }
203 }