diff --git a/src/ui/3rdparty/sqlite/sqlitedb.cpp b/src/ui/3rdparty/sqlite/sqlitedb.cpp index d7453b61..41aefdce 100644 --- a/src/ui/3rdparty/sqlite/sqlitedb.cpp +++ b/src/ui/3rdparty/sqlite/sqlitedb.cpp @@ -1,8 +1,10 @@ #include "sqlitedb.h" #include +#include #include #include +#include #include @@ -59,7 +61,10 @@ QVariant SqliteDb::executeEx(const char *sql, bool *ok) { QVariantList list; + QStringList bindTexts; + QList bindDatas; + sqlite3_stmt *stmt = nullptr; int res; @@ -72,22 +77,29 @@ QVariant SqliteDb::executeEx(const char *sql, int index = 0; for (const QVariant &v : vars) { ++index; - switch (v.type()) { + + const int vType = v.type(); + + switch (vType) { case QVariant::Invalid: res = sqlite3_bind_null(stmt, index); break; + case QVariant::Bool: case QVariant::Int: + case QVariant::UInt: res = sqlite3_bind_int(stmt, index, v.toInt()); break; + case QVariant::LongLong: + case QVariant::ULongLong: + res = sqlite3_bind_int64(stmt, index, v.toLongLong()); + break; case QVariant::Double: res = sqlite3_bind_double(stmt, index, v.toDouble()); break; - case QVariant::LongLong: - res = sqlite3_bind_int64(stmt, index, v.toLongLong()); - break; case QVariant::String: { const QString text = v.toString(); const int bytesCount = text.size() * int(sizeof(wchar_t)); + bindTexts.append(text); res = sqlite3_bind_text16(stmt, index, text.utf16(), @@ -96,12 +108,37 @@ QVariant SqliteDb::executeEx(const char *sql, } default: { QByteArray data; - QDataStream stream(data); - stream << v; + + // Write type + QDataStream stream(&data, QIODevice::WriteOnly); + stream << vType; + + // Write content + { + QByteArray bufData; + + QBuffer buf(&bufData); + buf.open(QIODevice::WriteOnly); + + switch (vType) { + case QVariant::Image: { + const QImage image = v.value(); + image.save(&buf, "PNG"); + break; + } + default: + Q_UNREACHABLE(); + } + + buf.close(); + stream << bufData; + } const char *bits = data.constData(); const int bytesCount = data.size(); + bindDatas.append(data); + res = sqlite3_bind_blob(stmt, index, bits, bytesCount, SQLITE_STATIC); } @@ -135,7 +172,27 @@ QVariant SqliteDb::executeEx(const char *sql, QByteArray data(bits, bytesCount); QDataStream stream(data); - stream >> v; + + // Load type + int vType; + stream >> vType; + + // Load content + { + QByteArray bufData; + stream >> bufData; + + switch (vType) { + case QVariant::Image: { + QImage image; + image.loadFromData(bufData, "PNG"); + v = image; + break; + } + default: + Q_UNREACHABLE(); + } + } break; } case SQLITE_NULL: @@ -156,7 +213,9 @@ QVariant SqliteDb::executeEx(const char *sql, *ok = (res == SQLITE_OK || res == SQLITE_ROW || res == SQLITE_DONE); } - return (list.size() == 1) ? list.at(0) : list; + const int listSize = list.size(); + return (listSize == 0) ? QVariant() + : (list.size() == 1) ? list.at(0) : list; } qint64 SqliteDb::lastInsertRowid() const @@ -248,7 +307,8 @@ bool SqliteDb::migrate(const QString &sqlDir, int version, QFile file(filePath); if (!file.open(QFile::ReadOnly | QFile::Text)) { - qWarning() << "SQLite: Cannot open migration file" << filePath; + qWarning() << "SQLite: Cannot open migration file" << filePath + << file.errorString(); res = false; break; } diff --git a/src/ui/FortFirewall.pro b/src/ui/FortFirewall.pro index b740c52d..10a5061e 100644 --- a/src/ui/FortFirewall.pro +++ b/src/ui/FortFirewall.pro @@ -1,4 +1,4 @@ -QT += core gui qml widgets +QT += core gui qml quick widgets CONFIG += c++11 @@ -46,8 +46,10 @@ SOURCES += \ task/taskupdatechecker.cpp \ task/taskworker.cpp \ translationmanager.cpp \ + util/app/appiconprovider.cpp \ util/app/appinfo.cpp \ util/app/appinfocache.cpp \ + util/app/appinfojob.cpp \ util/app/appinfomanager.cpp \ util/app/appinfoworker.cpp \ util/app/apputil.cpp \ @@ -62,8 +64,8 @@ SOURCES += \ util/nativeeventfilter.cpp \ util/net/hostinfo.cpp \ util/net/hostinfocache.cpp \ + util/net/hostinfojob.cpp \ util/net/hostinfomanager.cpp \ - util/net/hostinfoworker.cpp \ util/net/ip4range.cpp \ util/net/netdownloader.cpp \ util/net/netutil.cpp \ @@ -74,6 +76,7 @@ SOURCES += \ util/window/widgetwindow.cpp \ util/window/widgetwindowstatewatcher.cpp \ util/window/windowstatewatcher.cpp \ + util/worker/workerjob.cpp \ util/worker/workermanager.cpp \ util/worker/workerobject.cpp @@ -113,8 +116,10 @@ HEADERS += \ task/taskupdatechecker.h \ task/taskworker.h \ translationmanager.h \ + util/app/appiconprovider.h \ util/app/appinfo.h \ util/app/appinfocache.h \ + util/app/appinfojob.h \ util/app/appinfomanager.h \ util/app/appinfoworker.h \ util/app/apputil.h \ @@ -129,8 +134,8 @@ HEADERS += \ util/nativeeventfilter.h \ util/net/hostinfo.h \ util/net/hostinfocache.h \ + util/net/hostinfojob.h \ util/net/hostinfomanager.h \ - util/net/hostinfoworker.h \ util/net/ip4range.h \ util/net/netdownloader.h \ util/net/netutil.h \ @@ -141,6 +146,7 @@ HEADERS += \ util/window/widgetwindow.h \ util/window/widgetwindowstatewatcher.h \ util/window/windowstatewatcher.h \ + util/worker/workerjob.h \ util/worker/workermanager.h \ util/worker/workerobject.h @@ -168,8 +174,8 @@ OTHER_FILES += \ util/app/migrations/*.sql RESOURCES += \ - db/migrations.qrc \ - util/app/migrations.qrc + db/db-migrations.qrc \ + util/app/app-migrations.qrc # Shadow Build: Copy i18n/ to build path !equals(PWD, $${OUT_PWD}) { diff --git a/src/ui/db/migrations.qrc b/src/ui/db/db-migrations.qrc similarity index 100% rename from src/ui/db/migrations.qrc rename to src/ui/db/db-migrations.qrc diff --git a/src/ui/db/migrations/1.sql b/src/ui/db/migrations/1.sql index f005d34c..3f9ffd41 100644 --- a/src/ui/db/migrations/1.sql +++ b/src/ui/db/migrations/1.sql @@ -1,6 +1,6 @@ PRAGMA user_version = 1; -PRAGMA journal_mode=WAL; +PRAGMA journal_mode = WAL; CREATE TABLE IF NOT EXISTS app( app_id INTEGER PRIMARY KEY, diff --git a/src/ui/fortmanager.cpp b/src/ui/fortmanager.cpp index aeb5986c..e53429b7 100644 --- a/src/ui/fortmanager.cpp +++ b/src/ui/fortmanager.cpp @@ -26,6 +26,7 @@ #include "task/taskinfo.h" #include "task/taskmanager.h" #include "translationmanager.h" +#include "util/app/appiconprovider.h" #include "util/app/appinfocache.h" #include "util/fileutil.h" #include "util/guiutil.h" @@ -59,7 +60,8 @@ FortManager::FortManager(FortSettings *fortSettings, m_driverManager->driverWorker(), this)), m_nativeEventFilter(new NativeEventFilter(this)), m_hotKeyManager(new HotKeyManager(m_nativeEventFilter, this)), - m_taskManager(new TaskManager(this, this)) + m_taskManager(new TaskManager(this, this)), + m_appInfoCache(new AppInfoCache(this)) { setupLogger(); setupDatabaseManager(); @@ -219,6 +221,10 @@ bool FortManager::setupEngine() context->setContextProperty("fortManager", this); context->setContextProperty("driverManager", m_driverManager); context->setContextProperty("translationManager", TranslationManager::instance()); + context->setContextProperty("appInfoCache", m_appInfoCache); + + m_engine->addImageProvider(AppIconProvider::id(), + new AppIconProvider(m_appInfoCache->manager())); m_engine->load(QUrl("qrc:/qml/main.qml")); diff --git a/src/ui/fortmanager.h b/src/ui/fortmanager.h index adf95b2a..f9a4db89 100644 --- a/src/ui/fortmanager.h +++ b/src/ui/fortmanager.h @@ -8,6 +8,7 @@ QT_FORWARD_DECLARE_CLASS(QQmlApplicationEngine) QT_FORWARD_DECLARE_CLASS(QSystemTrayIcon) +QT_FORWARD_DECLARE_CLASS(AppInfoCache) QT_FORWARD_DECLARE_CLASS(DatabaseManager) QT_FORWARD_DECLARE_CLASS(DriverManager) QT_FORWARD_DECLARE_CLASS(FirewallConf) @@ -167,6 +168,7 @@ private: NativeEventFilter *m_nativeEventFilter; HotKeyManager *m_hotKeyManager; TaskManager *m_taskManager; + AppInfoCache *m_appInfoCache; }; #endif // FORTMANAGER_H diff --git a/src/ui/qml/controls/ListViewControl.qml b/src/ui/qml/controls/ListViewControl.qml index 6b9c2001..36d0f389 100644 --- a/src/ui/qml/controls/ListViewControl.qml +++ b/src/ui/qml/controls/ListViewControl.qml @@ -9,10 +9,6 @@ ListView { readonly property bool hasCurrentItem: currentIndex >= 0 && currentIndex < count && !!currentItem - readonly property string currentItemText: - (currentIndex >= 0 && currentIndex < count && currentItem) - ? currentItem.displayText : "" - Keys.onUpPressed: decrementCurrentIndex() Keys.onDownPressed: incrementCurrentIndex() diff --git a/src/ui/qml/pages/BlockedPage.qml b/src/ui/qml/pages/BlockedPage.qml index 5e00c59a..0e0fbaa3 100644 --- a/src/ui/qml/pages/BlockedPage.qml +++ b/src/ui/qml/pages/BlockedPage.qml @@ -15,12 +15,12 @@ BasePage { readonly property Item currentAppItem: appListView.hasCurrentItem ? appListView.currentItem : null readonly property string currentAppPath: - (currentAppItem && currentAppItem.displayText) || "" + (currentAppItem && currentAppItem.appPath) || "" readonly property Item currentIpItem: ipListView.hasCurrentItem ? ipListView.currentItem : null readonly property string currentIpText: - (currentIpItem && currentIpItem.displayText) || "" + (currentIpItem && currentIpItem.ipAddress) || "" readonly property string currentHostName: (currentIpItem && currentIpItem.hostName) || "" diff --git a/src/ui/qml/pages/StatisticsPage.qml b/src/ui/qml/pages/StatisticsPage.qml index 7682225b..c61592af 100644 --- a/src/ui/qml/pages/StatisticsPage.qml +++ b/src/ui/qml/pages/StatisticsPage.qml @@ -18,7 +18,7 @@ BasePage { readonly property Item currentAppItem: appListView.hasCurrentItem ? appListView.currentItem : null readonly property string currentAppPath: - (currentAppItem && currentAppItem.displayText) || "" + (currentAppItem && currentAppItem.appPath) || "" readonly property var trafCellWidths: [ trafsContainer.width * 0.34, diff --git a/src/ui/qml/pages/log/AppListView.qml b/src/ui/qml/pages/log/AppListView.qml index 5690ac63..62f39395 100644 --- a/src/ui/qml/pages/log/AppListView.qml +++ b/src/ui/qml/pages/log/AppListView.qml @@ -1,5 +1,6 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 import "../../controls" import com.fortfirewall 1.0 @@ -13,25 +14,31 @@ ListViewControl { onClicked: forceActiveFocus() - delegate: Row { + delegate: RowLayout { id: appItem width: appListView.width spacing: 6 - readonly property string displayText: display + readonly property string appPath: display + + readonly property var appInfo: + appInfoCache.infoTrigger && appInfoCache.appInfo(appPath) - // TODO: Use SHGetFileInfo() to get app's display name and icon Image { - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 - source: (!appItem.displayText && emptyIcon) + Layout.topMargin: 1 + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + source: (!appItem.appPath && emptyIcon) + || appInfo.iconPath || "qrc:/images/application.png" } Label { - font.pixelSize: 20 + Layout.fillWidth: true + font.pixelSize: 18 elide: Text.ElideRight - text: (!appItem.displayText && emptyText) - || fileUtil.fileName(appItem.displayText) + text: (!appItem.appPath && emptyText) + || appInfo.fileDescription + || fileUtil.fileName(appItem.appPath) } } } diff --git a/src/ui/qml/pages/log/IpListView.qml b/src/ui/qml/pages/log/IpListView.qml index 2a4801b8..02ba2604 100644 --- a/src/ui/qml/pages/log/IpListView.qml +++ b/src/ui/qml/pages/log/IpListView.qml @@ -11,14 +11,14 @@ ListViewControl { delegate: Label { width: ipListView.width elide: Text.ElideRight - text: hostName || displayText + text: hostName || ipAddress font.italic: !!hostName readonly property string hostName: (firewallConf.resolveAddress && hostInfoCache.hostTrigger - && hostInfoCache.hostName(displayText)) || "" + && hostInfoCache.hostName(ipAddress)) || "" - readonly property string displayText: display + readonly property string ipAddress: display } } diff --git a/src/ui/util/app/migrations.qrc b/src/ui/util/app/app-migrations.qrc similarity index 100% rename from src/ui/util/app/migrations.qrc rename to src/ui/util/app/app-migrations.qrc diff --git a/src/ui/util/app/appiconprovider.cpp b/src/ui/util/app/appiconprovider.cpp new file mode 100644 index 00000000..e4c2dc34 --- /dev/null +++ b/src/ui/util/app/appiconprovider.cpp @@ -0,0 +1,38 @@ +#include "appiconprovider.h" + +#include "appinfomanager.h" + +AppIconProvider::AppIconProvider(AppInfoManager *manager) : + QQuickImageProvider(QQuickImageProvider::Image), + m_manager(manager) +{ +} + +QImage AppIconProvider::requestImage(const QString &id, QSize *size, + const QSize &requestedSize) +{ + Q_UNUSED(size) + Q_UNUSED(requestedSize) + + QImage icon; + + bool ok; + const qint64 iconId = id.toLongLong(&ok, 16); + if (ok && iconId != 0) { + icon = m_manager->loadIconFromDb(iconId); + } + + return icon.isNull() ? QImage(":/images/application.png") + : icon; +} + +QString AppIconProvider::id() +{ + return QLatin1String("app-icon"); +} + +QString AppIconProvider::iconPath(qint64 iconId) +{ + return QLatin1String("image://") + id() + + QString("/%1").arg(iconId, 0, 16); +} diff --git a/src/ui/util/app/appiconprovider.h b/src/ui/util/app/appiconprovider.h new file mode 100644 index 00000000..d9cd32e1 --- /dev/null +++ b/src/ui/util/app/appiconprovider.h @@ -0,0 +1,23 @@ +#ifndef APPICONPROVIDER_H +#define APPICONPROVIDER_H + +#include + +QT_FORWARD_DECLARE_CLASS(AppInfoManager) + +class AppIconProvider: public QQuickImageProvider +{ +public: + explicit AppIconProvider(AppInfoManager *manager); + + QImage requestImage(const QString &id, QSize *size, + const QSize &requestedSize) override; + + static QString id(); + static QString iconPath(qint64 iconId); + +private: + AppInfoManager *m_manager; +}; + +#endif // APPICONPROVIDER_H diff --git a/src/ui/util/app/appinfo.cpp b/src/ui/util/app/appinfo.cpp index 212df21d..bb169838 100644 --- a/src/ui/util/app/appinfo.cpp +++ b/src/ui/util/app/appinfo.cpp @@ -1 +1,8 @@ #include "appinfo.h" + +#include "appiconprovider.h" + +QString AppInfo::iconPath() const +{ + return AppIconProvider::iconPath(iconId); +} diff --git a/src/ui/util/app/appinfo.h b/src/ui/util/app/appinfo.h index 9318a29d..69b3de38 100644 --- a/src/ui/util/app/appinfo.h +++ b/src/ui/util/app/appinfo.h @@ -1,24 +1,27 @@ #ifndef APPINFO_H #define APPINFO_H -#include +#include class AppInfo { Q_GADGET + Q_PROPERTY(QString iconPath READ iconPath CONSTANT) Q_PROPERTY(QString fileDescription MEMBER fileDescription CONSTANT) Q_PROPERTY(QString companyName MEMBER companyName CONSTANT) Q_PROPERTY(QString productName MEMBER productName CONSTANT) Q_PROPERTY(QString productVersion MEMBER productVersion CONSTANT) - Q_PROPERTY(QPixmap icon MEMBER icon CONSTANT) public: + QString iconPath() const; + +public: + qint64 iconId = 0; + QString fileDescription; QString companyName; QString productName; QString productVersion; - - QPixmap icon; }; Q_DECLARE_METATYPE(AppInfo) diff --git a/src/ui/util/app/appinfocache.cpp b/src/ui/util/app/appinfocache.cpp index 0f57a113..e502ea65 100644 --- a/src/ui/util/app/appinfocache.cpp +++ b/src/ui/util/app/appinfocache.cpp @@ -17,7 +17,7 @@ AppInfoCache::AppInfoCache(QObject *parent) : this, &AppInfoCache::cacheChanged); } -AppInfo *AppInfoCache::appInfo(const QString &appPath) +AppInfo AppInfoCache::appInfo(const QString &appPath) { AppInfo *appInfo = m_cache.object(appPath); @@ -25,10 +25,10 @@ AppInfo *AppInfoCache::appInfo(const QString &appPath) appInfo = new AppInfo(); m_cache.insert(appPath, appInfo, 1); - m_manager->lookupApp(appPath); + m_manager->lookupAppInfo(appPath); } - return appInfo; + return *appInfo; } void AppInfoCache::handleFinishedLookup(const QString &appPath, diff --git a/src/ui/util/app/appinfocache.h b/src/ui/util/app/appinfocache.h index af3f01c0..df87c355 100644 --- a/src/ui/util/app/appinfocache.h +++ b/src/ui/util/app/appinfocache.h @@ -7,6 +7,7 @@ #include "appinfo.h" +QT_FORWARD_DECLARE_CLASS(AppIconProvider) QT_FORWARD_DECLARE_CLASS(AppInfoManager) class AppInfoCache : public QObject @@ -19,11 +20,13 @@ public: bool infoTrigger() const { return true; } + AppInfoManager *manager() const { return m_manager; } + signals: void cacheChanged(); public slots: - AppInfo *appInfo(const QString &appPath); + AppInfo appInfo(const QString &appPath); private slots: void handleFinishedLookup(const QString &appPath, diff --git a/src/ui/util/app/appinfojob.cpp b/src/ui/util/app/appinfojob.cpp new file mode 100644 index 00000000..6981462e --- /dev/null +++ b/src/ui/util/app/appinfojob.cpp @@ -0,0 +1,6 @@ +#include "appinfojob.h" + +AppInfoJob::AppInfoJob(const QString &appPath) : + WorkerJob(appPath) +{ +} diff --git a/src/ui/util/app/appinfojob.h b/src/ui/util/app/appinfojob.h new file mode 100644 index 00000000..5d5f4136 --- /dev/null +++ b/src/ui/util/app/appinfojob.h @@ -0,0 +1,18 @@ +#ifndef APPINFOJOB_H +#define APPINFOJOB_H + +#include "../worker/workerjob.h" +#include "appinfo.h" + +class AppInfoJob : public WorkerJob +{ +public: + explicit AppInfoJob(const QString &appPath); + + QString appPath() const { return text; } + +public: + AppInfo appInfo; +}; + +#endif // APPINFOJOB_H diff --git a/src/ui/util/app/appinfomanager.cpp b/src/ui/util/app/appinfomanager.cpp index 6520bfdf..01c50f5f 100644 --- a/src/ui/util/app/appinfomanager.cpp +++ b/src/ui/util/app/appinfomanager.cpp @@ -1,13 +1,116 @@ #include "appinfomanager.h" #include +#include +#include "../fileutil.h" +#include "appinfojob.h" #include "appinfoworker.h" +#include "apputil.h" +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(CLOG_APPINFOCACHE) +Q_LOGGING_CATEGORY(CLOG_APPINFOCACHE, "fort.appInfoWorker") + +#define DATABASE_USER_VERSION 1 + +#define APP_CACHE_MAX_COUNT 2000 + +namespace { + +const char * const sqlSelectAppInfo = + "SELECT file_descr, company_name," + " product_name, product_ver, icon_id" + " FROM app WHERE path = ?1;" + ; + +const char * const sqlUpdateAppAccessTime = + "UPDATE app" + " SET access_time = datetime('now')" + " WHERE path = ?1;" + ; + +const char * const sqlSelectIconImage = + "SELECT image FROM icon WHERE icon_id = ?1;" + ; + +const char * const sqlSelectIconIdByHash = + "SELECT icon_id FROM icon WHERE hash = ?1;" + ; + +const char * const sqlInsertIcon = + "INSERT INTO icon(ref_count, hash, image)" + " VALUES(1, ?1, ?2);" + ; + +const char * const sqlUpdateIconRefCount = + "UPDATE icon" + " SET ref_count = ref_count + ?2" + " WHERE icon_id = ?1;" + ; + +const char * const sqlInsertAppInfo = + "INSERT INTO app(path, file_descr, company_name," + " product_name, product_ver, icon_id, access_time)" + " VALUES(?1, ?2, ?3, ?4, ?5, ?6, datetime('now'));" + ; + +const char * const sqlSelectAppCount = + "SELECT count(*) FROM app;" + ; + +const char * const sqlSelectAppOlds = + "SELECT path, icon_id" + " FROM app" + " ORDER BY access_time DESC" + " LIMIT ?1;" + ; + +const char * const sqlDeleteIconIfNotUsed = + "DELETE FROM icon" + " WHERE icon_id = ?1 AND ref_count = 0;" + ; + +const char * const sqlDeleteApp = + "DELETE FROM app WHERE path = ?1;" + ; + +} AppInfoManager::AppInfoManager(QObject *parent) : - WorkerManager(parent) + WorkerManager(parent), + m_sqliteDb(new SqliteDb()) { setMaxWorkersCount(1); + + setupDb(); +} + +AppInfoManager::~AppInfoManager() +{ + delete m_sqliteDb; +} + +void AppInfoManager::setupDb() +{ + const QString cachePath = FileUtil::appCacheLocation(); + + FileUtil::makePath(cachePath); + + const QString filePath = cachePath + "/appinfocache.db"; + + if (!m_sqliteDb->open(filePath)) { + qCritical(CLOG_APPINFOCACHE()) << "File open error:" + << filePath + << m_sqliteDb->errorMessage(); + return; + } + + if (!m_sqliteDb->migrate(":/appinfocache/migrations", DATABASE_USER_VERSION)) { + qCritical(CLOG_APPINFOCACHE()) << "Migration error" << filePath; + return; + } } WorkerObject *AppInfoManager::createWorker() @@ -15,15 +118,183 @@ WorkerObject *AppInfoManager::createWorker() return new AppInfoWorker(this); } -void AppInfoManager::lookupApp(const QString &appPath) +void AppInfoManager::lookupAppInfo(const QString &appPath) { - enqueueJob(appPath); + enqueueJob(new AppInfoJob(appPath)); } -void AppInfoManager::handleWorkerResult(const QString &appPath, - const QVariant &result) +void AppInfoManager::handleWorkerResult(WorkerJob *workerJob) { - auto appInfo = result.value(); + if (!aborted()) { + auto job = static_cast(workerJob); - emit lookupFinished(appPath, appInfo); + emit lookupFinished(job->appPath(), job->appInfo); + } + + delete workerJob; +} + +bool AppInfoManager::loadInfoFromFs(const QString &appPath, AppInfo &appInfo) +{ + return AppUtil::getInfo(appPath, appInfo); +} + +QImage AppInfoManager::loadIconFromFs(const QString &appPath) +{ + return AppUtil::getIcon(appPath); +} + +bool AppInfoManager::loadInfoFromDb(const QString &appPath, AppInfo &appInfo) +{ + QMutexLocker locker(&m_mutex); + + const QVariantList vars = QVariantList() << appPath; + + // Load version info + const int resultCount = 5; + const QVariantList list = m_sqliteDb->executeEx( + sqlSelectAppInfo, vars, resultCount) + .toList(); + if (list.size() != resultCount) + return false; + + appInfo.fileDescription = list.at(0).toString(); + appInfo.companyName = list.at(1).toString(); + appInfo.productName = list.at(2).toString(); + appInfo.productVersion = list.at(3).toString(); + appInfo.iconId = list.at(4).toLongLong(); + + // Update last access time + m_sqliteDb->executeEx(sqlUpdateAppAccessTime, vars); + + return true; +} + +QImage AppInfoManager::loadIconFromDb(qint64 iconId) +{ + QMutexLocker locker(&m_mutex); + + const QVariant icon = m_sqliteDb->executeEx( + sqlSelectIconImage, QVariantList() << iconId); + + return icon.value(); +} + +bool AppInfoManager::saveToDb(const QString &appPath, AppInfo &appInfo, + const QImage &appIcon) +{ + QMutexLocker locker(&m_mutex); + + bool ok = true; + + m_sqliteDb->beginTransaction(); + + // Save icon image + QVariant iconId; + { + const uint iconHash = qHashBits(appIcon.constBits(), + size_t(appIcon.sizeInBytes())); + + iconId = m_sqliteDb->executeEx(sqlSelectIconIdByHash, + QVariantList() << iconHash); + if (iconId.isNull()) { + m_sqliteDb->executeEx(sqlInsertIcon, + QVariantList() << iconHash << appIcon, + 0, &ok); + if (ok) { + iconId = m_sqliteDb->lastInsertRowid(); + } + } else { + m_sqliteDb->executeEx(sqlUpdateIconRefCount, + QVariantList() << iconId << +1, + 0, &ok); + } + } + + // Save version info + if (ok) { + const QVariantList vars = QVariantList() + << appPath + << appInfo.fileDescription + << appInfo.companyName + << appInfo.productName + << appInfo.productVersion + << iconId + ; + + m_sqliteDb->executeEx(sqlInsertAppInfo, vars, 0, &ok); + } + + m_sqliteDb->endTransaction(ok); + + if (ok) { + appInfo.iconId = iconId.toLongLong(); + + // Delete excess info + const int appCount = m_sqliteDb->executeEx(sqlSelectAppCount).toInt(); + const int excessCount = appCount - APP_CACHE_MAX_COUNT; + + if (excessCount > 0) { + shrinkDb(excessCount); + } + } + + return ok; +} + +void AppInfoManager::shrinkDb(int excessCount) +{ + QStringList appPaths; + QHash iconIds; + + bool ok = false; + + m_sqliteDb->beginTransaction(); + + // Get old app info list + { + SqliteStmt stmt; + if (stmt.prepare(m_sqliteDb->db(), sqlSelectAppOlds, + SqliteStmt::PreparePersistent) + && stmt.bindInt(1, excessCount)) { + + while (stmt.step() == SqliteStmt::StepRow) { + const QString appPath = stmt.columnText(0); + appPaths.append(appPath); + + const qint64 iconId = stmt.columnInt64(1); + const int iconCount = iconIds.value(iconId); + iconIds.insert(iconId, iconCount + 1); + } + + ok = true; + } + } + + // Delete old icons + auto iconIt = iconIds.constBegin(); + while (iconIt != iconIds.constEnd()) { + const qint64 iconId = iconIt.key(); + const int count = iconIt.value(); + + m_sqliteDb->executeEx(sqlUpdateIconRefCount, + QVariantList() << iconId << -count, + 0, &ok); + if (!ok) goto end; + + m_sqliteDb->executeEx(sqlDeleteIconIfNotUsed, + QVariantList() << iconId, + 0, &ok); + if (!ok) goto end; + } + + // Delete old app infos + for (const QString &path : appPaths) { + m_sqliteDb->executeEx(sqlDeleteApp, QVariantList() << path, + 0, &ok); + if (!ok) goto end; + } + + end: + m_sqliteDb->endTransaction(ok); } diff --git a/src/ui/util/app/appinfomanager.h b/src/ui/util/app/appinfomanager.h index 670110e7..9dbb0235 100644 --- a/src/ui/util/app/appinfomanager.h +++ b/src/ui/util/app/appinfomanager.h @@ -1,27 +1,49 @@ #ifndef APPINFOMANAGER_H #define APPINFOMANAGER_H +#include + #include "../worker/workermanager.h" #include "appinfo.h" +QT_FORWARD_DECLARE_CLASS(SqliteDb) + class AppInfoManager : public WorkerManager { Q_OBJECT public: explicit AppInfoManager(QObject *parent = nullptr); + ~AppInfoManager() override; + + bool loadInfoFromFs(const QString &appPath, AppInfo &appInfo); + QImage loadIconFromFs(const QString &appPath); + + bool loadInfoFromDb(const QString &appPath, AppInfo &appInfo); + QImage loadIconFromDb(qint64 iconId); + + bool saveToDb(const QString &appPath, AppInfo &appInfo, + const QImage &appIcon); signals: void lookupFinished(const QString &appPath, const AppInfo appInfo); public slots: - void lookupApp(const QString &appPath); + void lookupAppInfo(const QString &appPath); - void handleWorkerResult(const QString &appPath, - const QVariant &result) override; + void handleWorkerResult(WorkerJob *workerJob) override; protected: WorkerObject *createWorker() override; + +private: + void setupDb(); + + void shrinkDb(int excessCount); + +private: + SqliteDb *m_sqliteDb; + QMutex m_mutex; }; #endif // APPINFOMANAGER_H diff --git a/src/ui/util/app/appinfoworker.cpp b/src/ui/util/app/appinfoworker.cpp index 7e6893b4..b6d86afb 100644 --- a/src/ui/util/app/appinfoworker.cpp +++ b/src/ui/util/app/appinfoworker.cpp @@ -1,277 +1,32 @@ #include "appinfoworker.h" #include -#include -#include "../fileutil.h" #include "appinfo.h" #include "appinfomanager.h" -#include "apputil.h" -#include -#include - -Q_DECLARE_LOGGING_CATEGORY(CLOG_APPINFOCACHE) -Q_LOGGING_CATEGORY(CLOG_APPINFOCACHE, "fort.appInfoWorker") - -#define DATABASE_USER_VERSION 1 - -namespace { - -const char * const sqlSelectAppInfo = - "SELECT file_descr, company_name," - " product_name, product_ver, icon_id" - " FROM app WHERE path = ?1;" - ; - -const char * const sqlUpdateAppAccessTime = - "UPDATE app" - " SET access_time = datetime('now')" - " WHERE path = ?1;" - ; - -const char * const sqlSelectIconImage = - "SELECT image FROM icon WHERE icon_id = ?1;" - ; - -const char * const sqlSelectIconIdByHash = - "SELECT icon_id FROM icon WHERE hash = ?1;" - ; - -const char * const sqlInsertIcon = - "INSERT INTO icon(hash, image)" - " VALUES(?1, ?2);" - ; - -const char * const sqlUpdateIconRefCount = - "UPDATE icon" - " SET ref_count = ref_count + ?2" - " WHERE icon_id = ?1;" - ; - -const char * const sqlInsertAppInfo = - "INSERT INTO app(path, file_descr, company_name," - " product_name, product_ver, icon_id)" - " VALUES(?1, ?2, ?3, ?4, ?5, ?6);" - ; - -const char * const sqlSelectAppCount = - "SELECT count(*) FROM app;" - ; - -const char * const sqlSelectAppOlds = - "SELECT path, icon_id" - " FROM app" - " ORDER BY access_time DESC" - " LIMIT ?1;" - ; - -const char * const sqlDeleteIconIfNotUsed = - "DELETE FROM icon" - " WHERE icon_id = ?1 AND ref_count = 0;" - ; - -const char * const sqlDeleteApp = - "DELETE FROM app WHERE path = ?1;" - ; - -} +#include "appinfojob.h" AppInfoWorker::AppInfoWorker(AppInfoManager *manager) : - WorkerObject(manager), - m_sqliteDb(new SqliteDb()) + WorkerObject(manager) { - setupDb(); } -AppInfoWorker::~AppInfoWorker() +AppInfoManager *AppInfoWorker::manager() const { - delete m_sqliteDb; + return static_cast(WorkerObject::manager()); } -void AppInfoWorker::setupDb() +void AppInfoWorker::doJob(WorkerJob *workerJob) { - const QString cachePath = FileUtil::appCacheLocation() + "/cache"; + auto job = static_cast(workerJob); + const QString &appPath = job->appPath(); - FileUtil::makePath(cachePath); + if (!manager()->loadInfoFromDb(appPath, job->appInfo) + && manager()->loadInfoFromFs(appPath, job->appInfo)) { + const QImage appIcon = manager()->loadIconFromFs(appPath); - const QString filePath = cachePath + "/appinfocache.db"; - - if (!m_sqliteDb->open(filePath)) { - qCritical(CLOG_APPINFOCACHE()) << "File open error:" - << filePath - << m_sqliteDb->errorMessage(); - return; + manager()->saveToDb(appPath, job->appInfo, appIcon); } - if (!m_sqliteDb->migrate(":/appinfocache/migrations", DATABASE_USER_VERSION)) { - qCritical(CLOG_APPINFOCACHE()) << "Migration error" << filePath; - return; - } -} - -void AppInfoWorker::doJob(const QString &appPath) -{ - AppInfo appInfo; - - if (!loadFromDb(appPath, appInfo) - && loadFromFs(appPath, appInfo)) { - saveToDb(appPath, appInfo); - } - - if (aborted()) return; - - manager()->handleWorkerResult(appPath, QVariant::fromValue(appInfo)); -} - -bool AppInfoWorker::loadFromFs(const QString &appPath, AppInfo &appInfo) -{ - return AppUtil::getInfo(appPath, appInfo); -} - -bool AppInfoWorker::loadFromDb(const QString &appPath, AppInfo &appInfo) -{ - const QVariantList vars = QVariantList() << appPath; - - // Load version info - const int resultCount = 5; - const QVariantList list = m_sqliteDb->executeEx( - sqlSelectAppInfo, vars, resultCount) - .toList(); - if (list.size() != resultCount) - return false; - - appInfo.fileDescription = list.at(0).toString(); - appInfo.companyName = list.at(1).toString(); - appInfo.productName = list.at(2).toString(); - appInfo.productVersion = list.at(3).toString(); - - // Update last access time - m_sqliteDb->executeEx(sqlUpdateAppAccessTime, vars); - - // Load icon image - const QVariant iconId = list.at(4); - const QVariant icon = m_sqliteDb->executeEx( - sqlSelectIconImage, QVariantList() << iconId); - - appInfo.icon = icon.value(); - - return true; -} - -bool AppInfoWorker::saveToDb(const QString &appPath, const AppInfo &appInfo) -{ - bool ok = true; - - m_sqliteDb->beginTransaction(); - - // Save icon image - QVariant iconId; - { - const QPixmap pixmap = appInfo.icon; - - const QImage image = pixmap.toImage(); - const uint iconHash = qHashBits(image.constBits(), - size_t(image.sizeInBytes())); - - iconId = m_sqliteDb->executeEx(sqlSelectIconIdByHash, - QVariantList() << iconHash); - if (iconId.isNull()) { - m_sqliteDb->executeEx(sqlInsertIcon, - QVariantList() << iconHash << pixmap, - 0, &ok); - if (ok) { - iconId = m_sqliteDb->lastInsertRowid(); - } - } else { - m_sqliteDb->executeEx(sqlUpdateIconRefCount, - QVariantList() << iconId << +1, - 0, &ok); - } - } - - // Save version info - if (ok) { - const QVariantList vars = QVariantList() - << appPath - << appInfo.fileDescription - << appInfo.companyName - << appInfo.productName - << appInfo.productVersion - << iconId - ; - - m_sqliteDb->executeEx(sqlInsertAppInfo, vars, 0, &ok); - } - - m_sqliteDb->endTransaction(ok); - - // Delete excess info - if (ok) { - const int appMaxCount = 2000; - const int appCount = m_sqliteDb->executeEx(sqlSelectAppCount).toInt(); - const int excessCount = appCount - appMaxCount; - - if (excessCount > 0) { - shrinkDb(excessCount); - } - } - - return ok; -} - -void AppInfoWorker::shrinkDb(int excessCount) -{ - QStringList appPaths; - QHash iconIds; - - bool ok = false; - - m_sqliteDb->beginTransaction(); - - // Get old app info list - { - SqliteStmt stmt; - if (stmt.prepare(m_sqliteDb->db(), sqlSelectAppOlds, - SqliteStmt::PreparePersistent) - && stmt.bindInt(1, excessCount)) { - - while (stmt.step() == SqliteStmt::StepRow) { - const QString appPath = stmt.columnText(0); - appPaths.append(appPath); - - const qint64 iconId = stmt.columnInt64(1); - const int iconCount = iconIds.value(iconId); - iconIds.insert(iconId, iconCount + 1); - } - - ok = true; - } - } - - // Delete old icons - auto iconIt = iconIds.constBegin(); - while (iconIt != iconIds.constEnd()) { - const qint64 iconId = iconIt.key(); - const int count = iconIt.value(); - - m_sqliteDb->executeEx(sqlUpdateIconRefCount, - QVariantList() << iconId << -count, - 0, &ok); - if (!ok) goto end; - - m_sqliteDb->executeEx(sqlDeleteIconIfNotUsed, - QVariantList() << iconId, - 0, &ok); - if (!ok) goto end; - } - - // Delete old app infos - for (const QString &path : appPaths) { - m_sqliteDb->executeEx(sqlDeleteApp, QVariantList() << path, - 0, &ok); - if (!ok) goto end; - } - - end: - m_sqliteDb->endTransaction(ok); + WorkerObject::doJob(workerJob); } diff --git a/src/ui/util/app/appinfoworker.h b/src/ui/util/app/appinfoworker.h index e37d1f8d..43cc2e1d 100644 --- a/src/ui/util/app/appinfoworker.h +++ b/src/ui/util/app/appinfoworker.h @@ -5,28 +5,16 @@ QT_FORWARD_DECLARE_CLASS(AppInfo) QT_FORWARD_DECLARE_CLASS(AppInfoManager) -QT_FORWARD_DECLARE_CLASS(SqliteDb) class AppInfoWorker : public WorkerObject { public: explicit AppInfoWorker(AppInfoManager *manager); - ~AppInfoWorker() override; + + AppInfoManager *manager() const; protected: - void doJob(const QString &appPath) override; - -private: - void setupDb(); - - bool loadFromFs(const QString &appPath, AppInfo &appInfo); - - bool loadFromDb(const QString &appPath, AppInfo &appInfo); - bool saveToDb(const QString &appPath, const AppInfo &appInfo); - void shrinkDb(int excessCount); - -private: - SqliteDb *m_sqliteDb; + void doJob(WorkerJob *workerJob) override; }; #endif // APPINFOWORKER_H diff --git a/src/ui/util/app/apputil.cpp b/src/ui/util/app/apputil.cpp index 9189cd4a..af420637 100644 --- a/src/ui/util/app/apputil.cpp +++ b/src/ui/util/app/apputil.cpp @@ -1,6 +1,7 @@ #include "apputil.h" #include +#include #include #include @@ -45,7 +46,7 @@ QPixmap extractShellIcon(const QString &appPath) const HRESULT hr = SHGetFileInfoW(appPathW, 0, &info, sizeof(SHFILEINFOW), flags); if (SUCCEEDED(hr)) { - pixmap = pixmapFromImageList(SHIL_JUMBO, info); + pixmap = pixmapFromImageList(SHIL_EXTRALARGE, info); } return pixmap; @@ -124,9 +125,11 @@ bool extractVersionInfo(const QString &appPath, AppInfo &appInfo) bool AppUtil::getInfo(const QString &appPath, AppInfo &appInfo) { - if (extractVersionInfo(appPath, appInfo)) { - appInfo.icon = extractShellIcon(appPath); - return true; - } - return false; + return extractVersionInfo(appPath, appInfo); +} + +QImage AppUtil::getIcon(const QString &appPath) +{ + return extractShellIcon(appPath) + .toImage(); } diff --git a/src/ui/util/app/apputil.h b/src/ui/util/app/apputil.h index d1c17cbc..63769de2 100644 --- a/src/ui/util/app/apputil.h +++ b/src/ui/util/app/apputil.h @@ -2,6 +2,7 @@ #define APPUTIL_H #include +#include QT_FORWARD_DECLARE_CLASS(AppInfo) @@ -9,6 +10,7 @@ class AppUtil { public: static bool getInfo(const QString &appPath, AppInfo &appInfo); + static QImage getIcon(const QString &appPath); }; #endif // APPUTIL_H diff --git a/src/ui/util/app/migrations/1.sql b/src/ui/util/app/migrations/1.sql index 745578a3..e4e9001f 100644 --- a/src/ui/util/app/migrations/1.sql +++ b/src/ui/util/app/migrations/1.sql @@ -1,22 +1,22 @@ PRAGMA user_version = 1; -PRAGMA journal_mode=WAL; +PRAGMA journal_mode = WAL; -CREATE TABLE IF NOT EXISTS app( +CREATE TABLE app( path TEXT PRIMARY KEY, file_descr TEXT, company_name TEXT, product_name TEXT, product_ver TEXT, icon_id INTEGER, - access_time DATETIME DEFAULT datetime('now') + access_time DATETIME ) WITHOUT ROWID; CREATE INDEX idx_app_access_time ON app(access_time); -CREATE TABLE IF NOT EXISTS icon( +CREATE TABLE icon( icon_id INTEGER PRIMARY KEY, - ref_count INTEGER NOT NULL DEFAULT 1, + ref_count INTEGER NOT NULL, hash INTEGER NOT NULL, image BLOB NOT NULL ); diff --git a/src/ui/util/logger.cpp b/src/ui/util/logger.cpp index d56357cc..5d29cbdf 100644 --- a/src/ui/util/logger.cpp +++ b/src/ui/util/logger.cpp @@ -70,15 +70,16 @@ void Logger::setPath(const QString &path) bool Logger::openLogFile() { - const QString filename = QLatin1String(LOGGER_FILE_PREFIX) + const QString fileName = QLatin1String(LOGGER_FILE_PREFIX) + DateUtil::now().toString("yyyy-MM-dd_HH-mm-ss_zzz") + QLatin1String(LOGGER_FILE_SUFFIX); - m_file.setFileName(m_dir.filePath(filename)); + m_file.setFileName(m_dir.filePath(fileName)); if (!m_file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { - qWarning() << "Cannot open log file: " << m_file.errorString(); + qWarning() << "Cannot open log file: " << m_file.fileName() + << m_file.errorString(); return false; } diff --git a/src/ui/util/net/hostinfojob.cpp b/src/ui/util/net/hostinfojob.cpp new file mode 100644 index 00000000..8ae55c96 --- /dev/null +++ b/src/ui/util/net/hostinfojob.cpp @@ -0,0 +1,13 @@ +#include "hostinfojob.h" + +#include "netutil.h" + +HostInfoJob::HostInfoJob(const QString &address) : + WorkerJob(address) +{ +} + +void HostInfoJob::doJob() +{ + hostName = NetUtil::getHostName(address()); +} diff --git a/src/ui/util/net/hostinfojob.h b/src/ui/util/net/hostinfojob.h new file mode 100644 index 00000000..7232935d --- /dev/null +++ b/src/ui/util/net/hostinfojob.h @@ -0,0 +1,19 @@ +#ifndef HOSTINFOJOB_H +#define HOSTINFOJOB_H + +#include "../worker/workerjob.h" + +class HostInfoJob : public WorkerJob +{ +public: + explicit HostInfoJob(const QString &address); + + QString address() const { return text; } + + void doJob() override; + +public: + QString hostName; +}; + +#endif // HOSTINFOJOB_H diff --git a/src/ui/util/net/hostinfomanager.cpp b/src/ui/util/net/hostinfomanager.cpp index 76f815cf..5abe8237 100644 --- a/src/ui/util/net/hostinfomanager.cpp +++ b/src/ui/util/net/hostinfomanager.cpp @@ -1,6 +1,6 @@ #include "hostinfomanager.h" -#include "hostinfoworker.h" +#include "hostinfojob.h" HostInfoManager::HostInfoManager(QObject *parent) : WorkerManager(parent) @@ -10,18 +10,18 @@ HostInfoManager::HostInfoManager(QObject *parent) : QSysInfo::machineHostName(); // Initialize ws2_32.dll } -WorkerObject *HostInfoManager::createWorker() -{ - return new HostInfoWorker(this); -} - void HostInfoManager::lookupHost(const QString &address) { - enqueueJob(address); + enqueueJob(new HostInfoJob(address)); } -void HostInfoManager::handleWorkerResult(const QString &address, - const QVariant &hostName) +void HostInfoManager::handleWorkerResult(WorkerJob *workerJob) { - emit lookupFinished(address, hostName.toString()); + if (!aborted()) { + auto job = static_cast(workerJob); + + emit lookupFinished(job->address(), job->hostName); + } + + delete workerJob; } diff --git a/src/ui/util/net/hostinfomanager.h b/src/ui/util/net/hostinfomanager.h index d241debe..d7cbae50 100644 --- a/src/ui/util/net/hostinfomanager.h +++ b/src/ui/util/net/hostinfomanager.h @@ -16,11 +16,7 @@ signals: public slots: void lookupHost(const QString &address); - void handleWorkerResult(const QString &address, - const QVariant &hostName) override; - -protected: - WorkerObject *createWorker() override; + void handleWorkerResult(WorkerJob *workerJob) override; }; #endif // HOSTINFOMANAGER_H diff --git a/src/ui/util/net/hostinfoworker.cpp b/src/ui/util/net/hostinfoworker.cpp deleted file mode 100644 index 950ec718..00000000 --- a/src/ui/util/net/hostinfoworker.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "hostinfoworker.h" - -#include "hostinfomanager.h" -#include "netutil.h" - -HostInfoWorker::HostInfoWorker(HostInfoManager *manager) : - WorkerObject(manager) -{ -} - -void HostInfoWorker::doJob(const QString &address) -{ - const QString hostName = NetUtil::getHostName(address); - - if (aborted()) return; - - manager()->handleWorkerResult(address, hostName); -} diff --git a/src/ui/util/net/hostinfoworker.h b/src/ui/util/net/hostinfoworker.h deleted file mode 100644 index a5286d31..00000000 --- a/src/ui/util/net/hostinfoworker.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef HOSTINFOWORKER_H -#define HOSTINFOWORKER_H - -#include "../worker/workerobject.h" - -QT_FORWARD_DECLARE_CLASS(HostInfoManager) - -class HostInfoWorker : public WorkerObject -{ -public: - explicit HostInfoWorker(HostInfoManager *manager); - -protected: - void doJob(const QString &address) override; -}; - -#endif // HOSTINFOWORKER_H diff --git a/src/ui/util/worker/workerjob.cpp b/src/ui/util/worker/workerjob.cpp new file mode 100644 index 00000000..dc0d1bfa --- /dev/null +++ b/src/ui/util/worker/workerjob.cpp @@ -0,0 +1,6 @@ +#include "workerjob.h" + +WorkerJob::WorkerJob(const QString &_text) : + text(_text) +{ +} diff --git a/src/ui/util/worker/workerjob.h b/src/ui/util/worker/workerjob.h new file mode 100644 index 00000000..74d71f40 --- /dev/null +++ b/src/ui/util/worker/workerjob.h @@ -0,0 +1,18 @@ +#ifndef WORKERJOB_H +#define WORKERJOB_H + +#include + +class WorkerJob +{ +public: + explicit WorkerJob(const QString &_text); + virtual ~WorkerJob() {} + + virtual void doJob() {} + +public: + QString text; +}; + +#endif // WORKERJOB_H diff --git a/src/ui/util/worker/workermanager.cpp b/src/ui/util/worker/workermanager.cpp index 4c4c5dbf..580e52c9 100644 --- a/src/ui/util/worker/workermanager.cpp +++ b/src/ui/util/worker/workermanager.cpp @@ -26,7 +26,7 @@ void WorkerManager::setupWorker() if (workersCount != 0 && (workersCount >= maxWorkersCount() - || m_queue.isEmpty())) + || m_jobQueue.isEmpty())) return; WorkerObject *worker = createWorker(); // autoDelete = true @@ -46,11 +46,16 @@ void WorkerManager::workerFinished(WorkerObject *worker) } } +WorkerObject *WorkerManager::createWorker() +{ + return new WorkerObject(this); +} + void WorkerManager::clear() { QMutexLocker locker(&m_mutex); - m_queue.clear(); + m_jobQueue.clear(); } void WorkerManager::abort() @@ -59,42 +64,35 @@ void WorkerManager::abort() m_aborted = true; - if (!m_workers.isEmpty()) { - for (WorkerObject *worker : m_workers) { - worker->abort(); - } + m_waitCondition.wakeAll(); - m_waitCondition.wakeAll(); - - do { - m_waitCondition.wait(&m_mutex); - } while (!m_workers.isEmpty()); + while (!m_workers.isEmpty()) { + m_waitCondition.wait(&m_mutex); } } -void WorkerManager::enqueueJob(const QString &job) +void WorkerManager::enqueueJob(WorkerJob *job) { QMutexLocker locker(&m_mutex); setupWorker(); - m_queue.enqueue(job); + m_jobQueue.enqueue(job); m_waitCondition.wakeOne(); } -bool WorkerManager::dequeueJob(QString &job) +WorkerJob *WorkerManager::dequeueJob() { QMutexLocker locker(&m_mutex); - while (!m_aborted && m_queue.isEmpty()) { + while (!m_aborted && m_jobQueue.isEmpty()) { if (!m_waitCondition.wait(&m_mutex, WORKER_TIMEOUT_MSEC)) break; // timed out } - if (m_aborted || m_queue.isEmpty()) - return false; + if (m_aborted || m_jobQueue.isEmpty()) + return nullptr; - job = m_queue.dequeue(); - return true; + return m_jobQueue.dequeue(); } diff --git a/src/ui/util/worker/workermanager.h b/src/ui/util/worker/workermanager.h index d4b7950b..183b53b3 100644 --- a/src/ui/util/worker/workermanager.h +++ b/src/ui/util/worker/workermanager.h @@ -8,6 +8,7 @@ #include #include +QT_FORWARD_DECLARE_CLASS(WorkerJob) QT_FORWARD_DECLARE_CLASS(WorkerObject) class WorkerManager : public QObject @@ -26,22 +27,22 @@ signals: public slots: void clear(); - void enqueueJob(const QString &job); - bool dequeueJob(QString &job); + void enqueueJob(WorkerJob *job); + WorkerJob *dequeueJob(); void workerFinished(WorkerObject *worker); - virtual void handleWorkerResult(const QString &job, - const QVariant &result) = 0; + virtual void handleWorkerResult(WorkerJob *job) = 0; protected: - virtual WorkerObject *createWorker() = 0; + virtual WorkerObject *createWorker(); + + bool aborted() const { return m_aborted; } + void abort(); private: void setupWorker(); - void abort(); - private: volatile bool m_aborted; @@ -49,7 +50,7 @@ private: QList m_workers; - QQueue m_queue; + QQueue m_jobQueue; QMutex m_mutex; QWaitCondition m_waitCondition; diff --git a/src/ui/util/worker/workerobject.cpp b/src/ui/util/worker/workerobject.cpp index 60a659e1..c29547ca 100644 --- a/src/ui/util/worker/workerobject.cpp +++ b/src/ui/util/worker/workerobject.cpp @@ -1,19 +1,18 @@ #include "workerobject.h" +#include "workerjob.h" #include "workermanager.h" WorkerObject::WorkerObject(WorkerManager *manager) : - m_aborted(false), m_manager(manager) { } void WorkerObject::run() { - while (!aborted()) { - QString job; - - if (!manager()->dequeueJob(job)) + for (; ; ) { + WorkerJob *job = manager()->dequeueJob(); + if (job == nullptr) break; doJob(job); @@ -21,3 +20,10 @@ void WorkerObject::run() manager()->workerFinished(this); } + +void WorkerObject::doJob(WorkerJob *job) +{ + job->doJob(); + + manager()->handleWorkerResult(job); +} diff --git a/src/ui/util/worker/workerobject.h b/src/ui/util/worker/workerobject.h index a26ad01a..30bc3537 100644 --- a/src/ui/util/worker/workerobject.h +++ b/src/ui/util/worker/workerobject.h @@ -4,6 +4,7 @@ #include #include +QT_FORWARD_DECLARE_CLASS(WorkerJob) QT_FORWARD_DECLARE_CLASS(WorkerManager) class WorkerObject : public QRunnable @@ -13,17 +14,12 @@ public: WorkerManager *manager() const { return m_manager; } - bool aborted() const { return m_aborted; } - virtual void abort() { m_aborted = true; } - void run() override; protected: - virtual void doJob(const QString &job) = 0; + virtual void doJob(WorkerJob *job); private: - volatile bool m_aborted; - WorkerManager *m_manager; };