diff --git a/debian/control b/debian/control index f0d860d94..750b3e6bc 100644 --- a/debian/control +++ b/debian/control @@ -10,11 +10,9 @@ Build-Depends: debhelper (>= 8.0.0), pkg-config, libxcb-icccm4-dev, libxtst-dev, libdtkwidget-dev, libdtkcore-dev | libdtkutil-dev, qttools5-dev-tools, libxcb-icccm4-dev, - qtbase5-private-dev, - libdframeworkdbus-dev, - libgsettings-qt-dev, - cmake, - libdde-network-utils-dev + qtbase5-private-dev, libdframeworkdbus-dev, + libgsettings-qt-dev, cmake, + libdde-network-utils-dev, libdbusmenu-qt5-dev Standards-Version: 3.9.8 Homepage: http://www.deepin.org/ diff --git a/plugins/system-tray/CMakeLists.txt b/plugins/system-tray/CMakeLists.txt index 758f7c782..eb5e15b95 100644 --- a/plugins/system-tray/CMakeLists.txt +++ b/plugins/system-tray/CMakeLists.txt @@ -4,7 +4,8 @@ set(PLUGIN_NAME "system-tray") project(${PLUGIN_NAME}) # Sources files -file(GLOB_RECURSE SRCS "*.h" "*.cpp" "../../widgets/*.h" "../../widgets/*.cpp") +file(GLOB_RECURSE SRCS "*.h" "*.cpp" "../../widgets/*.h" "../../widgets/*.cpp" + "../../frame/util/themeappicon.h" "../../frame/util/themeappicon.cpp") find_package(PkgConfig REQUIRED) find_package(Qt5Widgets REQUIRED) @@ -13,7 +14,7 @@ find_package(Qt5DBus REQUIRED) find_package(Qt5X11Extras REQUIRED) find_package(DtkWidget REQUIRED) -pkg_check_modules(XCB_LIBS REQUIRED xcb-ewmh xcb xcb-image xcb-composite xtst xcb-icccm) +pkg_check_modules(XCB_LIBS REQUIRED xcb-ewmh xcb xcb-image xcb-composite xtst xcb-icccm dbusmenu-qt5) add_definitions("${QT_DEFINITIONS} -DQT_PLUGIN") add_library(${PLUGIN_NAME} SHARED ${SRCS} system-tray.qrc) diff --git a/plugins/system-tray/dbus/sni/dbusstructures.cpp b/plugins/system-tray/dbus/sni/dbusstructures.cpp new file mode 100644 index 000000000..e7cf5ada0 --- /dev/null +++ b/plugins/system-tray/dbus/sni/dbusstructures.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2011 Canonical Ltd. + * Copyright 2014 Dmitry Shachnev + * + * Maintainer: listenerri + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Based on sni-qt code by Aurelien Gateau . + */ + +#include +#include "dbusstructures.h" + +QDBusArgument &operator<<(QDBusArgument &argument, const DBusImage &image) +{ + argument.beginStructure(); + argument << image.width << image.height << image.pixels; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, DBusImage &image) +{ + argument.beginStructure(); + argument >> image.width >> image.height >> image.pixels; + argument.endStructure(); + return argument; +} + +QDBusArgument &operator<<(QDBusArgument &argument, const DBusToolTip &tip) +{ + argument.beginStructure(); + argument << tip.iconName << tip.iconPixmap << tip.title << tip.description; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, DBusToolTip &tip) +{ + argument.beginStructure(); + argument >> tip.iconName >> tip.iconPixmap >> tip.title >> tip.description; + argument.endStructure(); + return argument; +} + +void registerMetaTypes() +{ + static bool registered = false; + if (registered) { + return; + } + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); +} diff --git a/plugins/system-tray/dbus/sni/dbusstructures.h b/plugins/system-tray/dbus/sni/dbusstructures.h new file mode 100644 index 000000000..f8b524593 --- /dev/null +++ b/plugins/system-tray/dbus/sni/dbusstructures.h @@ -0,0 +1,60 @@ +/* + * Copyright 2011 Canonical Ltd. + * Copyright 2014 Dmitry Shachnev + * + * Maintainer: listenerri + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Based on sni-qt code by Aurelien Gateau . + */ + +#ifndef DBUSSTRUCTURES_H +#define DBUSSTRUCTURES_H + +#include +#include +#include +#include +#include +#include + +struct DBusImage +{ + int width; + int height; + QByteArray pixels; +}; +Q_DECLARE_METATYPE(DBusImage) + +typedef QList DBusImageList; +Q_DECLARE_METATYPE(DBusImageList) + +struct DBusToolTip +{ + QString iconName; + DBusImageList iconPixmap; + QString title; + QString description; +}; +Q_DECLARE_METATYPE(DBusToolTip) + +QDBusArgument &operator<<(QDBusArgument&, const DBusImage&); +const QDBusArgument &operator>>(const QDBusArgument&, DBusImage&); + +QDBusArgument &operator<<(QDBusArgument&, const DBusToolTip&); +const QDBusArgument &operator>>(const QDBusArgument&, DBusToolTip&); + +void registerMetaTypes(); + +#endif /* DBUSSTRUCTURES_H */ diff --git a/plugins/system-tray/dbus/sni/statusnotifieritem_interface.cpp b/plugins/system-tray/dbus/sni/statusnotifieritem_interface.cpp new file mode 100644 index 000000000..9c010fbbc --- /dev/null +++ b/plugins/system-tray/dbus/sni/statusnotifieritem_interface.cpp @@ -0,0 +1,23 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp item-inter.xml -a statusnotifieritemadapter -p statusnotifieritemproxy + * + * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd. + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "statusnotifieritem_interface.h" + +StatusNotifierItemInterface::StatusNotifierItemInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) +{ + registerMetaTypes(); +} + +StatusNotifierItemInterface::~StatusNotifierItemInterface() +{ +} + diff --git a/plugins/system-tray/dbus/sni/statusnotifieritem_interface.h b/plugins/system-tray/dbus/sni/statusnotifieritem_interface.h new file mode 100644 index 000000000..a2405b11e --- /dev/null +++ b/plugins/system-tray/dbus/sni/statusnotifieritem_interface.h @@ -0,0 +1,145 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp item-inter.xml -a statusnotifieritemadapter -p statusnotifieritemproxy + * + * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd. + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#ifndef STATUSNOTIFIERITEM_H +#define STATUSNOTIFIERITEM_H + +#include "dbusstructures.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Proxy class for interface org.kde.StatusNotifierItem + */ +class StatusNotifierItemInterface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.kde.StatusNotifierItem"; } + +public: + StatusNotifierItemInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr); + + ~StatusNotifierItemInterface(); + + Q_PROPERTY(QString AttentionIconName READ attentionIconName) + inline QString attentionIconName() const + { return qvariant_cast< QString >(property("AttentionIconName")); } + + Q_PROPERTY(DBusImageList AttentionIconPixmap READ attentionIconPixmap) + inline DBusImageList attentionIconPixmap() const + { return qvariant_cast< DBusImageList >(property("AttentionIconPixmap")); } + + Q_PROPERTY(QString AttentionMovieName READ attentionMovieName) + inline QString attentionMovieName() const + { return qvariant_cast< QString >(property("AttentionMovieName")); } + + Q_PROPERTY(QString Category READ category) + inline QString category() const + { return qvariant_cast< QString >(property("Category")); } + + Q_PROPERTY(QString IconName READ iconName) + inline QString iconName() const + { return qvariant_cast< QString >(property("IconName")); } + + Q_PROPERTY(DBusImageList IconPixmap READ iconPixmap) + inline DBusImageList iconPixmap() const + { return qvariant_cast< DBusImageList >(property("IconPixmap")); } + + Q_PROPERTY(QString Id READ id) + inline QString id() const + { return qvariant_cast< QString >(property("Id")); } + + Q_PROPERTY(bool ItemIsMenu READ itemIsMenu) + inline bool itemIsMenu() const + { return qvariant_cast< bool >(property("ItemIsMenu")); } + + Q_PROPERTY(QDBusObjectPath Menu READ menu) + inline QDBusObjectPath menu() const + { return qvariant_cast< QDBusObjectPath >(property("Menu")); } + + Q_PROPERTY(QString OverlayIconName READ overlayIconName) + inline QString overlayIconName() const + { return qvariant_cast< QString >(property("OverlayIconName")); } + + Q_PROPERTY(DBusImageList OverlayIconPixmap READ overlayIconPixmap) + inline DBusImageList overlayIconPixmap() const + { return qvariant_cast< DBusImageList >(property("OverlayIconPixmap")); } + + Q_PROPERTY(QString Status READ status) + inline QString status() const + { return qvariant_cast< QString >(property("Status")); } + + Q_PROPERTY(QString Title READ title) + inline QString title() const + { return qvariant_cast< QString >(property("Title")); } + + Q_PROPERTY(DBusToolTip ToolTip READ toolTip) + inline DBusToolTip toolTip() const + { return qvariant_cast< DBusToolTip >(property("ToolTip")); } + + Q_PROPERTY(int WindowId READ windowId) + inline int windowId() const + { return qvariant_cast< int >(property("WindowId")); } + +public Q_SLOTS: // METHODS + inline QDBusPendingReply<> Activate(int x, int y) + { + QList argumentList; + argumentList << QVariant::fromValue(x) << QVariant::fromValue(y); + return asyncCallWithArgumentList(QStringLiteral("Activate"), argumentList); + } + + inline QDBusPendingReply<> ContextMenu(int x, int y) + { + QList argumentList; + argumentList << QVariant::fromValue(x) << QVariant::fromValue(y); + return asyncCallWithArgumentList(QStringLiteral("ContextMenu"), argumentList); + } + + inline QDBusPendingReply<> Scroll(int delta, const QString &orientation) + { + QList argumentList; + argumentList << QVariant::fromValue(delta) << QVariant::fromValue(orientation); + return asyncCallWithArgumentList(QStringLiteral("Scroll"), argumentList); + } + + inline QDBusPendingReply<> SecondaryActivate(int x, int y) + { + QList argumentList; + argumentList << QVariant::fromValue(x) << QVariant::fromValue(y); + return asyncCallWithArgumentList(QStringLiteral("SecondaryActivate"), argumentList); + } + +Q_SIGNALS: // SIGNALS + void NewAttentionIcon(); + void NewIcon(); + void NewOverlayIcon(); + void NewStatus(const QString &status); + void NewTitle(); + void NewToolTip(); +}; + +namespace com { + namespace deepin { + namespace dde { + typedef ::StatusNotifierItemInterface StatusNotifierItem; + } + } +} +#endif diff --git a/plugins/system-tray/dbus/sni/statusnotifierwatcheradapter.cpp b/plugins/system-tray/dbus/sni/statusnotifierwatcheradapter.cpp new file mode 100644 index 000000000..5c6f7fb6f --- /dev/null +++ b/plugins/system-tray/dbus/sni/statusnotifierwatcheradapter.cpp @@ -0,0 +1,65 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp watcher-inter.xml -a statusnotifierwatcheradapter -p statusnotifierwatcherproxy + * + * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd. + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#include "statusnotifierwatcheradapter.h" +#include +#include +#include +#include +#include +#include +#include + +/* + * Implementation of adaptor class StatusNotifierWatcherAdaptor + */ + +StatusNotifierWatcherAdaptor::StatusNotifierWatcherAdaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) +{ + // constructor + setAutoRelaySignals(true); +} + +StatusNotifierWatcherAdaptor::~StatusNotifierWatcherAdaptor() +{ + // destructor +} + +bool StatusNotifierWatcherAdaptor::isStatusNotifierHostRegistered() const +{ + // get the value of property IsStatusNotifierHostRegistered + return qvariant_cast< bool >(parent()->property("IsStatusNotifierHostRegistered")); +} + +int StatusNotifierWatcherAdaptor::protocolVersion() const +{ + // get the value of property ProtocolVersion + return qvariant_cast< int >(parent()->property("ProtocolVersion")); +} + +QStringList StatusNotifierWatcherAdaptor::registeredStatusNotifierItems() const +{ + // get the value of property RegisteredStatusNotifierItems + return qvariant_cast< QStringList >(parent()->property("RegisteredStatusNotifierItems")); +} + +void StatusNotifierWatcherAdaptor::RegisterStatusNotifierHost(const QString &service) +{ + // handle method call org.kde.StatusNotifierWatcher.RegisterStatusNotifierHost + QMetaObject::invokeMethod(parent(), "RegisterStatusNotifierHost", Q_ARG(QString, service)); +} + +void StatusNotifierWatcherAdaptor::RegisterStatusNotifierItem(const QString &service) +{ + // handle method call org.kde.StatusNotifierWatcher.RegisterStatusNotifierItem + QMetaObject::invokeMethod(parent(), "RegisterStatusNotifierItem", Q_ARG(QString, service)); +} + diff --git a/plugins/system-tray/dbus/sni/statusnotifierwatcheradapter.h b/plugins/system-tray/dbus/sni/statusnotifierwatcheradapter.h new file mode 100644 index 000000000..c8b47e029 --- /dev/null +++ b/plugins/system-tray/dbus/sni/statusnotifierwatcheradapter.h @@ -0,0 +1,80 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp watcher-inter.xml -a statusnotifierwatcheradapter -p statusnotifierwatcherproxy + * + * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd. + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#ifndef STATUSNOTIFIERWATCHERADAPTER_H +#define STATUSNOTIFIERWATCHERADAPTER_H + +#include +#include +QT_BEGIN_NAMESPACE +class QByteArray; +template class QList; +template class QMap; +class QString; +class QStringList; +class QVariant; +QT_END_NAMESPACE + +/* + * Adaptor class for interface org.kde.StatusNotifierWatcher + */ +class StatusNotifierWatcherAdaptor: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.StatusNotifierWatcher") + Q_CLASSINFO("D-Bus Introspection", "" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" + "") +public: + StatusNotifierWatcherAdaptor(QObject *parent); + virtual ~StatusNotifierWatcherAdaptor(); + +public: // PROPERTIES + Q_PROPERTY(bool IsStatusNotifierHostRegistered READ isStatusNotifierHostRegistered) + bool isStatusNotifierHostRegistered() const; + + Q_PROPERTY(int ProtocolVersion READ protocolVersion) + int protocolVersion() const; + + Q_PROPERTY(QStringList RegisteredStatusNotifierItems READ registeredStatusNotifierItems) + QStringList registeredStatusNotifierItems() const; + +public Q_SLOTS: // METHODS + void RegisterStatusNotifierHost(const QString &service); + void RegisterStatusNotifierItem(const QString &service); +Q_SIGNALS: // SIGNALS + void StatusNotifierHostRegistered(); + void StatusNotifierHostUnregistered(); + void StatusNotifierItemRegistered(const QString &in0); + void StatusNotifierItemUnregistered(const QString &in0); +}; + +#endif diff --git a/plugins/system-tray/sni/statusnotifierwatcher.cpp b/plugins/system-tray/sni/statusnotifierwatcher.cpp new file mode 100644 index 000000000..0b2362d72 --- /dev/null +++ b/plugins/system-tray/sni/statusnotifierwatcher.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd. + * + * Author: listenerri + * + * Maintainer: listenerri + * + * 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 3 of the License, or + * 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, see . + */ + +#include "statusnotifierwatcher.h" + +#include +#include +#include +#include +#include + +#include "dbus/sni/statusnotifierwatcheradapter.h" +#include "dbus/sni/statusnotifieritem_interface.h" + +StatusNotifierWatcher::StatusNotifierWatcher(QObject *parent) +{ + new StatusNotifierWatcherAdaptor(this); + QDBusConnection dbus = QDBusConnection::sessionBus(); + dbus.registerObject(QStringLiteral("/StatusNotifierWatcher"), this); + dbus.registerService(QStringLiteral("org.kde.StatusNotifierWatcher")); + + m_serviceWatcher = new QDBusServiceWatcher(this); + m_serviceWatcher->setConnection(dbus); + m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration); + + connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &StatusNotifierWatcher::serviceUnregistered); +} + +StatusNotifierWatcher::~StatusNotifierWatcher() +{ + QDBusConnection dbus = QDBusConnection::sessionBus(); + dbus.unregisterService(QStringLiteral("org.kde.StatusNotifierWatcher")); +} + + +void StatusNotifierWatcher::RegisterStatusNotifierItem(const QString &serviceOrPath) +{ + QString service; + QString path; + if (serviceOrPath.startsWith(QLatin1Char('/'))) { + service = message().service(); + path = serviceOrPath; + } else { + service = serviceOrPath; + path = QStringLiteral("/StatusNotifierItem"); + } + QString notifierItemId = service + path; + if (QDBusConnection::sessionBus().interface()->isServiceRegistered(service).value() && + !m_registeredServices.contains(notifierItemId)) { + qDebug()<<"Registering" << notifierItemId << "to system tray"; + + //check if the service has registered a SystemTray object + com::deepin::dde::StatusNotifierItem trayclient(service, path, + QDBusConnection::sessionBus()); + if (trayclient.isValid()) { + m_registeredServices.append(notifierItemId); + m_serviceWatcher->addWatchedService(service); + emit StatusNotifierItemRegistered(notifierItemId); + } + } +} + +QStringList StatusNotifierWatcher::RegisteredStatusNotifierItems() const +{ + return m_registeredServices; +} + + +void StatusNotifierWatcher::serviceUnregistered(const QString& name) +{ + qDebug()<<"Service "<< name << "unregistered"; + m_serviceWatcher->removeWatchedService(name); + + QString match = name + QLatin1Char('/'); + QStringList::Iterator it = m_registeredServices.begin(); + while (it != m_registeredServices.end()) { + if (it->startsWith(match)) { + QString name = *it; + it = m_registeredServices.erase(it); + emit StatusNotifierItemUnregistered(name); + } else { + ++it; + } + } + + if (m_statusNotifierHostServices.contains(name)) { + m_statusNotifierHostServices.remove(name); + emit StatusNotifierHostUnregistered(); + } +} + +void StatusNotifierWatcher::RegisterStatusNotifierHost(const QString &service) +{ + if (service.contains(QStringLiteral("org.kde.StatusNotifierHost-")) && + QDBusConnection::sessionBus().interface()->isServiceRegistered(service).value() && + !m_statusNotifierHostServices.contains(service)) { + qDebug()<<"Registering"<addWatchedService(service); + emit StatusNotifierHostRegistered(); + } +} + +bool StatusNotifierWatcher::IsStatusNotifierHostRegistered() const +{ + return !m_statusNotifierHostServices.isEmpty(); +} + +int StatusNotifierWatcher::ProtocolVersion() const +{ + return 0; +} diff --git a/plugins/system-tray/sni/statusnotifierwatcher.h b/plugins/system-tray/sni/statusnotifierwatcher.h new file mode 100644 index 000000000..dc7888470 --- /dev/null +++ b/plugins/system-tray/sni/statusnotifierwatcher.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd. + * + * Author: listenerri + * + * Maintainer: listenerri + * + * 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 3 of the License, or + * 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, see . + */ + +#ifndef STATUSNOTIFIERWATCHER_H +#define STATUSNOTIFIERWATCHER_H + +#include +#include +#include +#include + +class QDBusServiceWatcher; + +class StatusNotifierWatcher : public QObject, protected QDBusContext +{ + Q_OBJECT + + Q_PROPERTY(QStringList RegisteredStatusNotifierItems READ RegisteredStatusNotifierItems) + Q_PROPERTY(bool IsStatusNotifierHostRegistered READ IsStatusNotifierHostRegistered) + Q_PROPERTY(int ProtocolVersion READ ProtocolVersion) + +public: + StatusNotifierWatcher(QObject *parent); + ~StatusNotifierWatcher(); + + QStringList RegisteredStatusNotifierItems() const; + + bool IsStatusNotifierHostRegistered() const; + + int ProtocolVersion() const; + +public Q_SLOTS: + void RegisterStatusNotifierItem(const QString &service); + + void RegisterStatusNotifierHost(const QString &service); + +protected Q_SLOTS: + void serviceUnregistered(const QString& name); + +Q_SIGNALS: + void StatusNotifierItemRegistered(const QString &service); + //TODO: decide if this makes sense, the systray itself could notice the vanishing of items, but looks complete putting it here + void StatusNotifierItemUnregistered(const QString &service); + void StatusNotifierHostRegistered(); + void StatusNotifierHostUnregistered(); + +private: + QDBusServiceWatcher *m_serviceWatcher = nullptr; + QStringList m_registeredServices; + QSet m_statusNotifierHostServices; +}; +#endif diff --git a/plugins/system-tray/snitraywidget.cpp b/plugins/system-tray/snitraywidget.cpp new file mode 100644 index 000000000..596029048 --- /dev/null +++ b/plugins/system-tray/snitraywidget.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd. + * + * Author: listenerri + * + * Maintainer: listenerri + * + * 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 3 of the License, or + * 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, see . + */ + +#include "snitraywidget.h" +#include "../frame/util/themeappicon.h" + +#include +#include + +#include + +#define IconSize 16 + +const QStringList ItemCategoryList {"ApplicationStatus" , "Communications" , "SystemServices", "Hardware"}; +const QStringList ItemStatusList {"ApplicationStatus" , "Communications" , "SystemServices", "Hardware"}; + +SNITrayWidget::SNITrayWidget(const QString &sniServicePath, QWidget *parent) + : AbstractTrayWidget(parent), + m_dbusMenuImporter(nullptr), + m_menu(nullptr) +{ + if (sniServicePath.startsWith("/") || !sniServicePath.contains("/")) { + return; + } + + QPair pair = serviceAndPath(sniServicePath); + const QString &service = pair.first; + const QString &path = pair.second; + + m_sniInter = new StatusNotifierItem(service, path, QDBusConnection::sessionBus(), this); + + if (!m_sniInter->isValid()) { + return; + } + + const QString &menuPath = m_sniInter->menu().path(); + m_dbusMenuImporter = new DBusMenuImporter(service, menuPath, SYNCHRONOUS, this); + m_menu = m_dbusMenuImporter->menu(); + + connect(m_sniInter, &StatusNotifierItem::NewIcon, this, &SNITrayWidget::refreshIcon); + connect(m_sniInter, &StatusNotifierItem::NewOverlayIcon, this, &SNITrayWidget::refreshOverlayIcon); + connect(m_sniInter, &StatusNotifierItem::NewAttentionIcon, this, &SNITrayWidget::refreshAttentionIcon); + + QTimer::singleShot(0, this, &SNITrayWidget::refreshIcon); +} + +SNITrayWidget::~SNITrayWidget() +{ +} + +void SNITrayWidget::setActive(const bool active) +{ +} + +void SNITrayWidget::updateIcon() +{ + update(); +} + +void SNITrayWidget::sendClick(uint8_t mouseButton, int x, int y) +{ + switch (mouseButton) { + case XCB_BUTTON_INDEX_1: + m_sniInter->Activate(x, y); + break; + case XCB_BUTTON_INDEX_2: + m_sniInter->SecondaryActivate(x, y); + break; + case XCB_BUTTON_INDEX_3: + // ContextMenu does not work + //m_sniInter->ContextMenu(x, y); + m_menu->popup(QPoint(x, y)); + break; + default: + qDebug() << "unknown mouse button key"; + break; + } +} + +const QImage SNITrayWidget::trayImage() +{ + return m_pixmap.toImage(); +} + +bool SNITrayWidget::isValid() +{ + return m_sniInter->isValid(); +} + +/* + *ItemCategory SNITrayWidget::category() + *{ + * const QString &category = m_sniInter->category(); + * if (!ItemCategoryList.contains(category)) { + * return UnknownCategory; + * } + * + * return static_cast(ItemCategoryList.indexOf(category)); + *} + * + *ItemStatus SNITrayWidget::status() + *{ + * const QString &status = m_sniInter->status(); + * if (!ItemStatusList.contains(status)) { + * return UnknownStatus; + * } + * + * return static_cast(ItemStatusList.indexOf(status)); + *} + * + */ + +void SNITrayWidget::refreshIcon() +{ + QPixmap pix = newIconPixmap(Icon); + if (pix.isNull()) { + return; + } + + m_pixmap = pix; + update(); + Q_EMIT iconChanged(); +} + +void SNITrayWidget::refreshOverlayIcon() +{ + QPixmap pix = newIconPixmap(OverlayIcon); + if (pix.isNull()) { + return; + } + + m_overlayPixmap = pix; + update(); + Q_EMIT iconChanged(); +} + +void SNITrayWidget::refreshAttentionIcon() +{ + /* TODO: A new approach may be needed to deal with attentionIcon */ + QPixmap pix = newIconPixmap(AttentionIcon); + if (pix.isNull()) { + return; + } + + m_pixmap = pix; + update(); + Q_EMIT iconChanged(); +} + +QSize SNITrayWidget::sizeHint() const +{ + return QSize(26, 26); +} + +void SNITrayWidget::paintEvent(QPaintEvent *e) +{ + Q_UNUSED(e); + if (m_pixmap.isNull()) + return; + + QPainter painter; + painter.begin(this); + painter.setRenderHint(QPainter::Antialiasing); +#ifdef QT_DEBUG + //painter.fillRect(rect(), Qt::red); +#endif + + const QPoint p = rect().center() - m_pixmap.rect().center() / m_pixmap.devicePixelRatioF(); + painter.drawPixmap(p, m_pixmap); + + if (!m_overlayPixmap.isNull()) { + painter.drawPixmap(p, m_overlayPixmap); + } + + painter.end(); +} + +QPair SNITrayWidget::serviceAndPath(const QString &servicePath) +{ + QStringList list = servicePath.split("/"); + QPair pair; + pair.first = list.takeFirst(); + + for (auto i : list) { + pair.second.append("/"); + pair.second.append(i); + } + + return pair; +} + +QPixmap SNITrayWidget::newIconPixmap(IconType iconType) +{ + QPixmap pixmap; + if (iconType == UnknownIconType) { + return pixmap; + } + + QString iconName; + DBusImageList dbusImageList; + + switch (iconType) { + case Icon: + iconName = m_sniInter->iconName(); + dbusImageList = m_sniInter->iconPixmap(); + break; + case OverlayIcon: + iconName = m_sniInter->overlayIconName(); + dbusImageList = m_sniInter->overlayIconPixmap(); + break; + case AttentionIcon: + iconName = m_sniInter->attentionIconName(); + dbusImageList = m_sniInter->attentionIconPixmap(); + break; + case AttentionMovieIcon: + iconName = m_sniInter->attentionMovieName(); + break; + default: + break; + } + + do { + if (!iconName.isEmpty()) { + pixmap = ThemeAppIcon::getIcon(iconName, IconSize); + if (!pixmap.isNull()) { + break; + } + } + + const auto ratio = qApp->devicePixelRatio(); + if (!dbusImageList.isEmpty()) { + for (DBusImage dbusImage : dbusImageList) { + char *image_data = dbusImage.pixels.data(); + + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + for (int i = 0; i < dbusImage.pixels.size(); i += 4) { + *(qint32*)(image_data + i) = qFromBigEndian(*(qint32*)(image_data + i)); + } + } + + QImage image((const uchar*)dbusImage.pixels.constData(), dbusImage.width, dbusImage.height, QImage::Format_ARGB32); + pixmap = QPixmap::fromImage(image.scaled(IconSize, IconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + pixmap.setDevicePixelRatio(ratio); + if (!pixmap.isNull()) { + break; + } + } + } + + if (pixmap.isNull()) { + qDebug() << "get icon faild!"; + } + } while (false); + + return pixmap; +} diff --git a/plugins/system-tray/snitraywidget.h b/plugins/system-tray/snitraywidget.h new file mode 100644 index 000000000..066db31ff --- /dev/null +++ b/plugins/system-tray/snitraywidget.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd. + * + * Author: listenerri + * + * Maintainer: listenerri + * + * 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 3 of the License, or + * 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, see . + */ + +#ifndef SNITRAYWIDGET_H +#define SNITRAYWIDGET_H + +#include "abstracttraywidget.h" +#include "dbus/sni/statusnotifieritem_interface.h" + +#include + +#include +#include + +using namespace com::deepin::dde; + +enum ItemCategory {UnknownCategory = -1, ApplicationStatus, Communications, SystemServices, Hardware}; +enum ItemStatus {UnknownStatus = -1, Passive, Active, NeedsAttention}; +enum IconType {UnknownIconType = -1, Icon, OverlayIcon, AttentionIcon, AttentionMovieIcon}; + +class SNITrayWidget : public AbstractTrayWidget +{ + Q_OBJECT +public: + SNITrayWidget(const QString &sniServicePath, QWidget *parent = Q_NULLPTR); + virtual ~SNITrayWidget(); + + void setActive(const bool active) Q_DECL_OVERRIDE; + void updateIcon() Q_DECL_OVERRIDE; + void sendClick(uint8_t mouseButton, int x, int y) Q_DECL_OVERRIDE; + const QImage trayImage() Q_DECL_OVERRIDE; + + bool isValid(); + + static QString toSNIKey(const QString &sniServicePath) { return QString("sni:%1").arg(sniServicePath); } + static bool isSNIKey(const QString &itemKey) { return itemKey.startsWith("sni:"); } + static QString toSNIServicePath(QString itemKey) { return itemKey.remove("sni:"); } + +private Q_SLOTS: + void refreshIcon(); + void refreshOverlayIcon(); + void refreshAttentionIcon(); + +private: + QSize sizeHint() const Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE; + QPair serviceAndPath(const QString &servicePath); + QPixmap newIconPixmap(IconType iconType); + +private: + StatusNotifierItem *m_sniInter; + + DBusMenuImporter *m_dbusMenuImporter; + + QMenu *m_menu; + + QPixmap m_pixmap; + QPixmap m_overlayPixmap; +}; + +#endif /* SNIWIDGET_H */ diff --git a/plugins/system-tray/systemtrayplugin.cpp b/plugins/system-tray/systemtrayplugin.cpp index 4e3483b2a..168bbe340 100644 --- a/plugins/system-tray/systemtrayplugin.cpp +++ b/plugins/system-tray/systemtrayplugin.cpp @@ -4,6 +4,7 @@ * Author: sbw * * Maintainer: sbw + * listenerri * * 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 @@ -21,6 +22,7 @@ #include "systemtrayplugin.h" #include "fashiontrayitem.h" +#include "snitraywidget.h" #include #include @@ -62,6 +64,16 @@ void SystemTrayPlugin::init(PluginProxyInterface *proxyInter) m_proxyInter = proxyInter; + m_sniWatcher = new StatusNotifierWatcher(this); + QDBusConnection dbusConn = QDBusConnection::sessionBus(); + const QString &host = QString("org.kde.StatusNotifierHost-") + QString::number(qApp->applicationPid()); + dbusConn.registerService(host); + dbusConn.registerObject("/StatusNotifierHost", this); + m_sniWatcher->RegisterStatusNotifierHost(host); + + connect(m_sniWatcher, &StatusNotifierWatcher::StatusNotifierItemRegistered, this, &SystemTrayPlugin::sniItemsChanged); + connect(m_sniWatcher, &StatusNotifierWatcher::StatusNotifierItemUnregistered, this, &SystemTrayPlugin::sniItemsChanged); + connect(m_trayInter, &DBusTrayManager::TrayIconsChanged, this, &SystemTrayPlugin::trayListChanged); connect(m_trayInter, &DBusTrayManager::Changed, this, &SystemTrayPlugin::trayChanged); @@ -71,6 +83,7 @@ void SystemTrayPlugin::init(PluginProxyInterface *proxyInter) QTimer::singleShot(1, this, &SystemTrayPlugin::trayListChanged); QTimer::singleShot(2, this, &SystemTrayPlugin::loadIndicator); + QTimer::singleShot(3, this, &SystemTrayPlugin::sniItemsChanged); } void SystemTrayPlugin::displayModeChanged(const Dock::DisplayMode mode) @@ -192,6 +205,24 @@ const QString SystemTrayPlugin::getWindowClass(quint32 winId) return ret; } +void SystemTrayPlugin::sniItemsChanged() +{ + const QStringList &itemServicePaths = m_sniWatcher->RegisteredStatusNotifierItems(); + QStringList sinTrayKeyList; + + for (auto item : itemServicePaths) { + sinTrayKeyList << SNITrayWidget::toSNIKey(item); + } + for (auto itemKey : m_trayList.keys()) + if (!sinTrayKeyList.contains(itemKey) && SNITrayWidget::isSNIKey(itemKey)) { + trayRemoved(itemKey); + } + + for (auto tray : sinTrayKeyList) { + trayAdded(tray); + } +} + void SystemTrayPlugin::trayListChanged() { QList winidList = m_trayInter->trayIcons(); @@ -211,39 +242,45 @@ void SystemTrayPlugin::trayListChanged() } } +void SystemTrayPlugin::addTrayWidget(const QString &itemKey, AbstractTrayWidget * trayWidget) +{ + if (!trayWidget) { + return; + } + + if (!m_trayList.values().contains(trayWidget)) { + m_trayList.insert(itemKey, trayWidget); + } + + m_fashionItem->setMouseEnable(m_trayList.size() == 1); + if (!m_fashionItem->activeTray()) { + m_fashionItem->setActiveTray(trayWidget); + } + + if (displayMode() == Dock::Efficient) { + m_proxyInter->itemAdded(this, itemKey); + } else { + m_proxyInter->itemAdded(this, FASHION_MODE_ITEM); + } +} + void SystemTrayPlugin::trayAdded(const QString itemKey) { if (m_trayList.contains(itemKey)) { return; } - auto addTrayWidget = [ = ](AbstractTrayWidget * trayWidget) { - if (trayWidget) { - if (!m_trayList.values().contains(trayWidget)) { - m_trayList.insert(itemKey, trayWidget); - } - - m_fashionItem->setMouseEnable(m_trayList.size() == 1); - if (!m_fashionItem->activeTray()) { - m_fashionItem->setActiveTray(trayWidget); - } - - if (displayMode() == Dock::Efficient) { - m_proxyInter->itemAdded(this, itemKey); - } else { - m_proxyInter->itemAdded(this, FASHION_MODE_ITEM); - } - } - }; - if (XWindowTrayWidget::isWinIdKey(itemKey)) { auto winId = XWindowTrayWidget::toWinId(itemKey); getWindowClass(winId); AbstractTrayWidget *trayWidget = new XWindowTrayWidget(winId); - addTrayWidget(trayWidget); - } - - if (IndicatorTrayWidget::isIndicatorKey(itemKey)) { + addTrayWidget(itemKey, trayWidget); + } else if (SNITrayWidget::isSNIKey(itemKey)) { + const QString &sniServicePath = SNITrayWidget::toSNIServicePath(itemKey); + AbstractTrayWidget *trayWidget = new SNITrayWidget(sniServicePath); + connect(trayWidget, &AbstractTrayWidget::iconChanged, this, &SystemTrayPlugin::sniItemIconChanged); + addTrayWidget(itemKey, trayWidget); + } else if (IndicatorTrayWidget::isIndicatorKey(itemKey)) { IndicatorTray *trayWidget = nullptr; QString indicatorKey = IndicatorTrayWidget::toIndicatorId(itemKey); @@ -257,7 +294,7 @@ void SystemTrayPlugin::trayAdded(const QString itemKey) connect(trayWidget, &IndicatorTray::delayLoaded, trayWidget, [ = ]() { - addTrayWidget(trayWidget->widget()); + addTrayWidget(itemKey, trayWidget->widget()); }); connect(trayWidget, &IndicatorTray::removed, this, [=] { @@ -311,6 +348,20 @@ void SystemTrayPlugin::trayChanged(quint32 winId) } } +void SystemTrayPlugin::sniItemIconChanged() +{ + AbstractTrayWidget *trayWidget = static_cast(sender()); + if (!m_trayList.values().contains(trayWidget)) { + return; + } + + m_fashionItem->setActiveTray(trayWidget); + + //if (m_trayApplet->isVisible()) { + // updateTipsContent(); + //} +} + void SystemTrayPlugin::switchToMode(const Dock::DisplayMode mode) { if (mode == Dock::Fashion) { diff --git a/plugins/system-tray/systemtrayplugin.h b/plugins/system-tray/systemtrayplugin.h index 7e7eeb6d2..ae32c4a31 100644 --- a/plugins/system-tray/systemtrayplugin.h +++ b/plugins/system-tray/systemtrayplugin.h @@ -4,6 +4,7 @@ * Author: sbw * * Maintainer: sbw + * listenerri * * 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,6 +25,7 @@ #include "pluginsiteminterface.h" #include "trayapplet.h" +#include "sni/statusnotifierwatcher.h" #include "dbus/dbustraymanager.h" #include "xwindowtraywidget.h" @@ -62,16 +64,20 @@ private: void loadIndicator(); void updateTipsContent(); const QString getWindowClass(quint32 winId); + void addTrayWidget(const QString &itemKey, AbstractTrayWidget * trayWidget); private slots: + void sniItemsChanged(); void trayListChanged(); void trayAdded(const QString itemKey); void trayRemoved(const QString itemKey); void trayChanged(quint32 winId); + void sniItemIconChanged(); void switchToMode(const Dock::DisplayMode mode); private: DBusTrayManager *m_trayInter; + StatusNotifierWatcher *m_sniWatcher; FashionTrayItem *m_fashionItem; QMap m_trayList; QMap m_indicatorList;