18#include "windowstatestorage.h"
27#include <QStandardPaths>
29#include <lomiri/shell/application/ApplicationInfoInterface.h>
32class AsyncQuery:
public QObject
37 AsyncQuery(
const QString &dbName):
44 QSqlDatabase::removeDatabase(m_connectionName);
47 Q_INVOKABLE
const QString getDbName()
52 QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
53 return connection.databaseName();
56 Q_INVOKABLE
bool initdb()
61 QSqlDatabase connection = QSqlDatabase::addDatabase(QStringLiteral(
"QSQLITE"), m_connectionName);
62 connection.setDatabaseName(m_dbName);
63 connection.setConnectOptions(QStringLiteral(
"QSQLITE_BUSY_TIMEOUT=1000"));
64 if (!connection.open()) {
65 qWarning() <<
"AsyncQuery::initdb: Error opening state database. Window positions will not be saved or restored." << m_dbName << connection.lastError().driverText() << connection.lastError().databaseText();
68 QSqlQuery query(connection);
70 if (!connection.tables().contains(QStringLiteral(
"geometry"))) {
71 QString geometryQuery = QStringLiteral(
"CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);");
72 if (!query.exec(geometryQuery)) {
78 if (!connection.tables().contains(QStringLiteral(
"state"))) {
79 QString stateQuery = QStringLiteral(
"CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);");
80 if (!query.exec(stateQuery)) {
86 if (!connection.tables().contains(QStringLiteral(
"stage"))) {
87 QString stageQuery = QStringLiteral(
"CREATE TABLE stage(appId TEXT UNIQUE, stage INTEGER);");
88 if (!query.exec(stageQuery)) {
97 Q_INVOKABLE
int getState(
const QString &windowId)
const
102 QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
103 QSqlQuery query(connection);
104 query.prepare(m_getStateQuery);
105 query.bindValue(
":windowId", windowId);
107 if (!query.isActive() || !query.isSelect()) {
111 if (!query.first()) {
114 bool converted =
false;
115 QVariant resultStr = query.value(0);
116 int result = resultStr.toInt(&converted);
120 qWarning() <<
"getState result expected integer, got " << resultStr;
125 Q_INVOKABLE QRect getGeometry(
const QString &windowId)
const
130 QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
131 QSqlQuery query(connection);
132 query.prepare(m_getGeometryQuery);
133 query.bindValue(
":windowId", windowId);
135 if (!query.isActive() || !query.isSelect()) {
140 if (!query.first()) {
144 bool xConverted, yConverted, widthConverted, heightConverted =
false;
145 int x, y, width, height;
146 QVariant xResultStr = query.value(QStringLiteral(
"x"));
147 QVariant yResultStr = query.value(QStringLiteral(
"y"));
148 QVariant widthResultStr = query.value(QStringLiteral(
"width"));
149 QVariant heightResultStr = query.value(QStringLiteral(
"height"));
150 x = xResultStr.toInt(&xConverted);
151 y = yResultStr.toInt(&yConverted);
152 width = widthResultStr.toInt(&widthConverted);
153 height = heightResultStr.toInt(&heightConverted);
155 if (xConverted && yConverted && widthConverted && heightConverted) {
156 return QRect(x, y, width, height);
158 qWarning() <<
"getGeometry result expected integers, got x:"
159 << xResultStr <<
"y:" << yResultStr <<
"width" << widthResultStr
160 <<
"height:" << heightResultStr;
166 Q_INVOKABLE
int getStage(
const QString &appId)
const
171 QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
172 QSqlQuery query(connection);
173 query.prepare(m_getStageQuery);
174 query.bindValue(
":appId", appId);
176 if (!query.isActive() || !query.isSelect()) {
180 if (!query.first()) {
183 bool converted =
false;
184 QVariant resultStr = query.value(0);
185 int result = resultStr.toInt(&converted);
189 qWarning() <<
"getStage result expected integer, got " << resultStr;
196 void saveState(
const QString &windowId, WindowStateStorage::WindowState state)
201 QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
202 QSqlQuery query(connection);
203 query.prepare(m_saveStateQuery);
204 query.bindValue(
":windowId", windowId);
205 query.bindValue(
":state", (
int)state);
211 void saveGeometry(
const QString &windowId,
const QRect &rect)
216 QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
217 QSqlQuery query(connection);
218 query.prepare(m_saveGeometryQuery);
219 query.bindValue(
":windowId", windowId);
220 query.bindValue(
":x", rect.x());
221 query.bindValue(
":y", rect.y());
222 query.bindValue(
":width", rect.width());
223 query.bindValue(
":height", rect.height());
229 void saveStage(
const QString &appId,
int stage)
234 QSqlDatabase connection = QSqlDatabase::database(m_connectionName);
235 QSqlQuery query(connection);
236 query.prepare(m_saveStageQuery);
237 query.bindValue(
":appId", appId);
238 query.bindValue(
":stage", stage);
245 static const QString m_connectionName;
246 static const QString m_getStateQuery;
247 static const QString m_saveStateQuery;
248 static const QString m_getGeometryQuery;
249 static const QString m_saveGeometryQuery;
250 static const QString m_getStageQuery;
251 static const QString m_saveStageQuery;
253 static void logSqlError(
const QSqlQuery query)
255 qWarning() <<
"Error executing query" << query.lastQuery()
256 <<
"Driver error:" << query.lastError().driverText()
257 <<
"Database error:" << query.lastError().databaseText();
264const QString AsyncQuery::m_connectionName = QStringLiteral(
"WindowStateStorage");
265const QString AsyncQuery::m_getStateQuery = QStringLiteral(
"SELECT state FROM state WHERE windowId = :windowId");
266const QString AsyncQuery::m_saveStateQuery = QStringLiteral(
"INSERT OR REPLACE INTO state (windowId, state) values (:windowId, :state)");
267const QString AsyncQuery::m_getGeometryQuery = QStringLiteral(
"SELECT * FROM geometry WHERE windowId = :windowId");
268const QString AsyncQuery::m_saveGeometryQuery = QStringLiteral(
"INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values (:windowId, :x, :y, :width, :height)");
269const QString AsyncQuery::m_getStageQuery = QStringLiteral(
"SELECT stage FROM stage WHERE appId = :appId");
270const QString AsyncQuery::m_saveStageQuery = QStringLiteral(
"INSERT OR REPLACE INTO stage (appId, stage) values (:appId, :stage)");
272WindowStateStorage::WindowStateStorage(
const QString &dbName, QObject *parent):
276 qRegisterMetaType<WindowStateStorage::WindowState>(
"WindowStateStorage::WindowState");
278 if (dbName.isEmpty()) {
279 const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral(
"/lomiri/");
282 dbFile = QString(dbPath) + QStringLiteral(
"windowstatestorage.sqlite");
286 m_asyncQuery =
new AsyncQuery(dbFile);
287 m_asyncQuery->moveToThread(&m_thread);
288 connect(&m_thread, &QThread::finished, m_asyncQuery, &QObject::deleteLater);
293 QMetaObject::invokeMethod(m_asyncQuery,
"initdb",
294 Qt::QueuedConnection);
295 connect(
this, &WindowStateStorage::saveState, m_asyncQuery, &AsyncQuery::saveState);
296 connect(
this, &WindowStateStorage::saveGeometry, m_asyncQuery, &AsyncQuery::saveGeometry);
297 connect(
this, &WindowStateStorage::saveStage, m_asyncQuery, &AsyncQuery::saveStage);
300WindowStateStorage::~WindowStateStorage()
306WindowStateStorage::WindowState WindowStateStorage::getState(
const QString &windowId, WindowStateStorage::WindowState defaultValue)
const
310 QMetaObject::invokeMethod(m_asyncQuery,
"getState", Qt::BlockingQueuedConnection,
311 Q_RETURN_ARG(
int, state),
312 Q_ARG(
const QString&, windowId)
319 return (WindowState)state;
322int WindowStateStorage::getStage(
const QString &appId,
int defaultValue)
const
326 QMetaObject::invokeMethod(m_asyncQuery,
"getStage", Qt::BlockingQueuedConnection,
327 Q_RETURN_ARG(
int, stage),
328 Q_ARG(
const QString&, appId)
338QRect WindowStateStorage::getGeometry(
const QString &windowId,
const QRect &defaultValue)
const
341 QMetaObject::invokeMethod(m_asyncQuery,
"getGeometry", Qt::BlockingQueuedConnection,
342 Q_RETURN_ARG(QRect, geometry),
343 Q_ARG(
const QString&, windowId)
345 if (geometry.isNull() || !geometry.isValid()) {
351const QString WindowStateStorage::getDbName()
354 QMetaObject::invokeMethod(m_asyncQuery,
"getDbName", Qt::BlockingQueuedConnection,
355 Q_RETURN_ARG(QString, dbName)
357 return QString(dbName);
360Mir::State WindowStateStorage::toMirState(WindowState state)
const
364 case WindowStateMaximized:
return Mir::MaximizedState;
365 case WindowStateMinimized:
return Mir::MinimizedState;
366 case WindowStateFullscreen:
return Mir::FullscreenState;
367 case WindowStateMaximizedLeft:
return Mir::MaximizedLeftState;
368 case WindowStateMaximizedRight:
return Mir::MaximizedRightState;
369 case WindowStateMaximizedHorizontally:
return Mir::HorizMaximizedState;
370 case WindowStateMaximizedVertically:
return Mir::VertMaximizedState;
371 case WindowStateMaximizedTopLeft:
return Mir::MaximizedTopLeftState;
372 case WindowStateMaximizedTopRight:
return Mir::MaximizedTopRightState;
373 case WindowStateMaximizedBottomLeft:
return Mir::MaximizedBottomLeftState;
374 case WindowStateMaximizedBottomRight:
return Mir::MaximizedBottomRightState;
376 case WindowStateNormal:
377 case WindowStateRestored:
379 return Mir::RestoredState;
383#include "windowstatestorage.moc"