From: Bas Pape Date: Thu, 14 Feb 2013 21:55:43 +0000 (+0100) Subject: Add notification backend to support DockManagers X-Git-Tag: 0.9-beta1~3 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=0bf922728d0d0e8c62f954dddc9b08b56cc0b69b;hp=db66cc58b1329fe25b6a26b3824b3de3103ee539 Add notification backend to support DockManagers DockManager is a DBus API that does fancy icons. Current support includes progress reporting whilst connecting, requesting attention when highlighted and showing pending highlight count in a label. Exact support also depends on the dock. --- diff --git a/dev-notes/DockManager-spec.txt b/dev-notes/DockManager-spec.txt new file mode 100644 index 00000000..a8376b65 --- /dev/null +++ b/dev-notes/DockManager-spec.txt @@ -0,0 +1,74 @@ +DBus Interface Specification + +Docky implements the DockManager specificiation as well as a custom DBus specification. +DockManager DBus Interface Specification + +DBus unique path: net.launchpad.DockManager + +Object paths: + /net/launchpad/DockManager + +- Interface: net.launchpad.DockManager + * Methods: + * GetCapabilities () -> (Array of [String] capabilities) + * GetItems () -> (Array of [Object path]) + * GetItemsByName (String name -> (Array of [Object path]) + * GetItemsByDesktopFile (String desktop_file_name) -> (Array of [Object path]) + * GetItemsByPID (Int32 pid) -> (Array of [Object path]) + * GetItemByXid (Int64 xid) -> (Object path) + * Signals: + * ItemAdded (Object path) + * ItemRemoved (Object path) + +capabilities: + +- dock-item-attention +- dock-item-badge +- dock-item-icon-file +- dock-item-message +- dock-item-progress +- dock-item-tooltip +- dock-item-waiting + +- menu-item-container-title +- menu-item-icon-file +- menu-item-icon-name +- menu-item-with-label +- menu-item-with-uri + + + /net/launchpad/DockManager/Item[.+] (unspecified identifier) + +- Interface: net.launchpad.DockItem + * Methods: + * AddMenuItem (Dict of {String key, Variant value} menu_hints) -> (Int32 id) + * RemoveMenuItem (Int32 id) + * UpdateDockItem (Dict of {String key, Variant value} hints) + * Properties (implementing org.freedesktop.DBus.Properties) + * string DesktopFile + * string Uri + * Signals: + * MenuItemActivated (Int32 id) + +Supported menu_hints: + +Required: + - label + String + OR + - uri + String +Optional: + - container-title + String + - icon-file + String + - icon-name + String + +Implementor can choose whether icon setting will be honored when the menu item is specified using the "uri" key. + +Supported hints: All hints are optional. +- attention + Boolean +- badge + String +- icon-file + String +- message + String +- progress + Int +- tooltip + String +- waiting + Boolean + diff --git a/interfaces/org.freedesktop.DockItem.xml b/interfaces/org.freedesktop.DockItem.xml new file mode 100644 index 00000000..e18a5d60 --- /dev/null +++ b/interfaces/org.freedesktop.DockItem.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interfaces/org.freedesktop.DockManager.xml b/interfaces/org.freedesktop.DockManager.xml new file mode 100644 index 00000000..778888ba --- /dev/null +++ b/interfaces/org.freedesktop.DockManager.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/qtui/CMakeLists.txt b/src/qtui/CMakeLists.txt index 8d8dce05..7f67c9de 100644 --- a/src/qtui/CMakeLists.txt +++ b/src/qtui/CMakeLists.txt @@ -155,8 +155,8 @@ else(HAVE_KDE) endif(HAVE_KDE) if(HAVE_DBUS) - set(SOURCES ${SOURCES} statusnotifieritem.cpp statusnotifieritemdbus.cpp) - set(MOC_HDRS ${MOC_HDRS} statusnotifieritem.h statusnotifieritemdbus.h) + set(SOURCES ${SOURCES} statusnotifieritem.cpp statusnotifieritemdbus.cpp dockmanagernotificationbackend.cpp) + set(MOC_HDRS ${MOC_HDRS} statusnotifieritem.h statusnotifieritemdbus.h dockmanagernotificationbackend.h) set(FORMS ${FORMS}) if(WITH_QT5) qt5_add_dbus_interface(DBUS ../../interfaces/org.kde.StatusNotifierWatcher.xml statusnotifierwatcher) diff --git a/src/qtui/dockmanagernotificationbackend.cpp b/src/qtui/dockmanagernotificationbackend.cpp new file mode 100644 index 00000000..c9fdc0de --- /dev/null +++ b/src/qtui/dockmanagernotificationbackend.cpp @@ -0,0 +1,227 @@ +/*************************************************************************** + * Copyright (C) 2013 by the Quassel Project * + * devel@quassel-irc.org * + * * + * 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) version 3. * + * * + * 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 "dockmanagernotificationbackend.h" + +#include +#include +#include + +#include "client.h" +#include "clientsettings.h" +#include "coreconnection.h" +#include "clientbacklogmanager.h" + +DockManagerNotificationBackend::DockManagerNotificationBackend(QObject *parent) + : AbstractNotificationBackend(parent), _bus(QDBusConnection::sessionBus()), _dock(0), _item(0), _count(0) +{ + NotificationSettings notificationSettings; + _enabled = notificationSettings.value("DockManager/Enabled", false).toBool(); + + notificationSettings.notify("DockManager/Enabled", this, SLOT(enabledChanged(const QVariant &))); + + _dock = new QDBusInterface("net.launchpad.DockManager", "/net/launchpad/DockManager", "net.launchpad.DockManager", _bus, this); + if (_dock->isValid()) { + _bus.connect("net.launchpad.DockManager", "/net/launchpad/DockManager", "net.launchpad.DockManager", "ItemAdded", this, SLOT(itemAdded(QDBusObjectPath))); + } else { + // evil implementations (awn) use fd.o + _dock = new QDBusInterface("org.freedesktop.DockManager", "/org/freedesktop/DockManager", "org.freedesktop.DockManager", _bus, this); + if (_dock->isValid()) { + _bus.connect("org.freedesktop.DockManager", "/org/freedesktop/DockManager", "org.freedesktop.DockManager", "ItemAdded", this, SLOT(itemAdded(QDBusObjectPath))); + } else { + qDebug() << "No DockManager available"; + _enabled = false; + return; + } + } + + itemAdded(QDBusObjectPath()); + + connect(Client::coreConnection(), SIGNAL(progressValueChanged(int)), this, SLOT(updateProgress(int))); + connect(Client::coreConnection(), SIGNAL(synchronized()), this, SLOT(synchronized())); +} + + +void DockManagerNotificationBackend::itemAdded(QDBusObjectPath p) +{ + Q_UNUSED(p); + + if (_item) + return; + + // stupid implementations (awn; kde?) use wrong casing of PID, but proper type + QDBusReply > paths = _dock->call("GetItemsByPid", (int)QCoreApplication::applicationPid()); + if (!paths.isValid()) { + // stupid implementations (i.e. docky) use uint, but proper casing + paths = _dock->call("GetItemsByPID", (unsigned int)QCoreApplication::applicationPid()); + if (!paths.isValid()) { + qDebug() << "DBus error:" << paths.error().message(); + return; + } + } + if (paths.value().count() == 0) { // no icon for this instance + return; + } + + QString path = paths.value()[0].path(); // no sense in using multiple icons for one instance + _item = new QDBusInterface("org.freedesktop.DockManager", path, "org.freedesktop.DockItem", _bus, this); +} + + +void DockManagerNotificationBackend::updateProgress(int progress) +{ + if (!_enabled || !_item) + return; + + CoreConnection *c = Client::instance()->coreConnection(); + int perc = 0; + if (c->progressMaximum() == c->progressMinimum()) + perc = 0; + else + perc = (progress - c->progressMinimum()) * 100 / (c->progressMaximum() - c->progressMinimum()); + + QHash args; + args["progress"] = perc; + _item->call("UpdateDockItem", args); +} + + +void DockManagerNotificationBackend::updateProgress(int done, int total) +{ + if (!_enabled || !_item) + return; + + int perc = 0; + if (done == total) { + disconnect(Client::backlogManager(), 0, this, 0); + perc = -1; + } else + perc = (done * 100) / total; + + QHash args; + args["progress"] = perc; + _item->call("UpdateDockItem", args); +} + + +void DockManagerNotificationBackend::synchronized() +{ + connect(Client::backlogManager(), SIGNAL(updateProgress(int, int)), this, SLOT(updateProgress(int, int))); +} + + +void DockManagerNotificationBackend::notify(const Notification ¬ification) +{ + if (!_enabled || !_item) { + return; + } + if (notification.type != Highlight && notification.type != PrivMsg) { + return; + } + + QHash args; + args["attention"] = true; + args["badge"] = QString::number(++_count); + _item->call("UpdateDockItem", args); +} + + +void DockManagerNotificationBackend::close(uint notificationId) +{ + Q_UNUSED(notificationId); + if (!_item) + return; + + QHash args; + args["attention"] = false; + args["badge"] = --_count == 0 ? QString() : QString::number(_count); + _item->call("UpdateDockItem", args); +} + + +void DockManagerNotificationBackend::enabledChanged(const QVariant &v) +{ + _enabled = v.toBool(); + + if (!_enabled && _item) { + QHash args; + args["attention"] = false; + args["badge"] = QString(); + _item->call("UpdateDockItem", args); + } +} + + +SettingsPage *DockManagerNotificationBackend::createConfigWidget() const +{ + return new ConfigWidget(); +} + + +/***************************************************************************/ + +DockManagerNotificationBackend::ConfigWidget::ConfigWidget(QWidget *parent) + : SettingsPage("Internal", "DockManagerNotification", parent) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(enabledBox = new QCheckBox(tr("Mark dockmanager entry"), this)); + enabledBox->setEnabled(true); + + connect(enabledBox, SIGNAL(toggled(bool)), SLOT(widgetChanged())); +} + + +void DockManagerNotificationBackend::ConfigWidget::widgetChanged() +{ + bool changed = enabled != enabledBox->isChecked(); + + if (changed != hasChanged()) setChangedState(changed); +} + + +bool DockManagerNotificationBackend::ConfigWidget::hasDefaults() const +{ + return true; +} + + +void DockManagerNotificationBackend::ConfigWidget::defaults() +{ + enabledBox->setChecked(false); + widgetChanged(); +} + + +void DockManagerNotificationBackend::ConfigWidget::load() +{ + NotificationSettings s; + enabled = s.value("DockManager/Enabled", false).toBool(); + + enabledBox->setChecked(enabled); + setChangedState(false); +} + + +void DockManagerNotificationBackend::ConfigWidget::save() +{ + NotificationSettings s; + s.setValue("DockManager/Enabled", enabledBox->isChecked()); + load(); +} diff --git a/src/qtui/dockmanagernotificationbackend.h b/src/qtui/dockmanagernotificationbackend.h new file mode 100644 index 00000000..70efb006 --- /dev/null +++ b/src/qtui/dockmanagernotificationbackend.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2013 by the Quassel Project * + * devel@quassel-irc.org * + * * + * 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) version 3. * + * * + * 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 DOCKMANAGERNOTIFICATIONBACKEND_H_ +#define DOCKMANAGERNOTIFICATIONBACKEND_H_ + +#include +#include + +#include "abstractnotificationbackend.h" +#include "settingspage.h" + +class QCheckBox; + +class DockManagerNotificationBackend : public AbstractNotificationBackend +{ + Q_OBJECT + +public: + DockManagerNotificationBackend(QObject *parent = 0); + + void notify(const Notification &); + void close(uint notificationId); + virtual SettingsPage *createConfigWidget() const; + +private slots: + void enabledChanged(const QVariant &); + void updateProgress(int progress); + void updateProgress(int done, int total); + void itemAdded(QDBusObjectPath); + void synchronized(); + +private: + class ConfigWidget; + bool _enabled; + QDBusConnection _bus; + QDBusInterface *_dock; + QDBusInterface *_item; + int _count; +}; + + +class DockManagerNotificationBackend::ConfigWidget : public SettingsPage +{ + Q_OBJECT + +public: + ConfigWidget(QWidget *parent = 0); + + void save(); + void load(); + bool hasDefaults() const; + void defaults(); + +private slots: + void widgetChanged(); + +private: + QCheckBox *enabledBox; + bool enabled; +}; + + +#endif diff --git a/src/qtui/mainwin.cpp b/src/qtui/mainwin.cpp index fc53283b..a4be9fd9 100644 --- a/src/qtui/mainwin.cpp +++ b/src/qtui/mainwin.cpp @@ -112,6 +112,10 @@ #include "osxnotificationbackend.h" #endif +#ifdef HAVE_DBUS + #include "dockmanagernotificationbackend.h" +#endif + #include "settingspages/aliasessettingspage.h" #include "settingspages/appearancesettingspage.h" #include "settingspages/backlogsettingspage.h" @@ -226,6 +230,10 @@ void MainWin::init() QtUi::registerNotificationBackend(new OSXNotificationBackend(this)); #endif +#ifdef HAVE_DBUS + QtUi::registerNotificationBackend(new DockManagerNotificationBackend(this)); +#endif + // we assume that at this point, all configurable actions are defined! QtUi::loadShortcuts();