diff --git a/applets/systemtray/CMakeLists.txt b/applets/systemtray/CMakeLists.txt --- a/applets/systemtray/CMakeLists.txt +++ b/applets/systemtray/CMakeLists.txt @@ -4,6 +4,7 @@ set(systemtray_SRCS systemtraymodel.cpp + sortedsystemtraymodel.cpp systemtray.cpp ) diff --git a/applets/systemtray/package/contents/ui/ConfigEntries.qml b/applets/systemtray/package/contents/ui/ConfigEntries.qml --- a/applets/systemtray/package/contents/ui/ConfigEntries.qml +++ b/applets/systemtray/package/contents/ui/ConfigEntries.qml @@ -75,19 +75,7 @@ property var visibilityColumnWidth: units.gridUnit property var keySequenceColumnWidth: units.gridUnit - model: PlasmaCore.SortFilterModel { - sourceModel: PlasmaCore.SortFilterModel { - sourceModel: plasmoid.nativeInterface.systemTrayModel - - sortRole: "display" - sortColumn: 0 - isSortLocaleAware: true - } - - sortRole: "category" - sortColumn: 0 - isSortLocaleAware: true - } + model: plasmoid.nativeInterface.configSystemTrayModel header: Kirigami.AbstractListItem { diff --git a/applets/systemtray/sortedsystemtraymodel.h b/applets/systemtray/sortedsystemtraymodel.h new file mode 100644 --- /dev/null +++ b/applets/systemtray/sortedsystemtraymodel.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2019 Konrad Materka * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef SORTEDSYSTEMTRAYMODEL_H +#define SORTEDSYSTEMTRAYMODEL_H + +#include + +class SortedSystemTrayModel : public QSortFilterProxyModel { + Q_OBJECT +public: + enum class SortingType { + ConfigurationPage + }; + + explicit SortedSystemTrayModel(SortingType sorting, QObject *parent = nullptr); + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; + +private: + bool lessThanConfigurationPage(const QModelIndex &left, const QModelIndex &right) const; + + int compareCategoriesAlphabetically(const QModelIndex &left, const QModelIndex &right) const; + + SortingType m_sorting; +}; + +#endif // SORTEDSYSTEMTRAYMODEL_H diff --git a/applets/systemtray/sortedsystemtraymodel.cpp b/applets/systemtray/sortedsystemtraymodel.cpp new file mode 100644 --- /dev/null +++ b/applets/systemtray/sortedsystemtraymodel.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2019 Konrad Materka * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "sortedsystemtraymodel.h" +#include "systemtraymodel.h" +#include "debug.h" + +#include + +SortedSystemTrayModel::SortedSystemTrayModel(SortingType sorting, QObject *parent) + : QSortFilterProxyModel(parent), + m_sorting(sorting) +{ + setSortLocaleAware(true); + sort(0); +} + +bool SortedSystemTrayModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + switch (m_sorting) { + case SortedSystemTrayModel::SortingType::ConfigurationPage: + return lessThanConfigurationPage(left, right); + } + + return QSortFilterProxyModel::lessThan(left, right); +} + +bool SortedSystemTrayModel::lessThanConfigurationPage(const QModelIndex &left, const QModelIndex &right) const +{ + const int categoriesComparison = compareCategoriesAlphabetically(left, right); + if (categoriesComparison == 0) { + return QSortFilterProxyModel::lessThan(left, right); + } else { + return categoriesComparison < 0; + } +} + +int SortedSystemTrayModel::compareCategoriesAlphabetically(const QModelIndex &left, const QModelIndex &right) const +{ + QVariant leftData = sourceModel()->data(left, static_cast(BaseModel::BaseRole::Category)); + QString leftCategory = leftData.isNull() ? QStringLiteral("UnknownCategory") : leftData.toString(); + + QVariant rightData = sourceModel()->data(right, static_cast(BaseModel::BaseRole::Category)); + QString rightCategory = rightData.isNull() ? QStringLiteral("UnknownCategory") : rightData.toString(); + + return QString::localeAwareCompare(leftCategory, rightCategory); +} diff --git a/applets/systemtray/systemtray.h b/applets/systemtray/systemtray.h --- a/applets/systemtray/systemtray.h +++ b/applets/systemtray/systemtray.h @@ -35,11 +35,12 @@ class PlasmoidModel; class StatusNotifierModel; class SystemTrayModel; +class SortedSystemTrayModel; class SystemTray : public Plasma::Containment { Q_OBJECT - Q_PROPERTY(QAbstractItemModel* systemTrayModel READ systemTrayModel CONSTANT) + Q_PROPERTY(QAbstractItemModel* configSystemTrayModel READ configSystemTrayModel CONSTANT) Q_PROPERTY(QAbstractItemModel* availablePlasmoids READ availablePlasmoids CONSTANT) Q_PROPERTY(QStringList allowedPlasmoids READ allowedPlasmoids WRITE setAllowedPlasmoids NOTIFY allowedPlasmoidsChanged) Q_PROPERTY(QStringList defaultPlasmoids READ defaultPlasmoids CONSTANT) @@ -53,7 +54,7 @@ void restoreContents(KConfigGroup &group) override; void restorePlasmoids(); - QAbstractItemModel* systemTrayModel(); + QAbstractItemModel *configSystemTrayModel(); QStringList defaultPlasmoids() const; @@ -130,6 +131,7 @@ PlasmoidModel *m_availablePlasmoidsModel; StatusNotifierModel *m_statusNotifierModel; SystemTrayModel *m_systemTrayModel; + SortedSystemTrayModel *m_configSystemTrayModel; QHash m_knownPlugins; QHash m_dbusServiceCounts; }; diff --git a/applets/systemtray/systemtray.cpp b/applets/systemtray/systemtray.cpp --- a/applets/systemtray/systemtray.cpp +++ b/applets/systemtray/systemtray.cpp @@ -19,6 +19,7 @@ #include "systemtray.h" #include "systemtraymodel.h" +#include "sortedsystemtraymodel.h" #include "debug.h" #include @@ -43,17 +44,19 @@ SystemTray::SystemTray(QObject *parent, const QVariantList &args) : Plasma::Containment(parent, args), m_availablePlasmoidsModel(nullptr), - m_systemTrayModel(new SystemTrayModel(this)) + m_systemTrayModel(new SystemTrayModel(this)), + m_configSystemTrayModel(nullptr) { setHasConfigurationInterface(true); setContainmentType(Plasma::Types::CustomEmbeddedContainment); PlasmoidModel *currentPlasmoidsModel = new PlasmoidModel(m_systemTrayModel); connect(this, &SystemTray::appletAdded, currentPlasmoidsModel, &PlasmoidModel::addApplet); connect(this, &SystemTray::appletRemoved, currentPlasmoidsModel, &PlasmoidModel::removeApplet); - m_systemTrayModel->addSourceModel(currentPlasmoidsModel); m_statusNotifierModel = new StatusNotifierModel(m_systemTrayModel); + + m_systemTrayModel->addSourceModel(currentPlasmoidsModel); m_systemTrayModel->addSourceModel(m_statusNotifierModel); } @@ -386,8 +389,6 @@ } } - QStringList ownApplets; - QMap sortedApplets; for (auto it = m_systrayApplets.constBegin(); it != m_systrayApplets.constEnd(); ++it) { const KPluginMetaData &info = it.value(); @@ -429,9 +430,13 @@ initDBusActivatables(); } -QAbstractItemModel *SystemTray::systemTrayModel() +QAbstractItemModel *SystemTray::configSystemTrayModel() { - return m_systemTrayModel; + if (!m_configSystemTrayModel) { + m_configSystemTrayModel = new SortedSystemTrayModel(SortedSystemTrayModel::SortingType::ConfigurationPage, this); + m_configSystemTrayModel->setSourceModel(m_systemTrayModel); + } + return m_configSystemTrayModel; } QStringList SystemTray::defaultPlasmoids() const @@ -487,7 +492,7 @@ connect(systemCallWatcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *callWatcher){ SystemTray::serviceNameFetchFinished(callWatcher, QDBusConnection::systemBus()); - }); + }); } void SystemTray::serviceNameFetchFinished(QDBusPendingCallWatcher* watcher, const QDBusConnection &connection) diff --git a/applets/systemtray/systemtraymodel.h b/applets/systemtray/systemtraymodel.h --- a/applets/systemtray/systemtraymodel.h +++ b/applets/systemtray/systemtraymodel.h @@ -30,20 +30,29 @@ class Applet; } -enum class BaseRole { - ItemType = Qt::UserRole + 1, - ItemId, - CanRender, - Category, - LastBaseRole +class BaseModel: public QStandardItemModel +{ + Q_OBJECT +public: + enum class BaseRole { + ItemType = Qt::UserRole + 1, + ItemId, + CanRender, + Category, + LastBaseRole + }; + + explicit BaseModel(QObject *parent = nullptr); + + QHash roleNames() const override; }; -class PlasmoidModel: public QStandardItemModel +class PlasmoidModel: public BaseModel { Q_OBJECT public: enum class Role { - Applet = static_cast(BaseRole::LastBaseRole) + 1, + Applet = static_cast(BaseModel::BaseRole::LastBaseRole) + 1, HasApplet }; @@ -56,11 +65,11 @@ void removeApplet(Plasma::Applet *applet); }; -class StatusNotifierModel : public QStandardItemModel, public Plasma::DataEngineConsumer { +class StatusNotifierModel : public BaseModel, public Plasma::DataEngineConsumer { Q_OBJECT public: enum class Role { - DataEngineSource = static_cast(BaseRole::LastBaseRole) + 100, + DataEngineSource = static_cast(BaseModel::BaseRole::LastBaseRole) + 100, AttentionIcon, AttentionIconName, AttentionMovieName, diff --git a/applets/systemtray/systemtraymodel.cpp b/applets/systemtray/systemtraymodel.cpp --- a/applets/systemtray/systemtraymodel.cpp +++ b/applets/systemtray/systemtraymodel.cpp @@ -29,6 +29,23 @@ #include +BaseModel::BaseModel(QObject *parent) + : QStandardItemModel(parent) +{ +} + +QHash BaseModel::roleNames() const +{ + QHash roles = QStandardItemModel::roleNames(); + + roles.insert(static_cast(BaseRole::ItemType), QByteArrayLiteral("itemType")); + roles.insert(static_cast(BaseRole::ItemId), QByteArrayLiteral("itemId")); + roles.insert(static_cast(BaseRole::CanRender), QByteArrayLiteral("canRender")); + roles.insert(static_cast(BaseRole::Category), QByteArrayLiteral("category")); + + return roles; +} + static QString plasmoidCategoryForMetadata(const KPluginMetaData &metadata) { QString category = QStringLiteral("UnknownCategory"); @@ -43,7 +60,7 @@ return category; } -PlasmoidModel::PlasmoidModel(QObject *parent) : QStandardItemModel(parent) +PlasmoidModel::PlasmoidModel(QObject *parent) : BaseModel(parent) { for (const auto &info : Plasma::PluginLoader::self()->listAppletMetaData(QString())) { if (!info.isValid() || info.value(QStringLiteral("X-Plasma-NotificationArea")) != "true") { @@ -58,24 +75,18 @@ name += i18n(" (Automatic load)"); } QStandardItem *item = new QStandardItem(QIcon::fromTheme(info.iconName()), name); - item->setData(info.pluginId(), static_cast(BaseRole::ItemId)); - item->setData(QStringLiteral("Plasmoid"), static_cast(BaseRole::ItemType)); - item->setData(false, static_cast(BaseRole::CanRender)); - item->setData(plasmoidCategoryForMetadata(info), static_cast(BaseRole::Category)); + item->setData(info.pluginId(), static_cast(BaseModel::BaseRole::ItemId)); + item->setData(QStringLiteral("Plasmoid"), static_cast(BaseModel::BaseRole::ItemType)); + item->setData(false, static_cast(BaseModel::BaseRole::CanRender)); + item->setData(plasmoidCategoryForMetadata(info), static_cast(BaseModel::BaseRole::Category)); item->setData(false, static_cast(Role::HasApplet)); appendRow(item); } - sort(0); } QHash PlasmoidModel::roleNames() const { - QHash roles = QStandardItemModel::roleNames(); - - roles.insert(static_cast(BaseRole::ItemType), QByteArrayLiteral("itemType")); - roles.insert(static_cast(BaseRole::ItemId), QByteArrayLiteral("itemId")); - roles.insert(static_cast(BaseRole::CanRender), QByteArrayLiteral("canRender")); - roles.insert(static_cast(BaseRole::Category), QByteArrayLiteral("category")); + QHash roles = BaseModel::roleNames(); roles.insert(static_cast(Role::Applet), QByteArrayLiteral("applet")); roles.insert(static_cast(Role::HasApplet), QByteArrayLiteral("hasApplet")); @@ -85,44 +96,55 @@ void PlasmoidModel::addApplet(Plasma::Applet *applet) { - auto info = applet->pluginMetaData(); + auto pluginMetaData = applet->pluginMetaData(); QStandardItem *dataItem = nullptr; for (int i = 0; i < rowCount(); i++) { QStandardItem *currentItem = item(i); - if (currentItem->data(static_cast(BaseRole::ItemId)) == info.pluginId()) { + if (currentItem->data(static_cast(BaseModel::BaseRole::ItemId)) == pluginMetaData.pluginId()) { dataItem = currentItem; break; } } if (!dataItem) { - dataItem = new QStandardItem(QIcon::fromTheme(info.iconName()), info.name()); + dataItem = new QStandardItem(); appendRow(dataItem); } - dataItem->setData(info.pluginId(), static_cast(BaseRole::ItemId)); + dataItem->setData(applet->title(), Qt::DisplayRole); + connect(applet, &Plasma::Applet::titleChanged, this, [dataItem] (const QString &title) { + dataItem->setData(title, static_cast(Qt::DisplayRole)); + }); + dataItem->setData(QIcon::fromTheme(applet->icon()), Qt::DecorationRole); + connect(applet, &Plasma::Applet::iconChanged, this, [dataItem] (const QString &icon) { + dataItem->setData(QIcon::fromTheme(icon), Qt::DecorationRole); + }); + + dataItem->setData(pluginMetaData.pluginId(), static_cast(BaseModel::BaseRole::ItemId)); + dataItem->setData(true, static_cast(BaseModel::BaseRole::CanRender)); + dataItem->setData(plasmoidCategoryForMetadata(pluginMetaData), static_cast(BaseModel::BaseRole::Category)); + dataItem->setData(applet->property("_plasma_graphicObject"), static_cast(Role::Applet)); dataItem->setData(true, static_cast(Role::HasApplet)); - dataItem->setData(true, static_cast(BaseRole::CanRender)); - dataItem->setData(plasmoidCategoryForMetadata(applet->pluginMetaData()), static_cast(BaseRole::Category)); } void PlasmoidModel::removeApplet(Plasma::Applet *applet) { int rows = rowCount(); for (int i = 0; i < rows; i++) { QStandardItem *currentItem = item(i); - QVariant plugin = currentItem->data(static_cast(BaseRole::ItemId)); + QVariant plugin = currentItem->data(static_cast(BaseModel::BaseRole::ItemId)); if (plugin.isValid() && plugin.value() == applet->pluginMetaData().pluginId()) { - currentItem->setData(false, static_cast(BaseRole::CanRender)); + currentItem->setData(false, static_cast(BaseModel::BaseRole::CanRender)); currentItem->setData(QVariant(), static_cast(Role::Applet)); currentItem->setData(false, static_cast(Role::HasApplet)); + applet->disconnect(this); return; } } } -StatusNotifierModel::StatusNotifierModel(QObject *parent) : QStandardItemModel(parent) +StatusNotifierModel::StatusNotifierModel(QObject *parent) : BaseModel(parent) { m_dataEngine = dataEngine(QStringLiteral("statusnotifieritem")); @@ -134,12 +156,7 @@ QHash StatusNotifierModel::roleNames() const { - QHash roles = QStandardItemModel::roleNames(); - - roles.insert(static_cast(BaseRole::ItemType), QByteArrayLiteral("itemType")); - roles.insert(static_cast(BaseRole::ItemId), QByteArrayLiteral("itemId")); - roles.insert(static_cast(BaseRole::CanRender), QByteArrayLiteral("canRender")); - roles.insert(static_cast(BaseRole::Category), QByteArrayLiteral("category")); + QHash roles = BaseModel::roleNames(); roles.insert(static_cast(Role::DataEngineSource), QByteArrayLiteral("DataEngineSource")); roles.insert(static_cast(Role::AttentionIcon), QByteArrayLiteral("AttentionIcon")); @@ -207,8 +224,8 @@ dataItem = item(m_sources.indexOf(sourceName)); } else { dataItem = new QStandardItem(); - dataItem->setData(QStringLiteral("StatusNotifier"), static_cast(BaseRole::ItemType)); - dataItem->setData(true, static_cast(BaseRole::CanRender)); + dataItem->setData(QStringLiteral("StatusNotifier"), static_cast(BaseModel::BaseRole::ItemType)); + dataItem->setData(true, static_cast(BaseModel::BaseRole::CanRender)); } dataItem->setData(data.value("Title"), Qt::DisplayRole); @@ -219,8 +236,9 @@ dataItem->setData(data.value("IconName"), Qt::DecorationRole); } - dataItem->setData(data.value("Id"), static_cast(BaseRole::ItemId)); - dataItem->setData(data.value("Category"), static_cast(BaseRole::Category)); + dataItem->setData(data.value("Id"), static_cast(BaseModel::BaseRole::ItemId)); + QVariant category = data.value("Category"); + dataItem->setData(category.isNull() ? QStringLiteral("UnknownCategory") : data.value("Category"), static_cast(BaseModel::BaseRole::Category)); dataItem->setData(sourceName, static_cast(Role::DataEngineSource)); updateItemData(dataItem, data, Role::AttentionIcon);