You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
392 lines
16 KiB
392 lines
16 KiB
/*************************************************************************** |
|
* Copyright (C) 2019 Konrad Materka <materka@gmail.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 * |
|
* 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 "systemtraymodel.h" |
|
#include "debug.h" |
|
|
|
#include <QQuickItem> |
|
|
|
#include <Plasma/Applet> |
|
#include <Plasma/DataContainer> |
|
#include <Plasma/Service> |
|
#include <PluginLoader> |
|
|
|
#include <KLocalizedString> |
|
|
|
BaseModel::BaseModel(QObject *parent) |
|
: QStandardItemModel(parent), |
|
m_showAllItems(false) |
|
{ |
|
connect(this, &BaseModel::rowsInserted, this, &BaseModel::onRowsInserted); |
|
connect(this, &BaseModel::dataChanged, this, &BaseModel::onDataChanged); |
|
} |
|
|
|
QHash<int, QByteArray> BaseModel::roleNames() const |
|
{ |
|
QHash<int, QByteArray> roles = QStandardItemModel::roleNames(); |
|
|
|
roles.insert(static_cast<int>(BaseRole::ItemType), QByteArrayLiteral("itemType")); |
|
roles.insert(static_cast<int>(BaseRole::ItemId), QByteArrayLiteral("itemId")); |
|
roles.insert(static_cast<int>(BaseRole::CanRender), QByteArrayLiteral("canRender")); |
|
roles.insert(static_cast<int>(BaseRole::Category), QByteArrayLiteral("category")); |
|
roles.insert(static_cast<int>(BaseRole::Status), QByteArrayLiteral("status")); |
|
roles.insert(static_cast<int>(BaseRole::EffectiveStatus), QByteArrayLiteral("effectiveStatus")); |
|
|
|
return roles; |
|
} |
|
|
|
void BaseModel::onConfigurationChanged(const KConfigGroup &config) |
|
{ |
|
if (!config.isValid()) { |
|
return; |
|
} |
|
|
|
const KConfigGroup generalGroup = config.group("General"); |
|
|
|
m_showAllItems = generalGroup.readEntry("showAllItems", false); |
|
m_shownItems = generalGroup.readEntry("shownItems", QStringList()); |
|
m_hiddenItems = generalGroup.readEntry("hiddenItems", QStringList()); |
|
|
|
for (int i = 0; i < rowCount(); i++) { |
|
QStandardItem *dataItem = item(i); |
|
updateEffectiveStatus(dataItem); |
|
} |
|
} |
|
|
|
void BaseModel::onRowsInserted(const QModelIndex &parent, int first, int last) |
|
{ |
|
if (parent.isValid()) { |
|
return; |
|
} |
|
|
|
for (int i = first; i <= last; ++i) { |
|
QStandardItem *dataItem = item(i); |
|
updateEffectiveStatus(dataItem); |
|
} |
|
} |
|
|
|
void BaseModel::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) |
|
{ |
|
if (roles.contains(static_cast<int>(BaseRole::Status)) || roles.contains(static_cast<int>(BaseRole::CanRender))) { |
|
for (int i = topLeft.row(); i <= bottomRight.row(); i++) { |
|
QStandardItem *dataItem = item(i); |
|
updateEffectiveStatus(dataItem); |
|
} |
|
} |
|
} |
|
|
|
void BaseModel::updateEffectiveStatus(QStandardItem *dataItem) |
|
{ |
|
Plasma::Types::ItemStatus status = calculateEffectiveStatus(dataItem); |
|
dataItem->setData(status, static_cast<int>(BaseModel::BaseRole::EffectiveStatus)); |
|
} |
|
|
|
Plasma::Types::ItemStatus BaseModel::calculateEffectiveStatus(QStandardItem *dataItem) |
|
{ |
|
bool canRender = dataItem->data(static_cast<int>(BaseRole::CanRender)).toBool(); |
|
if (!canRender) { |
|
return Plasma::Types::ItemStatus::HiddenStatus; |
|
} |
|
|
|
Plasma::Types::ItemStatus status = readStatus(dataItem); |
|
if (status == Plasma::Types::ItemStatus::HiddenStatus) { |
|
return Plasma::Types::ItemStatus::HiddenStatus; |
|
} |
|
|
|
QString itemId = dataItem->data(static_cast<int>(BaseRole::ItemId)).toString(); |
|
|
|
bool forcedShown = m_showAllItems || m_shownItems.contains(itemId); |
|
bool forcedHidden = m_hiddenItems.contains(itemId); |
|
|
|
if (forcedShown || (!forcedHidden && status != Plasma::Types::ItemStatus::PassiveStatus)) { |
|
return Plasma::Types::ItemStatus::ActiveStatus; |
|
} else { |
|
return Plasma::Types::ItemStatus::PassiveStatus; |
|
} |
|
} |
|
|
|
Plasma::Types::ItemStatus BaseModel::readStatus(QStandardItem *dataItem) const |
|
{ |
|
QVariant statusData = dataItem->data(static_cast<int>(BaseRole::Status)); |
|
if (statusData.isValid()) { |
|
return statusData.value<Plasma::Types::ItemStatus>(); |
|
} else { |
|
return Plasma::Types::ItemStatus::UnknownStatus; |
|
} |
|
} |
|
|
|
static QString plasmoidCategoryForMetadata(const KPluginMetaData &metadata) |
|
{ |
|
QString category = QStringLiteral("UnknownCategory"); |
|
|
|
if (metadata.isValid()) { |
|
const QString notificationAreaCategory = metadata.value(QStringLiteral("X-Plasma-NotificationAreaCategory")); |
|
if (!notificationAreaCategory.isEmpty()) { |
|
category = notificationAreaCategory; |
|
} |
|
} |
|
|
|
return category; |
|
} |
|
|
|
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") { |
|
continue; |
|
} |
|
|
|
QString name = info.name(); |
|
const QString dbusactivation = info.value(QStringLiteral("X-Plasma-DBusActivationService")); |
|
if (!dbusactivation.isEmpty()) { |
|
name += i18n(" (Automatic load)"); |
|
} |
|
QStandardItem *item = new QStandardItem(QIcon::fromTheme(info.iconName()), name); |
|
item->setData(info.pluginId(), static_cast<int>(BaseModel::BaseRole::ItemId)); |
|
item->setData(QStringLiteral("Plasmoid"), static_cast<int>(BaseModel::BaseRole::ItemType)); |
|
item->setData(false, static_cast<int>(BaseModel::BaseRole::CanRender)); |
|
item->setData(plasmoidCategoryForMetadata(info), static_cast<int>(BaseModel::BaseRole::Category)); |
|
item->setData(false, static_cast<int>(Role::HasApplet)); |
|
appendRow(item); |
|
} |
|
} |
|
|
|
QHash<int, QByteArray> PlasmoidModel::roleNames() const |
|
{ |
|
QHash<int, QByteArray> roles = BaseModel::roleNames(); |
|
|
|
roles.insert(static_cast<int>(Role::Applet), QByteArrayLiteral("applet")); |
|
roles.insert(static_cast<int>(Role::HasApplet), QByteArrayLiteral("hasApplet")); |
|
|
|
return roles; |
|
} |
|
|
|
void PlasmoidModel::addApplet(Plasma::Applet *applet) |
|
{ |
|
auto pluginMetaData = applet->pluginMetaData(); |
|
QStandardItem *dataItem = nullptr; |
|
for (int i = 0; i < rowCount(); i++) { |
|
QStandardItem *currentItem = item(i); |
|
if (currentItem->data(static_cast<int>(BaseModel::BaseRole::ItemId)) == pluginMetaData.pluginId()) { |
|
dataItem = currentItem; |
|
break; |
|
} |
|
} |
|
|
|
if (!dataItem) { |
|
QString name = pluginMetaData.name(); |
|
const QString dbusactivation = pluginMetaData.value(QStringLiteral("X-Plasma-DBusActivationService")); |
|
if (!dbusactivation.isEmpty()) { |
|
name += i18n(" (Automatic load)"); |
|
} |
|
dataItem = new QStandardItem(QIcon::fromTheme(pluginMetaData.iconName()), name); |
|
appendRow(dataItem); |
|
} |
|
|
|
dataItem->setData(pluginMetaData.pluginId(), static_cast<int>(BaseModel::BaseRole::ItemId)); |
|
dataItem->setData(plasmoidCategoryForMetadata(pluginMetaData), static_cast<int>(BaseModel::BaseRole::Category)); |
|
dataItem->setData(applet->status(), static_cast<int>(BaseModel::BaseRole::Status)); |
|
connect(applet, &Plasma::Applet::statusChanged, this, [dataItem] (Plasma::Types::ItemStatus status) { |
|
dataItem->setData(status, static_cast<int>(BaseModel::BaseRole::Status)); |
|
}); |
|
|
|
dataItem->setData(applet->property("_plasma_graphicObject"), static_cast<int>(Role::Applet)); |
|
dataItem->setData(true, static_cast<int>(Role::HasApplet)); |
|
|
|
// CanRender has to be the last one |
|
dataItem->setData(true, static_cast<int>(BaseModel::BaseRole::CanRender)); |
|
} |
|
|
|
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<int>(BaseModel::BaseRole::ItemId)); |
|
if (plugin.isValid() && plugin.value<QString>() == applet->pluginMetaData().pluginId()) { |
|
currentItem->setData(false, static_cast<int>(BaseModel::BaseRole::CanRender)); |
|
currentItem->setData(QVariant(), static_cast<int>(Role::Applet)); |
|
currentItem->setData(false, static_cast<int>(Role::HasApplet)); |
|
applet->disconnect(this); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
StatusNotifierModel::StatusNotifierModel(QObject *parent) : BaseModel(parent) |
|
{ |
|
m_dataEngine = dataEngine(QStringLiteral("statusnotifieritem")); |
|
|
|
connect(m_dataEngine, &Plasma::DataEngine::sourceAdded, this, &StatusNotifierModel::addSource); |
|
connect(m_dataEngine, &Plasma::DataEngine::sourceRemoved, this, &StatusNotifierModel::removeSource); |
|
|
|
m_dataEngine->connectAllSources(this); |
|
} |
|
|
|
QHash<int, QByteArray> StatusNotifierModel::roleNames() const |
|
{ |
|
QHash<int, QByteArray> roles = BaseModel::roleNames(); |
|
|
|
roles.insert(static_cast<int>(Role::DataEngineSource), QByteArrayLiteral("DataEngineSource")); |
|
roles.insert(static_cast<int>(Role::AttentionIcon), QByteArrayLiteral("AttentionIcon")); |
|
roles.insert(static_cast<int>(Role::AttentionIconName), QByteArrayLiteral("AttentionIconName")); |
|
roles.insert(static_cast<int>(Role::AttentionMovieName), QByteArrayLiteral("AttentionMovieName")); |
|
roles.insert(static_cast<int>(Role::Category), QByteArrayLiteral("Category")); |
|
roles.insert(static_cast<int>(Role::Icon), QByteArrayLiteral("Icon")); |
|
roles.insert(static_cast<int>(Role::IconName), QByteArrayLiteral("IconName")); |
|
roles.insert(static_cast<int>(Role::IconThemePath), QByteArrayLiteral("IconThemePath")); |
|
roles.insert(static_cast<int>(Role::Id), QByteArrayLiteral("Id")); |
|
roles.insert(static_cast<int>(Role::ItemIsMenu), QByteArrayLiteral("ItemIsMenu")); |
|
roles.insert(static_cast<int>(Role::OverlayIconName), QByteArrayLiteral("OverlayIconName")); |
|
roles.insert(static_cast<int>(Role::Status), QByteArrayLiteral("Status")); |
|
roles.insert(static_cast<int>(Role::Title), QByteArrayLiteral("Title")); |
|
roles.insert(static_cast<int>(Role::ToolTipSubTitle), QByteArrayLiteral("ToolTipSubTitle")); |
|
roles.insert(static_cast<int>(Role::ToolTipTitle), QByteArrayLiteral("ToolTipTitle")); |
|
roles.insert(static_cast<int>(Role::WindowId), QByteArrayLiteral("WindowId")); |
|
|
|
return roles; |
|
} |
|
|
|
Plasma::Service *StatusNotifierModel::serviceForSource(const QString &source) |
|
{ |
|
if (m_services.contains(source)) { |
|
return m_services.value(source); |
|
} |
|
|
|
Plasma::Service *service = m_dataEngine->serviceForSource(source); |
|
if (!service) { |
|
return nullptr; |
|
} |
|
m_services[source] = service; |
|
return service; |
|
} |
|
|
|
void StatusNotifierModel::addSource(const QString &source) |
|
{ |
|
m_dataEngine->connectSource(source, this); |
|
} |
|
|
|
void StatusNotifierModel::removeSource(const QString &source) |
|
{ |
|
m_dataEngine->disconnectSource(source, this); |
|
if (m_sources.contains(source)) { |
|
removeRow(m_sources.indexOf(source)); |
|
m_sources.removeAll(source); |
|
} |
|
|
|
QHash<QString, Plasma::Service *>::iterator it = m_services.find(source); |
|
if (it != m_services.end()) { |
|
delete it.value(); |
|
m_services.erase(it); |
|
} |
|
} |
|
|
|
void StatusNotifierModel::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data) |
|
{ |
|
QStandardItem *dataItem; |
|
if (m_sources.contains(sourceName)) { |
|
dataItem = item(m_sources.indexOf(sourceName)); |
|
} else { |
|
dataItem = new QStandardItem(); |
|
dataItem->setData(QStringLiteral("StatusNotifier"), static_cast<int>(BaseModel::BaseRole::ItemType)); |
|
dataItem->setData(true, static_cast<int>(BaseModel::BaseRole::CanRender)); |
|
} |
|
|
|
dataItem->setData(data.value("Title"), Qt::DisplayRole); |
|
QVariant icon = data.value("Icon"); |
|
if (icon.isValid() && icon.canConvert<QIcon>() && !icon.value<QIcon>().isNull()) { |
|
dataItem->setData(icon, Qt::DecorationRole); |
|
dataItem->setData(icon, static_cast<int>(Role::Icon)); |
|
} else { |
|
dataItem->setData(data.value("IconName"), Qt::DecorationRole); |
|
dataItem->setData(QVariant(), static_cast<int>(Role::Icon)); |
|
} |
|
QVariant attentionIcon = data.value("AttentionIcon"); |
|
if (attentionIcon.isValid() && attentionIcon.canConvert<QIcon>() && !attentionIcon.value<QIcon>().isNull()) { |
|
dataItem->setData(attentionIcon, static_cast<int>(Role::AttentionIcon)); |
|
} else { |
|
dataItem->setData(QVariant(), static_cast<int>(Role::AttentionIcon)); |
|
} |
|
|
|
dataItem->setData(data.value("Id"), static_cast<int>(BaseModel::BaseRole::ItemId)); |
|
QVariant category = data.value("Category"); |
|
dataItem->setData(category.isNull() ? QStringLiteral("UnknownCategory") : data.value("Category"), static_cast<int>(BaseModel::BaseRole::Category)); |
|
|
|
QString status = data.value("Status").toString(); |
|
if (status == QLatin1String("Active")) { |
|
dataItem->setData(Plasma::Types::ItemStatus::ActiveStatus, static_cast<int>(BaseModel::BaseRole::Status)); |
|
} else if (status == QLatin1String("NeedsAttention")) { |
|
dataItem->setData(Plasma::Types::ItemStatus::NeedsAttentionStatus, static_cast<int>(BaseModel::BaseRole::Status)); |
|
} else if (status == QLatin1String("Passive")) { |
|
dataItem->setData(Plasma::Types::ItemStatus::PassiveStatus, static_cast<int>(BaseModel::BaseRole::Status)); |
|
} else { |
|
dataItem->setData(Plasma::Types::ItemStatus::UnknownStatus, static_cast<int>(BaseModel::BaseRole::Status)); |
|
} |
|
|
|
dataItem->setData(sourceName, static_cast<int>(Role::DataEngineSource)); |
|
updateItemData(dataItem, data, Role::AttentionIconName); |
|
updateItemData(dataItem, data, Role::AttentionMovieName); |
|
updateItemData(dataItem, data, Role::Category); |
|
updateItemData(dataItem, data, Role::IconName); |
|
updateItemData(dataItem, data, Role::IconThemePath); |
|
updateItemData(dataItem, data, Role::Id); |
|
updateItemData(dataItem, data, Role::ItemIsMenu); |
|
updateItemData(dataItem, data, Role::OverlayIconName); |
|
updateItemData(dataItem, data, Role::Status); |
|
updateItemData(dataItem, data, Role::Title); |
|
updateItemData(dataItem, data, Role::ToolTipSubTitle); |
|
updateItemData(dataItem, data, Role::ToolTipTitle); |
|
updateItemData(dataItem, data, Role::WindowId); |
|
|
|
if (!m_sources.contains(sourceName)) { |
|
m_sources.append(sourceName); |
|
appendRow(dataItem); |
|
} |
|
} |
|
|
|
void StatusNotifierModel::updateItemData(QStandardItem *dataItem, |
|
const Plasma::DataEngine::Data &data, const Role role) |
|
{ |
|
int roleId = static_cast<int>(role); |
|
dataItem->setData(data.value(roleNames().value(roleId)), roleId); |
|
} |
|
|
|
SystemTrayModel::SystemTrayModel(QObject *parent) : KConcatenateRowsProxyModel(parent) |
|
{ |
|
m_roleNames = KConcatenateRowsProxyModel::roleNames(); |
|
} |
|
|
|
QHash<int, QByteArray> SystemTrayModel::roleNames() const |
|
{ |
|
return m_roleNames; |
|
} |
|
|
|
void SystemTrayModel::addSourceModel(QAbstractItemModel *sourceModel) |
|
{ |
|
QHashIterator<int, QByteArray> it(sourceModel->roleNames()); |
|
while (it.hasNext()) { |
|
it.next(); |
|
|
|
if (!m_roleNames.contains(it.key())) { |
|
m_roleNames.insert(it.key(), it.value()); |
|
} |
|
} |
|
|
|
KConcatenateRowsProxyModel::addSourceModel(sourceModel); |
|
}
|
|
|