diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ ) find_package(LibKWorkspace 5.14.90 CONFIG REQUIRED) +find_package(LibNotificationManager 5.15.80 CONFIG REQUIRED) find_package(LibTaskManager 5.14.90 CONFIG REQUIRED) find_package(LibNotificationManager 5.14.90 CONFIG REQUIRED) find_package(LibColorCorrect 5.14.90 CONFIG REQUIRED) diff --git a/applets/taskmanager/CMakeLists.txt b/applets/taskmanager/CMakeLists.txt --- a/applets/taskmanager/CMakeLists.txt +++ b/applets/taskmanager/CMakeLists.txt @@ -29,6 +29,7 @@ KF5::KIOFileWidgets # KFilePlacesModel KF5::Plasma KF5::Service - KF5::WindowSystem) + KF5::WindowSystem + PW::LibNotificationManager) install(TARGETS taskmanagerplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/taskmanager) diff --git a/applets/taskmanager/package/contents/config/main.xml b/applets/taskmanager/package/contents/config/main.xml --- a/applets/taskmanager/package/contents/config/main.xml +++ b/applets/taskmanager/package/contents/config/main.xml @@ -87,10 +87,6 @@ 0 - - - true - true diff --git a/applets/taskmanager/package/contents/ui/ConfigAppearance.qml b/applets/taskmanager/package/contents/ui/ConfigAppearance.qml --- a/applets/taskmanager/package/contents/ui/ConfigAppearance.qml +++ b/applets/taskmanager/package/contents/ui/ConfigAppearance.qml @@ -34,7 +34,6 @@ property alias cfg_showToolTips: showToolTips.checked property alias cfg_highlightWindows: highlightWindows.checked - property alias cfg_smartLaunchersEnabled: smartLaunchers.checked property bool cfg_indicateAudioStreams property alias cfg_iconSize: iconSize.value property alias cfg_maxStripes: maxStripes.value @@ -55,11 +54,6 @@ text: i18n("Highlight windows when hovering over tasks") } - CheckBox { - id: smartLaunchers - text: i18n("Show progress and status information in task buttons") - } - CheckBox { id: indicateAudioStreams text: i18n("Mark applications that play audio") diff --git a/applets/taskmanager/package/contents/ui/Task.qml b/applets/taskmanager/package/contents/ui/Task.qml --- a/applets/taskmanager/package/contents/ui/Task.qml +++ b/applets/taskmanager/package/contents/ui/Task.qml @@ -55,7 +55,7 @@ property int pressY: -1 property QtObject contextMenu: null property int wheelDelta: 0 - readonly property bool smartLauncherEnabled: plasmoid.configuration.smartLaunchersEnabled && !inPopup && model.IsStartup !== true + readonly property bool smartLauncherEnabled: !inPopup && model.IsStartup !== true property QtObject smartLauncherItem: null property alias toolTipAreaItem: toolTipArea @@ -381,7 +381,7 @@ }); toolTipDelegate.smartLauncherCountVisible = Qt.binding(function() { - return plasmoid.configuration.smartLaunchersEnabled && task.smartLauncherItem && task.smartLauncherItem.countVisible; + return task.smartLauncherItem && task.smartLauncherItem.countVisible; }); toolTipDelegate.smartLauncherCount = Qt.binding(function() { return toolTipDelegate.smartLauncherCountVisible ? task.smartLauncherItem.count : 0; @@ -395,7 +395,7 @@ anchors.fill: frame asynchronous: true source: "TaskProgressOverlay.qml" - active: plasmoid.configuration.smartLaunchersEnabled && task.smartLauncherItem && task.smartLauncherItem.progressVisible + active: task.smartLauncherItem && task.smartLauncherItem.progressVisible } Item { @@ -448,7 +448,7 @@ height: parent.height asynchronous: true source: "TaskBadgeOverlay.qml" - active: plasmoid.configuration.smartLaunchersEnabled && height >= units.iconSizes.small + active: height >= units.iconSizes.small && task.smartLauncherItem && task.smartLauncherItem.countVisible } diff --git a/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.h b/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.h --- a/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.h +++ b/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2016 Kai Uwe Broulik * + * Copyright (C) 2016, 2019 Kai Uwe Broulik * * * * 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 * @@ -25,13 +25,14 @@ #include #include -#include +#include class QDBusServiceWatcher; class QString; -namespace Plasma { -class DataEngineConsumer; +namespace NotificationManager +{ +class Settings; } namespace SmartLauncher { @@ -53,7 +54,6 @@ explicit Backend(QObject *parent = nullptr); ~Backend() override; - bool available() const; bool hasLauncher(const QString &storageId) const; int count(const QString &uri) const; @@ -64,49 +64,52 @@ QHash unityMappingRules() const; -public slots: - void dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data); - signals: void countChanged(const QString &uri, int count); void countVisibleChanged(const QString &uri, bool countVisible); void progressChanged(const QString &uri, int progress); void progressVisibleChanged(const QString &uri, bool progressVisible); void urgentChanged(const QString &uri, bool urgent); + void reloadRequested(const QString &uri); void launcherRemoved(const QString &uri); private slots: void update(const QString &uri, const QMap &properties); private: - bool setupUnity(); - bool setupApplicationJobs(); + void reload(); + void setupUnity(); + void setupApplicationJobs(); void onServiceUnregistered(const QString &service); template void updateLauncherProperty(const QString &storageId, // our KService storage id const QVariantMap &properties, // the map of properties we're given by DBus const QString &property, // the property we're looking for T *entryMember, // the member variable we're going to write our result in + // the getter for this property which might return something different from the raw value + T (Backend::*getter)(const QString &) const, // the change signal that will be emitted if the property has changed void (Backend::*changeSignal)(const QString &, T)) { auto foundProperty = properties.constFind(property); if (foundProperty != properties.constEnd()) { + const T oldSanitizedValue = ((this)->*getter)(storageId); + T newValue = foundProperty->value(); + *entryMember = newValue; - if (newValue != *entryMember) { - *entryMember = newValue; - emit ((this)->*changeSignal)(storageId, newValue); + const T newSanitizedValue = ((this)->*getter)(storageId); + + if (newSanitizedValue != oldSanitizedValue) { + emit ((this)->*changeSignal)(storageId, newSanitizedValue); } } } - void onApplicationJobAdded(const QString &source); - void onApplicationJobRemoved(const QString &source); - void updateApplicationJobPercent(const QString &storageId, Entry *entry); + bool doNotDisturbMode() const; // Unity Launchers QDBusServiceWatcher *m_watcher; @@ -118,14 +121,14 @@ QHash m_unityMappingRules; // Application Jobs - Plasma::DataEngineConsumer *m_dataEngineConsumer; - Plasma::DataEngine *m_dataEngine; - QHash m_dataSourceToStorageId; // - QHash m_storageIdToJobs; // > - QHash m_jobProgress; // + NotificationManager::JobsModel::Ptr m_jobsModel; + + NotificationManager::Settings *m_settings = nullptr; QHash m_launchers; + QStringList m_badgeBlacklist; + bool m_available = false; }; diff --git a/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.cpp b/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.cpp --- a/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.cpp +++ b/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2016 Kai Uwe Broulik * + * Copyright (C) 2016, 2019 Kai Uwe Broulik * * * * 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,47 +24,69 @@ #include #include -#include -#include - +#include #include #include +#include + +#include +#include + using namespace SmartLauncher; +using namespace NotificationManager; Backend::Backend(QObject *parent) : QObject(parent) , m_watcher(new QDBusServiceWatcher(this)) - , m_dataEngineConsumer(new Plasma::DataEngineConsumer) - , m_dataEngine(m_dataEngineConsumer->dataEngine(QStringLiteral("applicationjobs"))) + , m_settings(new Settings(this)) { - m_available = setupUnity(); - m_available = setupApplicationJobs() || m_available; + setupUnity(); + + reload(); + connect(m_settings, &Settings::settingsChanged, this, &Backend::reload); } -Backend::~Backend() +Backend::~Backend() = default; + +void Backend::reload() { - delete m_dataEngineConsumer; + m_badgeBlacklist = m_settings->badgeBlacklistedApplications(); + + // Unity Launcher API operates on storage IDs ("foo.desktop"), whereas settings return desktop entries "foo" + std::transform(m_badgeBlacklist.begin(), m_badgeBlacklist.end(), m_badgeBlacklist.begin(), [](const QString &desktopEntry) { + return desktopEntry + QStringLiteral(".desktop"); + }); + + setupApplicationJobs(); + + emit reloadRequested(QString() /*all*/); +} + +bool Backend::doNotDisturbMode() const +{ + return m_settings->notificationsInhibitedByApplication() + || (m_settings->notificationsInhibitedUntil().isValid() && m_settings->notificationsInhibitedUntil() > QDateTime::currentDateTimeUtc()); } -bool Backend::setupUnity() +void Backend::setupUnity() { auto sessionBus = QDBusConnection::sessionBus(); if (!sessionBus.connect({}, {}, QStringLiteral("com.canonical.Unity.LauncherEntry"), QStringLiteral("Update"), this, SLOT(update(QString,QMap)))) { qWarning() << "failed to register Update signal"; - return false; + return; } if (!sessionBus.registerObject(QStringLiteral("/Unity"), this)) { qWarning() << "Failed to register unity object"; - return false; + return; } if (!sessionBus.registerService(QStringLiteral("com.canonical.Unity"))) { qWarning() << "Failed to register unity service"; - return false; + // In case an external process uses this (e.g. Latte Dock), let it just listen. } KConfigGroup grp(KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc")), QStringLiteral("Unity Launcher Mapping")); @@ -77,31 +99,16 @@ m_unityMappingRules.insert(key, value); } - - return true; } -bool Backend::setupApplicationJobs() +void Backend::setupApplicationJobs() { - if (!m_dataEngine->isValid()) { - qWarning() << "Failed to setup application jobs, data engine is not valid"; - return false; - } - - const QStringList &sources = m_dataEngine->sources(); - for (const QString &source : sources) { - onApplicationJobAdded(source); + if (m_settings->jobsInTaskManager() && !m_jobsModel) { + m_jobsModel = JobsModel::createJobsModel(); + m_jobsModel->init(); + } else if (!m_settings->jobsInTaskManager() && m_jobsModel) { + m_jobsModel = nullptr; } - - connect(m_dataEngine, &Plasma::DataEngine::sourceAdded, this, &Backend::onApplicationJobAdded); - connect(m_dataEngine, &Plasma::DataEngine::sourceRemoved, this, &Backend::onApplicationJobRemoved); - - return true; -} - -bool Backend::available() const -{ - return m_available; } bool Backend::hasLauncher(const QString &storageId) const @@ -111,21 +118,37 @@ int Backend::count(const QString &uri) const { + if (!m_settings->badgesInTaskManager() + || doNotDisturbMode() + || m_badgeBlacklist.contains(uri)) { + return 0; + } return m_launchers.value(uri).count; } bool Backend::countVisible(const QString &uri) const { + if (!m_settings->badgesInTaskManager() + || doNotDisturbMode() + || m_badgeBlacklist.contains(uri)) { + return false; + } return m_launchers.value(uri).countVisible; } int Backend::progress(const QString &uri) const { + if (!m_settings->jobsInTaskManager()) { + return 0; + } return m_launchers.value(uri).progress; } bool Backend::progressVisible(const QString &uri) const { + if (!m_settings->jobsInTaskManager()) { + return false; + } return m_launchers.value(uri).progressVisible; } @@ -192,22 +215,26 @@ } } - updateLauncherProperty(storageId, properties, QStringLiteral("count"), &foundEntry->count, &Backend::countChanged); - updateLauncherProperty(storageId, properties, QStringLiteral("count-visible"), &foundEntry->countVisible, &Backend::countVisibleChanged); + updateLauncherProperty(storageId, properties, QStringLiteral("count"), &foundEntry->count, &Backend::count, &Backend::countChanged); + updateLauncherProperty(storageId, properties, QStringLiteral("count-visible"), &foundEntry->countVisible, &Backend::countVisible, &Backend::countVisibleChanged); // the API gives us progress as 0..1 double but we'll use percent to avoid unnecessary // changes when it just changed a fraction of a percent, hence not using our fancy updateLauncherProperty method auto foundProgress = properties.constFind(QStringLiteral("progress")); if (foundProgress != propertiesEnd) { - int newProgress = qRound(foundProgress->toDouble() * 100); - if (newProgress != foundEntry->progress) { - foundEntry->progress = newProgress; - emit progressChanged(storageId, newProgress); + const int oldSanitizedProgress = progress(storageId); + + foundEntry->progress = qRound(foundProgress->toDouble() * 100); + + const int newSanitizedProgress = progress(storageId); + + if (oldSanitizedProgress != newSanitizedProgress) { + emit progressChanged(storageId, newSanitizedProgress); } } - updateLauncherProperty(storageId, properties, QStringLiteral("progress-visible"), &foundEntry->progressVisible, &Backend::progressVisibleChanged); - updateLauncherProperty(storageId, properties, QStringLiteral("urgent"), &foundEntry->urgent, &Backend::urgentChanged); + updateLauncherProperty(storageId, properties, QStringLiteral("progress-visible"), &foundEntry->progressVisible, &Backend::progressVisible, &Backend::progressVisibleChanged); + updateLauncherProperty(storageId, properties, QStringLiteral("urgent"), &foundEntry->urgent, &Backend::urgent, &Backend::urgentChanged); } void Backend::onServiceUnregistered(const QString &service) @@ -225,129 +252,3 @@ m_launchers.remove(storageId); emit launcherRemoved(storageId); } - -void Backend::onApplicationJobAdded(const QString &source) -{ - m_dataEngine->connectSource(source, this); -} - -void Backend::onApplicationJobRemoved(const QString &source) -{ - m_dataEngine->disconnectSource(source, this); - - const QString &storageId = m_dataSourceToStorageId.take(source); - if (storageId.isEmpty()) { - return; - } - - // remove job, calculate new percentage, or remove launcher if gone altogether - auto &jobs = m_storageIdToJobs[storageId]; - jobs.removeOne(source); - if (jobs.isEmpty()) { - m_storageIdToJobs.remove(storageId); - } - - m_jobProgress.remove(source); - - auto foundEntry = m_launchers.find(storageId); - if (foundEntry == m_launchers.end()) { - qWarning() << "Cannot remove application job" << source << "as we don't know" << storageId; - return; - } - - updateApplicationJobPercent(storageId, &*foundEntry); - - if (!foundEntry->progressVisible && !foundEntry->progress) { - // no progress anymore whatsoever, remove entire launcher - m_launchers.remove(storageId); - emit launcherRemoved(storageId); - } -} - -void Backend::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data) -{ - QString storageId; - - auto foundStorageId = m_dataSourceToStorageId.constFind(sourceName); - if (foundStorageId == m_dataSourceToStorageId.constEnd()) { // we don't know this one, register - QString appName = data.value(QStringLiteral("appName")).toString(); - if (appName.isEmpty()) { - qWarning() << "Application jobs got update for" << sourceName << "without app name"; - return; - } - - KService::Ptr service = KService::serviceByStorageId(appName); - if (!service) { - appName.prepend(QLatin1String("org.kde.")); - // HACK try to find a service with org.kde. notation - service = KService::serviceByStorageId(appName); - if (!service) { - qWarning() << "Could not find service for job" << sourceName << "with app name" << appName; - return; - } - } - - storageId = service->storageId(); - m_dataSourceToStorageId.insert(sourceName, storageId); - } else { - storageId = *foundStorageId; - } - - auto foundEntry = m_launchers.find(storageId); - if (foundEntry == m_launchers.end()) { // we don't have it yet, create new Entry - Entry entry; - foundEntry = m_launchers.insert(storageId, entry); - } - - int percent = data.value(QStringLiteral("percentage"), 0).toInt(); - - // setup everything and calculate new percentage - auto &jobs = m_storageIdToJobs[storageId]; - if (!jobs.contains(sourceName)) { - jobs.append(sourceName); - } - - m_jobProgress.insert(sourceName, percent); // insert() overrides if exist - - updateApplicationJobPercent(storageId, &*foundEntry); -} - -void Backend::updateApplicationJobPercent(const QString &storageId, Entry *entry) -{ - // basically get all jobs for the given storageId and calculate an average progress - - const auto &jobs = m_storageIdToJobs.value(storageId); - qreal jobCount = jobs.count(); - - int totalProgress = 0; - for (const QString &job : jobs) { - totalProgress += m_jobProgress.value(job, 0); - } - - int progress = 0; - if (jobCount > 0) { - progress = qRound(totalProgress / jobCount); - } - - bool visible = (jobCount > 0); - - if (entry->count != jobCount) { - entry->count = jobCount; - emit countChanged(storageId, jobCount); - } - - if (entry->countVisible != visible) { - entry->countVisible = visible; - emit countVisibleChanged(storageId, visible); - } - - if (entry->progress != progress) { - entry->progress = progress; - emit progressChanged(storageId, progress); - } - - if (entry->progressVisible != visible) { - entry->progressVisible = visible; - emit progressVisibleChanged(storageId, visible); - } -} diff --git a/applets/taskmanager/plugin/smartlaunchers/smartlauncheritem.h b/applets/taskmanager/plugin/smartlaunchers/smartlauncheritem.h --- a/applets/taskmanager/plugin/smartlaunchers/smartlauncheritem.h +++ b/applets/taskmanager/plugin/smartlaunchers/smartlauncheritem.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2016 Kai Uwe Broulik * + * Copyright (C) 2016, 2019 Kai Uwe Broulik * * * * 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 * @@ -35,8 +35,6 @@ Q_PROPERTY(QUrl launcherUrl READ launcherUrl WRITE setLauncherUrl NOTIFY launcherUrlChanged) - Q_PROPERTY(bool available READ available NOTIFY availableChanged) - Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(bool countVisible READ countVisible NOTIFY countVisibleChanged) Q_PROPERTY(int progress READ progress NOTIFY progressChanged) @@ -50,8 +48,6 @@ QUrl launcherUrl() const; void setLauncherUrl(const QUrl &launcherUrl); - bool available() const; - int count() const; bool countVisible() const; int progress() const; @@ -61,8 +57,6 @@ signals: void launcherUrlChanged(const QUrl &launcherUrl); - void availableChanged(bool available); - void countChanged(int count); void countVisibleChanged(bool countVisible); void progressChanged(int progress); @@ -88,7 +82,6 @@ QUrl m_launcherUrl; QString m_storageId; - bool m_available = false; bool m_inited = false; int m_count = 0; diff --git a/applets/taskmanager/plugin/smartlaunchers/smartlauncheritem.cpp b/applets/taskmanager/plugin/smartlaunchers/smartlauncheritem.cpp --- a/applets/taskmanager/plugin/smartlaunchers/smartlauncheritem.cpp +++ b/applets/taskmanager/plugin/smartlaunchers/smartlauncheritem.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2016 Kai Uwe Broulik * + * Copyright (C) 2016, 2019 Kai Uwe Broulik * * * * 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 * @@ -38,53 +38,58 @@ void Item::init() { - if (m_inited || m_storageId.isEmpty() || !m_backendPtr || !m_backendPtr->available()) { + if (m_inited || m_storageId.isEmpty() || !m_backendPtr) { return; } + connect(m_backendPtr.data(), &Backend::reloadRequested, this, [this](const QString &uri) { + if (uri.isEmpty() || m_storageId == uri) { + populate(); + } + }); + connect(m_backendPtr.data(), &Backend::launcherRemoved, this, [this](const QString &uri) { - if (m_storageId == uri) { + if (uri.isEmpty() || m_storageId == uri) { clear(); } }); connect(m_backendPtr.data(), &Backend::countChanged, this, [this](const QString &uri, int count) { - if (m_storageId == uri) { + if (uri.isEmpty() || m_storageId == uri) { setCount(count); } }); connect(m_backendPtr.data(), &Backend::countVisibleChanged, this, [this](const QString &uri, bool countVisible) { - if (m_storageId == uri) { + if (uri.isEmpty() || m_storageId == uri) { setCountVisible(countVisible); } }); connect(m_backendPtr.data(), &Backend::progressChanged, this, [this](const QString &uri, int progress) { - if (m_storageId == uri) { + if (uri.isEmpty() || m_storageId == uri) { setProgress(progress); } }); connect(m_backendPtr.data(), &Backend::progressVisibleChanged, this, [this](const QString &uri, bool progressVisible) { - if (m_storageId == uri) { + if (uri.isEmpty() || m_storageId == uri) { setProgressVisible(progressVisible); } }); connect(m_backendPtr.data(), &Backend::urgentChanged, this, [this](const QString &uri, bool urgent) { - if (m_storageId == uri) { + if (uri.isEmpty() || m_storageId == uri) { setUrgent(urgent); } }); - m_available = true; - emit availableChanged(m_available); + m_inited = true; } void Item::populate() { - if (!m_backendPtr || !m_backendPtr->available() || m_storageId.isEmpty()) { + if (!m_backendPtr || m_storageId.isEmpty()) { return; } @@ -156,10 +161,6 @@ } } -bool Item::available() const -{ - return m_available; -} int Item::count() const {