Save sorted list

pull/9/head
reionwong 4 years ago
parent 5abb8268ee
commit 046f912d5a

@ -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

@ -1,7 +1,7 @@
/*
* Copyright (C) 2021 CutefishOS.
*
* Author: revenmartin <revenmartin@gmail.com>
* Author: Reion Wong <reion@cutefishos.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
}

@ -1,7 +1,7 @@
/*
* Copyright (C) 2021 CutefishOS.
*
* Author: revenmartin <revenmartin@gmail.com>
* Author: Reion Wong <reion@cutefishos.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef LAUNCHERITEM_H
#define LAUNCHERITEM_H
#ifndef APPITEM_H
#define APPITEM_H
#include <QObject>
#include <QDataStream>
#include <QString>
#include <QStringList>
#include <QMetaType>
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

@ -1,7 +1,7 @@
/*
* Copyright (C) 2021 CutefishOS.
*
* Author: revenmartin <revenmartin@gmail.com>
* Author: Reion Wong <reion@cutefishos.com>
*
* 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<int, QByteArray> 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,9 +140,9 @@ 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)) {
@ -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<QProcess> 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);
// 存在需要更新信息
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();
}

@ -1,7 +1,7 @@
/*
* Copyright (C) 2021 CutefishOS.
*
* Author: revenmartin <revenmartin@gmail.com>
* Author: Reion Wong <reion@cutefishos.com>
*
* 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 <QLoggingCategory>
#include <QAbstractListModel>
#include <QSettings>
#include <QTimer>
#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<LauncherItem *> m_items;
QList<LauncherItem *> m_searchItems;
QList<AppItem> m_appItems;
QList<AppItem> m_searchItems;
QTimer m_saveTimer;
QSettings m_settings;
Mode m_mode;
bool m_needSort;
};
#endif // LAUNCHERMODEL_H

@ -25,7 +25,6 @@
#include <QCommandLineParser>
#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>("LauncherItem");
qmlRegisterType<LauncherItem>(uri, 1, 0, "LauncherItem");
qmlRegisterType<LauncherModel>(uri, 1, 0, "LauncherModel");
qmlRegisterType<PageModel>(uri, 1, 0, "PageModel");
qmlRegisterType<IconItem>(uri, 1, 0, "IconItem");

Loading…
Cancel
Save