diff options
Diffstat (limited to 'plugin')
| -rw-r--r-- | plugin/src/Caelestia/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | plugin/src/Caelestia/appdb.cpp | 201 | ||||
| -rw-r--r-- | plugin/src/Caelestia/appdb.hpp | 93 |
3 files changed, 297 insertions, 1 deletions
diff --git a/plugin/src/Caelestia/CMakeLists.txt b/plugin/src/Caelestia/CMakeLists.txt index 1a96bf4..c13417f 100644 --- a/plugin/src/Caelestia/CMakeLists.txt +++ b/plugin/src/Caelestia/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(Qt6 REQUIRED COMPONENTS Core Qml Gui Concurrent) +find_package(Qt6 REQUIRED COMPONENTS Core Qml Gui Concurrent Sql) find_package(PkgConfig REQUIRED) pkg_check_modules(Qalculate IMPORTED_TARGET libqalculate REQUIRED) pkg_check_modules(Pipewire IMPORTED_TARGET libpipewire-0.3 REQUIRED) @@ -21,6 +21,7 @@ qt_add_qml_module(caelestia audiocollector.hpp audiocollector.cpp audioprovider.hpp audioprovider.cpp cavaprovider.hpp cavaprovider.cpp + appdb.hpp appdb.cpp ) qt_query_qml_module(caelestia @@ -45,6 +46,7 @@ target_link_libraries(caelestia PRIVATE Qt::Qml Qt::Gui Qt::Concurrent + Qt::Sql PkgConfig::Qalculate PkgConfig::Pipewire PkgConfig::Aubio diff --git a/plugin/src/Caelestia/appdb.cpp b/plugin/src/Caelestia/appdb.cpp new file mode 100644 index 0000000..20e37bb --- /dev/null +++ b/plugin/src/Caelestia/appdb.cpp @@ -0,0 +1,201 @@ +#include "appdb.hpp" + +#include <qsqldatabase.h> +#include <qsqlquery.h> +#include <quuid.h> + +namespace caelestia { + +AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent) + : QObject(parent) + , m_entry(entry) + , m_frequency(frequency) {} + +QObject* AppEntry::entry() const { + return m_entry; +} + +quint32 AppEntry::frequency() const { + return m_frequency; +} + +void AppEntry::setFrequency(unsigned int frequency) { + if (m_frequency != frequency) { + m_frequency = frequency; + emit frequencyChanged(); + } +} + +void AppEntry::incrementFrequency() { + m_frequency++; + emit frequencyChanged(); +} + +QString AppEntry::id() const { + return m_entry->property("id").toString(); +} + +QString AppEntry::name() const { + return m_entry->property("name").toString(); +} + +QString AppEntry::desc() const { + return m_entry->property("comment").toString(); +} + +QString AppEntry::execString() const { + return m_entry->property("execString").toString(); +} + +QString AppEntry::wmClass() const { + return m_entry->property("startupClass").toString(); +} + +QString AppEntry::genericName() const { + return m_entry->property("genericName").toString(); +} + +QString AppEntry::categories() const { + return m_entry->property("categories").toStringList().join(" "); +} + +QString AppEntry::keywords() const { + return m_entry->property("keywords").toStringList().join(" "); +} + +AppDb::AppDb(QObject* parent) + : QObject(parent) + , m_uuid(QUuid::createUuid().toString()) { + auto db = QSqlDatabase::addDatabase("QSQLITE", m_uuid); + db.setDatabaseName(":memory:"); + db.open(); + + QSqlQuery query(db); + query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)"); +} + +QString AppDb::uuid() const { + return m_uuid; +} + +QString AppDb::path() const { + return m_path; +} + +void AppDb::setPath(const QString& path) { + if (m_path == path) { + return; + } + + m_path = path; + emit pathChanged(); + + auto db = QSqlDatabase::database(m_uuid, false); + db.close(); + db.setDatabaseName(path); + + updateAppFrequencies(); +} + +QList<QObject*> AppDb::entries() const { + return m_entries; +} + +void AppDb::setEntries(const QList<QObject*>& entries) { + if (m_entries == entries) { + return; + } + + m_entries = entries; + emit entriesChanged(); + + updateApps(); +} + +QList<AppEntry*> AppDb::apps() const { + auto apps = m_apps.values(); + std::sort(apps.begin(), apps.end(), [](AppEntry* a, AppEntry* b) { + if (a->frequency() != b->frequency()) { + return a->frequency() > b->frequency(); + } + return a->name().localeAwareCompare(b->name()) < 0; + }); + return apps; +} + +void AppDb::incrementFrequency(const QString& id) { + auto db = QSqlDatabase::database(m_uuid); + QSqlQuery query(db); + + query.prepare("INSERT INTO frequencies (id, frequency) " + "VALUES (:id, 1) " + "ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1"); + query.bindValue(":id", id); + query.exec(); + + for (auto app : m_apps) { + if (app->id() == id) { + const auto before = apps(); + + app->incrementFrequency(); + + if (before != apps()) { + emit appsChanged(); + } + + return; + } + } + + qWarning() << "AppDb::incrementFrequency: could not find app with id" << id; +} + +quint32 AppDb::getFrequency(const QString& id) const { + auto db = QSqlDatabase::database(m_uuid); + QSqlQuery query(db); + + query.prepare("SELECT frequency FROM frequencies WHERE id = :id"); + query.bindValue(":id", id); + + if (query.exec() && query.next()) { + return query.value(0).toUInt(); + } + + return 0; +} + +void AppDb::updateAppFrequencies() { + for (auto app : m_apps) { + app->setFrequency(getFrequency(app->id())); + } +} + +void AppDb::updateApps() { + bool dirty = false; + + for (auto entry : m_entries) { + const auto id = entry->property("id").toString(); + if (!m_apps.contains(id)) { + dirty = true; + m_apps.insert(id, new AppEntry(entry, getFrequency(id), this)); + } + } + + QSet<QString> newIds; + for (auto entry : m_entries) { + newIds.insert(entry->property("id").toString()); + } + + for (auto id : m_apps.keys()) { + if (!newIds.contains(id)) { + dirty = true; + delete m_apps.take(id); + } + } + + if (dirty) { + emit appsChanged(); + } +} + +} // namespace caelestia diff --git a/plugin/src/Caelestia/appdb.hpp b/plugin/src/Caelestia/appdb.hpp new file mode 100644 index 0000000..dfedcc6 --- /dev/null +++ b/plugin/src/Caelestia/appdb.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include <qhash.h> +#include <qobject.h> +#include <qqmlintegration.h> + +namespace caelestia { + +class AppEntry : public QObject { + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("AppEntry instances can only be retrieved from an AppDb") + + // The actual DesktopEntry, but we don't have access to the type so it's a QObject + Q_PROPERTY(QObject* entry READ entry CONSTANT) + + Q_PROPERTY(quint32 frequency READ frequency NOTIFY frequencyChanged) + Q_PROPERTY(QString id READ id CONSTANT) + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QString desc READ desc CONSTANT) + Q_PROPERTY(QString execString READ execString CONSTANT) + Q_PROPERTY(QString wmClass READ wmClass CONSTANT) + Q_PROPERTY(QString genericName READ genericName CONSTANT) + Q_PROPERTY(QString categories READ categories CONSTANT) + Q_PROPERTY(QString keywords READ keywords CONSTANT) + +public: + explicit AppEntry(QObject* entry, quint32 frequency, QObject* parent = nullptr); + + [[nodiscard]] QObject* entry() const; + + [[nodiscard]] quint32 frequency() const; + void setFrequency(quint32 frequency); + void incrementFrequency(); + + [[nodiscard]] QString id() const; + [[nodiscard]] QString name() const; + [[nodiscard]] QString desc() const; + [[nodiscard]] QString execString() const; + [[nodiscard]] QString wmClass() const; + [[nodiscard]] QString genericName() const; + [[nodiscard]] QString categories() const; + [[nodiscard]] QString keywords() const; + +signals: + void frequencyChanged(); + +private: + QObject* m_entry; + quint32 m_frequency; +}; + +class AppDb : public QObject { + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QString uuid READ uuid CONSTANT) + Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED) + Q_PROPERTY(QList<QObject*> entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED) + Q_PROPERTY(QList<AppEntry*> apps READ apps NOTIFY appsChanged) + +public: + explicit AppDb(QObject* parent = nullptr); + + [[nodiscard]] QString uuid() const; + + [[nodiscard]] QString path() const; + void setPath(const QString& path); + + [[nodiscard]] QList<QObject*> entries() const; + void setEntries(const QList<QObject*>& entries); + + [[nodiscard]] QList<AppEntry*> apps() const; + + Q_INVOKABLE void incrementFrequency(const QString& id); + +signals: + void pathChanged(); + void entriesChanged(); + void appsChanged(); + +private: + const QString m_uuid; + QString m_path; + QList<QObject*> m_entries; + QHash<QString, AppEntry*> m_apps; + + quint32 getFrequency(const QString& id) const; + void updateAppFrequencies(); + void updateApps(); +}; + +} // namespace caelestia |