diff --git a/CMakeLists.txt b/CMakeLists.txt index 367289f..f732dfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set(SRCS src/iconthemeimageprovider.cpp src/launcher.cpp src/launchermodel.cpp - src/launcheritem.cpp + src/appitem.cpp src/main.cpp src/pagemodel.cpp src/ucunits.cpp diff --git a/src/launcheritem.cpp b/src/appitem.cpp similarity index 53% rename from src/launcheritem.cpp rename to src/appitem.cpp index 92c6fee..e5234ee 100644 --- a/src/launcheritem.cpp +++ b/src/appitem.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 CutefishOS. * - * Author: revenmartin + * Author: Reion Wong * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,33 +17,41 @@ * along with this program. If not, see . */ -#include "launcheritem.h" +#include "appitem.h" -LauncherItem::LauncherItem() +AppItem::AppItem() { } -LauncherItem::LauncherItem(const LauncherItem &item) +AppItem::AppItem(const AppItem &info) + : id(info.id) + , name(info.name) + , genericName(info.genericName) + , comment(info.comment) + , iconName(info.iconName) + , args(info.args) { } -LauncherItem::~LauncherItem() +AppItem::~AppItem() { } -QDataStream &operator<<(QDataStream &argument, const LauncherItem &item) +QDataStream &operator<<(QDataStream &argument, const AppItem &info) { - argument << item.id << item.name; + argument << info.id << info.name << info.genericName; + argument << info.comment << info.iconName; return argument; } -const QDataStream &operator>>(QDataStream &argument, LauncherItem &item) +const QDataStream &operator>>(QDataStream &argument, AppItem &info) { - argument >> item.id << item.name; + argument >> info.id >> info.name >> info.genericName; + argument >> info.comment >> info.iconName; return argument; } diff --git a/src/launcheritem.h b/src/appitem.h similarity index 66% rename from src/launcheritem.h rename to src/appitem.h index 227a5e9..255041c 100644 --- a/src/launcheritem.h +++ b/src/appitem.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 CutefishOS. * - * Author: revenmartin + * Author: Reion Wong * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,26 +17,24 @@ * along with this program. If not, see . */ -#ifndef LAUNCHERITEM_H -#define LAUNCHERITEM_H +#ifndef APPITEM_H +#define APPITEM_H -#include -#include +#include +#include +#include -class LauncherItem : public QObject +class AppItem { - Q_OBJECT - public: - LauncherItem(); - LauncherItem(const LauncherItem &item); - ~LauncherItem(); + AppItem(); + AppItem(const AppItem &info); + ~AppItem(); - inline bool operator==(const LauncherItem &other) const { return id == other.id; } - friend QDataStream &operator<<(QDataStream &argument, const LauncherItem &item); - friend const QDataStream &operator>>(QDataStream &argument, LauncherItem &item); + inline bool operator==(const AppItem &other) const { return id == other.id; } + friend QDataStream &operator<<(QDataStream &argument, const AppItem &info); + friend const QDataStream &operator>>(QDataStream &argument, AppItem &info); -public: QString id; QString name; QString genericName; @@ -45,6 +43,6 @@ public: QStringList args; }; -Q_DECLARE_METATYPE(LauncherItem) +Q_DECLARE_METATYPE(AppItem) -#endif // LAUNCHERITEM_H +#endif // APPITEM_H diff --git a/src/launchermodel.cpp b/src/launchermodel.cpp index 2d700a6..24951fb 100755 --- a/src/launchermodel.cpp +++ b/src/launchermodel.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 CutefishOS. * - * Author: revenmartin + * Author: Reion Wong * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,7 +47,16 @@ LauncherModel::LauncherModel(QObject *parent) : QAbstractListModel(parent) , m_settings("cutefishos", "launcher-applist", this) , m_mode(NormalMode) + , m_needSort(false) { + // Init datas. + QByteArray listByteArray = m_settings.value("list").toByteArray(); + QDataStream in(&listByteArray, QIODevice::ReadOnly); + in >> m_appItems; + + if (m_appItems.isEmpty()) + m_needSort = true; + QtConcurrent::run(LauncherModel::refresh, this); QFileSystemWatcher *watcher = new QFileSystemWatcher(this); @@ -56,24 +65,18 @@ LauncherModel::LauncherModel(QObject *parent) QtConcurrent::run(LauncherModel::refresh, this); }); + m_saveTimer.setInterval(1000); + connect(&m_saveTimer, &QTimer::timeout, this, &LauncherModel::save); + connect(this, &QAbstractItemModel::rowsInserted, this, &LauncherModel::countChanged); connect(this, &QAbstractItemModel::rowsRemoved, this, &LauncherModel::countChanged); connect(this, &QAbstractItemModel::modelReset, this, &LauncherModel::countChanged); connect(this, &QAbstractItemModel::layoutChanged, this, &LauncherModel::countChanged); - - connect(this, &LauncherModel::refreshed, this, [=] { - beginResetModel(); - std::sort(m_items.begin(), m_items.end(), [=] (LauncherItem *a, LauncherItem *b) { - return a->name < b->name; - }); - endResetModel(); - }); + connect(this, &LauncherModel::refreshed, this, &LauncherModel::onRefreshed); } LauncherModel::~LauncherModel() { - while (!m_items.isEmpty()) - delete m_items.takeFirst(); } int LauncherModel::count() const @@ -88,7 +91,7 @@ int LauncherModel::rowCount(const QModelIndex &parent) const if (m_mode == SearchMode) return m_searchItems.size(); - return m_items.size(); + return m_appItems.size(); } QHash LauncherModel::roleNames() const @@ -112,21 +115,21 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const if (!index.isValid()) return QVariant(); - LauncherItem *item = m_mode == NormalMode - ? m_items.at(index.row()) : m_searchItems.at(index.row()); + AppItem appItem = m_mode == NormalMode ? m_appItems.at(index.row()) + : m_searchItems.at(index.row()); switch (role) { case AppIdRole: - return item->id; + return appItem.id; case NameRole: - return item->name; + return appItem.name; case IconNameRole: - return item->iconName; + return appItem.iconName; case FilterInfoRole: - return QString(item->name + QStringLiteral(" ") - + item->genericName + return QString(appItem.name + QStringLiteral(" ") + + appItem.genericName + QStringLiteral(" ") - + item->comment); + + appItem.comment); default: return QVariant(); } @@ -137,12 +140,12 @@ void LauncherModel::search(const QString &key) m_mode = key.isEmpty() ? NormalMode : SearchMode; m_searchItems.clear(); - for (LauncherItem *item : qAsConst(m_items)) { - const QString &name = item->name; - const QString &fileName = item->id; + for (const AppItem &item : qAsConst(m_appItems)) { + const QString &name = item.name; + const QString &fileName = item.id; if (name.contains(key, Qt::CaseInsensitive) || - fileName.contains(key, Qt::CaseInsensitive)) { + fileName.contains(key, Qt::CaseInsensitive)) { m_searchItems.append(item); continue; } @@ -181,8 +184,8 @@ void LauncherModel::removeFromDock(const QString &desktop) int LauncherModel::findById(const QString &id) { - for (int i = 0; i < m_items.size(); ++i) { - if (m_items.at(i)->id == id) + for (int i = 0; i < m_appItems.size(); ++i) { + if (m_appItems.at(i).id == id) return i; } @@ -192,8 +195,8 @@ int LauncherModel::findById(const QString &id) void LauncherModel::refresh(LauncherModel *manager) { QStringList addedEntries; - for (LauncherItem *item : qAsConst(manager->m_items)) - addedEntries.append(item->id); + for (const AppItem &item : qAsConst(manager->m_appItems)) + addedEntries.append(item.id); QStringList allEntries; QDirIterator it("/usr/share/applications", { "*.desktop" }, QDir::NoFilter, QDirIterator::Subdirectories); @@ -207,14 +210,13 @@ void LauncherModel::refresh(LauncherModel *manager) } for (const QString &fileName : allEntries) { - if (!addedEntries.contains(fileName)) + //if (!addedEntries.contains(fileName)) QMetaObject::invokeMethod(manager, "addApp", Q_ARG(QString, fileName)); } - for (LauncherItem *item : qAsConst(manager->m_items)) { - if (!allEntries.contains(item->id)) - QMetaObject::invokeMethod(manager, "removeApp", Q_ARG(LauncherItem *, item)); - } + for (const AppItem &item : qAsConst(manager->m_appItems)) + if (!allEntries.contains(item.id)) + QMetaObject::invokeMethod(manager, "removeApp", Q_ARG(QString, item.id)); // Signal the model was refreshed QMetaObject::invokeMethod(manager, "refreshed"); @@ -228,7 +230,7 @@ void LauncherModel::move(int from, int to, int page, int pageCount) int newFrom = from + (page * pageCount); int newTo = to + (page * pageCount); - m_items.move(newFrom, newTo); + m_appItems.move(newFrom, newTo); // if (from < to) // beginMoveRows(QModelIndex(), from, from, QModelIndex(), to + 1); @@ -236,23 +238,34 @@ void LauncherModel::move(int from, int to, int page, int pageCount) // beginMoveRows(QModelIndex(), from, from, QModelIndex(), to); // endMoveRows(); + + delaySave(); } void LauncherModel::save() { + m_settings.clear(); QByteArray datas; QDataStream out(&datas, QIODevice::WriteOnly); - out << m_items; + out << m_appItems; m_settings.setValue("list", datas); } +void LauncherModel::delaySave() +{ + if (m_saveTimer.isActive()) + m_saveTimer.stop(); + + m_saveTimer.start(); +} + bool LauncherModel::launch(const QString &path) { int index = findById(path); if (index != -1) { - LauncherItem *item = m_items.at(index); - QStringList args = item->args; + const AppItem &item = m_appItems.at(index); + QStringList args = item.args; QScopedPointer p(new QProcess); p->setStandardInputFile(QProcess::nullDevice()); p->setProcessChannelMode(QProcess::ForwardedChannels); @@ -275,11 +288,26 @@ bool LauncherModel::launch(const QString &path) return false; } -void LauncherModel::addApp(const QString &fileName) +void LauncherModel::onRefreshed() { - if (findById(fileName) != -1) + if (!m_needSort) return; + m_needSort = false; + + beginResetModel(); + std::sort(m_appItems.begin(), m_appItems.end(), [=] (AppItem &a, AppItem &b) { + return a.name < b.name; + }); + endResetModel(); + + delaySave(); +} + +void LauncherModel::addApp(const QString &fileName) +{ + int index = findById(fileName) ; + DesktopProperties desktop(fileName, "Desktop Entry"); if (desktop.contains("Terminal") && desktop.value("Terminal").toBool()) @@ -308,32 +336,43 @@ void LauncherModel::addApp(const QString &fileName) appExec = appExec.replace("\"", ""); appExec = appExec.simplified(); - LauncherItem *item = new LauncherItem; - item->id = fileName; - item->name = appName; - item->genericName = desktop.value("Comment").toString(); - item->comment = desktop.value("Comment").toString(); - item->iconName = desktop.value("Icon").toString(); - item->args = appExec.split(" "); - - beginInsertRows(QModelIndex(), m_items.count(), m_items.count()); - m_items.append(item); - qDebug() << "added: " << item->name; - // Q_EMIT applicationAdded(item); - endInsertRows(); + // 存在需要更新信息 + if (index >= 0 && index <= m_appItems.size()) { + AppItem &item = m_appItems[index]; + item.name = appName; + item.genericName = desktop.value("Comment").toString(); + item.comment = desktop.value("Comment").toString(); + item.iconName = desktop.value("Icon").toString(); + item.args = appExec.split(" "); + emit dataChanged(LauncherModel::index(index), LauncherModel::index(index)); + } else { + AppItem appItem; + appItem.id = fileName; + appItem.name = appName; + appItem.genericName = desktop.value("Comment").toString(); + appItem.comment = desktop.value("Comment").toString(); + appItem.iconName = desktop.value("Icon").toString(); + appItem.args = appExec.split(" "); + + beginInsertRows(QModelIndex(), m_appItems.count(), m_appItems.count()); + m_appItems.append(appItem); + qDebug() << "added: " << appItem.name; + endInsertRows(); + + if (!m_needSort) + delaySave(); + } } -void LauncherModel::removeApp(LauncherItem *item) +void LauncherModel::removeApp(const QString &fileName) { - int index = m_items.indexOf(item); + int index = findById(fileName); if (index < 0) return; beginRemoveRows(QModelIndex(), index, index); - qDebug() << "removed: " << item->name; - m_items.removeAt(index); + m_appItems.removeAt(index); endRemoveRows(); - Q_EMIT applicationRemoved(item); - item->deleteLater(); + delaySave(); } diff --git a/src/launchermodel.h b/src/launchermodel.h index 37cb901..5aad0ba 100755 --- a/src/launchermodel.h +++ b/src/launchermodel.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 CutefishOS. * - * Author: revenmartin + * Author: Reion Wong * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,8 +24,9 @@ #include #include #include +#include -#include "launcheritem.h" +#include "appitem.h" class LauncherModel : public QAbstractListModel { @@ -33,8 +34,7 @@ class LauncherModel : public QAbstractListModel Q_PROPERTY(int count READ count NOTIFY countChanged) public: - enum Roles - { + enum Roles { AppIdRole = Qt::UserRole + 1, ApplicationRole, NameRole, @@ -73,6 +73,8 @@ public: Q_INVOKABLE void move(int from, int to, int page, int pageCount); Q_INVOKABLE void save(); + void delaySave(); + public Q_SLOTS: Q_INVOKABLE bool launch(const QString &path); Q_INVOKABLE bool launch() { return launch(QString()); } @@ -80,19 +82,22 @@ public Q_SLOTS: Q_SIGNALS: void countChanged(); void refreshed(); - void applicationAdded(LauncherItem *app); - void applicationRemoved(LauncherItem *app); void applicationLaunched(); private Q_SLOTS: + void onRefreshed(); void addApp(const QString &fileName); - void removeApp(LauncherItem *item); + void removeApp(const QString &fileName); private: - QList m_items; - QList m_searchItems; + QList m_appItems; + QList m_searchItems; + + QTimer m_saveTimer; QSettings m_settings; Mode m_mode; + + bool m_needSort; }; #endif // LAUNCHERMODEL_H diff --git a/src/main.cpp b/src/main.cpp index 67e9f3f..932cbdc 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,7 +25,6 @@ #include #include "launcher.h" -#include "launcheritem.h" #include "launchermodel.h" #include "pagemodel.h" #include "iconitem.h" @@ -43,8 +42,6 @@ int main(int argc, char *argv[]) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QByteArray uri = "Cutefish.Launcher"; - qRegisterMetaType("LauncherItem"); - qmlRegisterType(uri, 1, 0, "LauncherItem"); qmlRegisterType(uri, 1, 0, "LauncherModel"); qmlRegisterType(uri, 1, 0, "PageModel"); qmlRegisterType(uri, 1, 0, "IconItem");