diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6ff3e6e..42b3ca3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,7 +13,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 CONFIG REQUIRED Widgets DBus X11Extras Concurrent Svg LinguistTools QuickControls2)
find_package(KF5WindowSystem REQUIRED)
-find_package(dbusmenu-qt5 REQUIRED)
find_package(FishUI REQUIRED)
set(SRCS
@@ -34,6 +33,11 @@ set(SRCS
src/systemtray/systemtraymodel.cpp
src/systemtray/statusnotifierwatcher.cpp
+ src/libdbusmenuqt/dbusmenuimporter.cpp
+ src/libdbusmenuqt/dbusmenushortcut_p.cpp
+ src/libdbusmenuqt/dbusmenutypes_p.cpp
+ src/libdbusmenuqt/utils.cpp
+
qml.qrc
)
@@ -52,7 +56,15 @@ qt5_add_dbus_interface(SRCS ${statusnotifieritem_xml} statusnotifieritem_interfa
qt5_add_dbus_adaptor(SRCS src/systemtray/org.kde.StatusNotifierWatcher.xml
src/systemtray/statusnotifierwatcher.h StatusNotifierWatcher)
-add_executable(cutefish-statusbar ${SRCS})
+# libdbusmenuqt
+set_source_files_properties(src/libdbusmenuqt/com.canonical.dbusmenu.xml PROPERTIES
+ NO_NAMESPACE true
+ INCLUDE "src/libdbusmenuqt/dbusmenutypes_p.h"
+ CLASSNAME DBusMenuInterface
+)
+qt_add_dbus_interface(libdbusmenu_SRCS src/libdbusmenuqt/com.canonical.dbusmenu.xml dbusmenu_interface)
+
+add_executable(cutefish-statusbar ${SRCS} ${libdbusmenu_SRCS})
target_link_libraries(cutefish-statusbar
PRIVATE
@@ -68,7 +80,6 @@ target_link_libraries(cutefish-statusbar
FishUI
KF5::WindowSystem
- dbusmenu-qt5
)
file(GLOB TS_FILES translations/*.ts)
diff --git a/src/libdbusmenuqt/com.canonical.dbusmenu.xml b/src/libdbusmenuqt/com.canonical.dbusmenu.xml
new file mode 100644
index 0000000..c90776a
--- /dev/null
+++ b/src/libdbusmenuqt/com.canonical.dbusmenu.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/libdbusmenuqt/dbusmenuimporter.cpp b/src/libdbusmenuqt/dbusmenuimporter.cpp
new file mode 100644
index 0000000..fde7abc
--- /dev/null
+++ b/src/libdbusmenuqt/dbusmenuimporter.cpp
@@ -0,0 +1,549 @@
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any later
+ version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "dbusmenuimporter.h"
+
+// Qt
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Local
+#include "dbusmenushortcut_p.h"
+#include "dbusmenutypes_p.h"
+#include "utils.h"
+
+// Generated
+#include "dbusmenu_interface.h"
+
+//#define BENCHMARK
+#ifdef BENCHMARK
+static QTime sChrono;
+#endif
+
+#define DMRETURN_IF_FAIL(cond) \
+ if (!(cond)) { \
+ qCWarning() << "Condition failed: " #cond; \
+ return; \
+ }
+
+static const char *DBUSMENU_PROPERTY_ID = "_dbusmenu_id";
+static const char *DBUSMENU_PROPERTY_ICON_NAME = "_dbusmenu_icon_name";
+static const char *DBUSMENU_PROPERTY_ICON_DATA_HASH = "_dbusmenu_icon_data_hash";
+
+static QAction *createKdeTitle(QAction *action, QWidget *parent)
+{
+ QToolButton *titleWidget = new QToolButton(nullptr);
+ QFont font = titleWidget->font();
+ font.setBold(true);
+ titleWidget->setFont(font);
+ titleWidget->setIcon(action->icon());
+ titleWidget->setText(action->text());
+ titleWidget->setDown(true);
+ titleWidget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+
+ QWidgetAction *titleAction = new QWidgetAction(parent);
+ titleAction->setDefaultWidget(titleWidget);
+ return titleAction;
+}
+
+class DBusMenuImporterPrivate
+{
+public:
+ DBusMenuImporter *q;
+
+ DBusMenuInterface *m_interface;
+ QMenu *m_menu;
+ using ActionForId = QMap;
+ ActionForId m_actionForId;
+ QTimer *m_pendingLayoutUpdateTimer;
+
+ QSet m_idsRefreshedByAboutToShow;
+ QSet m_pendingLayoutUpdates;
+
+ QDBusPendingCallWatcher *refresh(int id)
+ {
+ auto call = m_interface->GetLayout(id, 1, QStringList());
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, q);
+ watcher->setProperty(DBUSMENU_PROPERTY_ID, id);
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, &DBusMenuImporter::slotGetLayoutFinished);
+
+ return watcher;
+ }
+
+ QMenu *createMenu(QWidget *parent)
+ {
+ QMenu *menu = q->createMenu(parent);
+ return menu;
+ }
+
+ /**
+ * Init all the immutable action properties here
+ * TODO: Document immutable properties?
+ *
+ * Note: we remove properties we handle from the map (using QMap::take()
+ * instead of QMap::value()) to avoid warnings about these properties in
+ * updateAction()
+ */
+ QAction *createAction(int id, const QVariantMap &_map, QWidget *parent)
+ {
+ QVariantMap map = _map;
+ QAction *action = new QAction(parent);
+ action->setProperty(DBUSMENU_PROPERTY_ID, id);
+
+ QString type = map.take(QStringLiteral("type")).toString();
+ if (type == QLatin1String("separator")) {
+ action->setSeparator(true);
+ }
+
+ if (map.take(QStringLiteral("children-display")).toString() == QLatin1String("submenu")) {
+ QMenu *menu = createMenu(parent);
+ action->setMenu(menu);
+ }
+
+ QString toggleType = map.take(QStringLiteral("toggle-type")).toString();
+ if (!toggleType.isEmpty()) {
+ action->setCheckable(true);
+ if (toggleType == QLatin1String("radio")) {
+ QActionGroup *group = new QActionGroup(action);
+ group->addAction(action);
+ }
+ }
+
+ bool isKdeTitle = map.take(QStringLiteral("x-kde-title")).toBool();
+ updateAction(action, map, map.keys());
+
+ if (isKdeTitle) {
+ action = createKdeTitle(action, parent);
+ }
+
+ return action;
+ }
+
+ /**
+ * Update mutable properties of an action. A property may be listed in
+ * requestedProperties but not in map, this means we should use the default value
+ * for this property.
+ *
+ * @param action the action to update
+ * @param map holds the property values
+ * @param requestedProperties which properties has been requested
+ */
+ void updateAction(QAction *action, const QVariantMap &map, const QStringList &requestedProperties)
+ {
+ Q_FOREACH (const QString &key, requestedProperties) {
+ updateActionProperty(action, key, map.value(key));
+ }
+ }
+
+ void updateActionProperty(QAction *action, const QString &key, const QVariant &value)
+ {
+ if (key == QLatin1String("label")) {
+ updateActionLabel(action, value);
+ } else if (key == QLatin1String("enabled")) {
+ updateActionEnabled(action, value);
+ } else if (key == QLatin1String("toggle-state")) {
+ updateActionChecked(action, value);
+ } else if (key == QLatin1String("icon-name")) {
+ updateActionIconByName(action, value);
+ } else if (key == QLatin1String("icon-data")) {
+ updateActionIconByData(action, value);
+ } else if (key == QLatin1String("visible")) {
+ updateActionVisible(action, value);
+ } else if (key == QLatin1String("shortcut")) {
+ updateActionShortcut(action, value);
+ } else {
+ qDebug() << "Unhandled property update" << key;
+ }
+ }
+
+ void updateActionLabel(QAction *action, const QVariant &value)
+ {
+ QString text = swapMnemonicChar(value.toString(), '_', '&');
+ action->setText(text);
+ }
+
+ void updateActionEnabled(QAction *action, const QVariant &value)
+ {
+ action->setEnabled(value.isValid() ? value.toBool() : true);
+ }
+
+ void updateActionChecked(QAction *action, const QVariant &value)
+ {
+ if (action->isCheckable() && value.isValid()) {
+ action->setChecked(value.toInt() == 1);
+ }
+ }
+
+ void updateActionIconByName(QAction *action, const QVariant &value)
+ {
+ const QString iconName = value.toString();
+ const QString previous = action->property(DBUSMENU_PROPERTY_ICON_NAME).toString();
+ if (previous == iconName) {
+ return;
+ }
+ action->setProperty(DBUSMENU_PROPERTY_ICON_NAME, iconName);
+ if (iconName.isEmpty()) {
+ action->setIcon(QIcon());
+ return;
+ }
+ action->setIcon(q->iconForName(iconName));
+ }
+
+ void updateActionIconByData(QAction *action, const QVariant &value)
+ {
+ const QByteArray data = value.toByteArray();
+ uint dataHash = qHash(data);
+ uint previousDataHash = action->property(DBUSMENU_PROPERTY_ICON_DATA_HASH).toUInt();
+ if (previousDataHash == dataHash) {
+ return;
+ }
+ action->setProperty(DBUSMENU_PROPERTY_ICON_DATA_HASH, dataHash);
+ QPixmap pix;
+ if (!pix.loadFromData(data)) {
+ qDebug() << "Failed to decode icon-data property for action" << action->text();
+ action->setIcon(QIcon());
+ return;
+ }
+ action->setIcon(QIcon(pix));
+ }
+
+ void updateActionVisible(QAction *action, const QVariant &value)
+ {
+ action->setVisible(value.isValid() ? value.toBool() : true);
+ }
+
+ void updateActionShortcut(QAction *action, const QVariant &value)
+ {
+ QDBusArgument arg = value.value();
+ DBusMenuShortcut dmShortcut;
+ arg >> dmShortcut;
+ QKeySequence keySequence = dmShortcut.toKeySequence();
+ action->setShortcut(keySequence);
+ }
+
+ QMenu *menuForId(int id) const
+ {
+ if (id == 0) {
+ return q->menu();
+ }
+ QAction *action = m_actionForId.value(id);
+ if (!action) {
+ return nullptr;
+ }
+ return action->menu();
+ }
+
+ void slotItemsPropertiesUpdated(const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList);
+
+ void sendEvent(int id, const QString &eventId)
+ {
+ m_interface->Event(id, eventId, QDBusVariant(QString()), 0u);
+ }
+};
+
+DBusMenuImporter::DBusMenuImporter(const QString &service, const QString &path, QObject *parent)
+ : QObject(parent)
+ , d(new DBusMenuImporterPrivate)
+{
+ DBusMenuTypes_register();
+
+ d->q = this;
+ d->m_interface = new DBusMenuInterface(service, path, QDBusConnection::sessionBus(), this);
+ d->m_menu = nullptr;
+
+ d->m_pendingLayoutUpdateTimer = new QTimer(this);
+ d->m_pendingLayoutUpdateTimer->setSingleShot(true);
+ connect(d->m_pendingLayoutUpdateTimer, &QTimer::timeout, this, &DBusMenuImporter::processPendingLayoutUpdates);
+
+ connect(d->m_interface, &DBusMenuInterface::LayoutUpdated, this, &DBusMenuImporter::slotLayoutUpdated);
+ connect(d->m_interface, &DBusMenuInterface::ItemActivationRequested, this, &DBusMenuImporter::slotItemActivationRequested);
+ connect(d->m_interface,
+ &DBusMenuInterface::ItemsPropertiesUpdated,
+ this,
+ [this](const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList) {
+ d->slotItemsPropertiesUpdated(updatedList, removedList);
+ });
+
+ d->refresh(0);
+}
+
+DBusMenuImporter::~DBusMenuImporter()
+{
+ // Do not use "delete d->m_menu": even if we are being deleted we should
+ // leave enough time for the menu to finish what it was doing, for example
+ // if it was being displayed.
+ d->m_menu->deleteLater();
+ delete d;
+}
+
+void DBusMenuImporter::slotLayoutUpdated(uint revision, int parentId)
+{
+ Q_UNUSED(revision)
+ if (d->m_idsRefreshedByAboutToShow.remove(parentId)) {
+ return;
+ }
+ d->m_pendingLayoutUpdates << parentId;
+ if (!d->m_pendingLayoutUpdateTimer->isActive()) {
+ d->m_pendingLayoutUpdateTimer->start();
+ }
+}
+
+void DBusMenuImporter::processPendingLayoutUpdates()
+{
+ QSet ids = d->m_pendingLayoutUpdates;
+ d->m_pendingLayoutUpdates.clear();
+ Q_FOREACH (int id, ids) {
+ d->refresh(id);
+ }
+}
+
+QMenu *DBusMenuImporter::menu() const
+{
+ if (!d->m_menu) {
+ d->m_menu = d->createMenu(nullptr);
+ }
+ return d->m_menu;
+}
+
+void DBusMenuImporterPrivate::slotItemsPropertiesUpdated(const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList)
+{
+ Q_FOREACH (const DBusMenuItem &item, updatedList) {
+ QAction *action = m_actionForId.value(item.id);
+ if (!action) {
+ // We don't know this action. It probably is in a menu we haven't fetched yet.
+ continue;
+ }
+
+ QVariantMap::ConstIterator it = item.properties.constBegin(), end = item.properties.constEnd();
+ for (; it != end; ++it) {
+ updateActionProperty(action, it.key(), it.value());
+ }
+ }
+
+ Q_FOREACH (const DBusMenuItemKeys &item, removedList) {
+ QAction *action = m_actionForId.value(item.id);
+ if (!action) {
+ // We don't know this action. It probably is in a menu we haven't fetched yet.
+ continue;
+ }
+
+ Q_FOREACH (const QString &key, item.properties) {
+ updateActionProperty(action, key, QVariant());
+ }
+ }
+}
+
+QAction *DBusMenuImporter::actionForId(int id) const
+{
+ return d->m_actionForId.value(id);
+}
+
+void DBusMenuImporter::slotItemActivationRequested(int id, uint /*timestamp*/)
+{
+ QAction *action = d->m_actionForId.value(id);
+ // DMRETURN_IF_FAIL(action);
+ actionActivationRequested(action);
+}
+
+void DBusMenuImporter::slotGetLayoutFinished(QDBusPendingCallWatcher *watcher)
+{
+ int parentId = watcher->property(DBUSMENU_PROPERTY_ID).toInt();
+ watcher->deleteLater();
+
+ QMenu *menu = d->menuForId(parentId);
+
+ QDBusPendingReply reply = *watcher;
+ if (!reply.isValid()) {
+ qDebug() << reply.error().message();
+ if (menu) {
+ emit menuUpdated(menu);
+ }
+ return;
+ }
+
+#ifdef BENCHMARK
+ DMDEBUG << "- items received:" << sChrono.elapsed() << "ms";
+#endif
+ DBusMenuLayoutItem rootItem = reply.argumentAt<1>();
+
+ if (!menu) {
+ qDebug() << "No menu for id" << parentId;
+ return;
+ }
+
+ // remove outdated actions
+ QSet newDBusMenuItemIds;
+ newDBusMenuItemIds.reserve(rootItem.children.count());
+ for (const DBusMenuLayoutItem &item : qAsConst(rootItem.children)) {
+ newDBusMenuItemIds << item.id;
+ }
+ for (QAction *action : menu->actions()) {
+ int id = action->property(DBUSMENU_PROPERTY_ID).toInt();
+ if (!newDBusMenuItemIds.contains(id)) {
+ // Not calling removeAction() as QMenu will immediately close when it becomes empty,
+ // which can happen when an application completely reloads this menu.
+ // When the action is deleted deferred, it is removed from the menu.
+ action->deleteLater();
+ if (action->menu()) {
+ action->menu()->deleteLater();
+ }
+ d->m_actionForId.remove(id);
+ }
+ }
+
+ // insert or update new actions into our menu
+ for (const DBusMenuLayoutItem &dbusMenuItem : qAsConst(rootItem.children)) {
+ DBusMenuImporterPrivate::ActionForId::Iterator it = d->m_actionForId.find(dbusMenuItem.id);
+ QAction *action = nullptr;
+ if (it == d->m_actionForId.end()) {
+ int id = dbusMenuItem.id;
+ action = d->createAction(id, dbusMenuItem.properties, menu);
+ d->m_actionForId.insert(id, action);
+
+ connect(action, &QObject::destroyed, this, [this, id]() {
+ d->m_actionForId.remove(id);
+ });
+
+ connect(action, &QAction::triggered, this, [id, this]() {
+ sendClickedEvent(id);
+ });
+
+ if (QMenu *menuAction = action->menu()) {
+ connect(menuAction, &QMenu::aboutToShow, this, &DBusMenuImporter::slotMenuAboutToShow, Qt::UniqueConnection);
+ }
+ connect(menu, &QMenu::aboutToHide, this, &DBusMenuImporter::slotMenuAboutToHide, Qt::UniqueConnection);
+
+ menu->addAction(action);
+ } else {
+ action = *it;
+ QStringList filteredKeys = dbusMenuItem.properties.keys();
+ filteredKeys.removeOne("type");
+ filteredKeys.removeOne("toggle-type");
+ filteredKeys.removeOne("children-display");
+ d->updateAction(*it, dbusMenuItem.properties, filteredKeys);
+ // Move the action to the tail so we can keep the order same as the dbus request.
+ menu->removeAction(action);
+ menu->addAction(action);
+ }
+ }
+
+ emit menuUpdated(menu);
+}
+
+void DBusMenuImporter::sendClickedEvent(int id)
+{
+ d->sendEvent(id, QStringLiteral("clicked"));
+}
+
+void DBusMenuImporter::updateMenu()
+{
+ updateMenu(DBusMenuImporter::menu());
+}
+
+void DBusMenuImporter::updateMenu(QMenu *menu)
+{
+ Q_ASSERT(menu);
+
+ QAction *action = menu->menuAction();
+ Q_ASSERT(action);
+
+ int id = action->property(DBUSMENU_PROPERTY_ID).toInt();
+
+ auto call = d->m_interface->AboutToShow(id);
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
+ watcher->setProperty(DBUSMENU_PROPERTY_ID, id);
+ connect(watcher, &QDBusPendingCallWatcher::finished, this, &DBusMenuImporter::slotAboutToShowDBusCallFinished);
+
+ // Firefox deliberately ignores "aboutToShow" whereas Qt ignores" opened", so we'll just send both all the time...
+ d->sendEvent(id, QStringLiteral("opened"));
+}
+
+void DBusMenuImporter::slotAboutToShowDBusCallFinished(QDBusPendingCallWatcher *watcher)
+{
+ int id = watcher->property(DBUSMENU_PROPERTY_ID).toInt();
+ watcher->deleteLater();
+
+ QMenu *menu = d->menuForId(id);
+ if (!menu) {
+ return;
+ }
+
+ QDBusPendingReply reply = *watcher;
+ if (reply.isError()) {
+ qDebug() << "Call to AboutToShow() failed:" << reply.error().message();
+ Q_EMIT menuUpdated(menu);
+ return;
+ }
+ // Note, this isn't used by Qt's QPT - but we get a LayoutChanged emitted before
+ // this returns, which equates to the same thing
+ bool needRefresh = reply.argumentAt<0>();
+
+ if (needRefresh || menu->actions().isEmpty()) {
+ d->m_idsRefreshedByAboutToShow << id;
+ d->refresh(id);
+ } else if (menu) {
+ Q_EMIT menuUpdated(menu);
+ }
+}
+
+void DBusMenuImporter::slotMenuAboutToHide()
+{
+ QMenu *menu = qobject_cast(sender());
+ Q_ASSERT(menu);
+
+ QAction *action = menu->menuAction();
+ Q_ASSERT(action);
+
+ int id = action->property(DBUSMENU_PROPERTY_ID).toInt();
+ d->sendEvent(id, QStringLiteral("closed"));
+}
+
+void DBusMenuImporter::slotMenuAboutToShow()
+{
+ QMenu *menu = qobject_cast(sender());
+ Q_ASSERT(menu);
+
+ updateMenu(menu);
+}
+
+QMenu *DBusMenuImporter::createMenu(QWidget *parent)
+{
+ return new QMenu(parent);
+}
+
+QIcon DBusMenuImporter::iconForName(const QString & /*name*/)
+{
+ return QIcon();
+}
+
+#include "moc_dbusmenuimporter.cpp"
diff --git a/src/libdbusmenuqt/dbusmenuimporter.h b/src/libdbusmenuqt/dbusmenuimporter.h
new file mode 100644
index 0000000..ff5c18f
--- /dev/null
+++ b/src/libdbusmenuqt/dbusmenuimporter.h
@@ -0,0 +1,111 @@
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any later
+ version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUIMPORTER_H
+#define DBUSMENUIMPORTER_H
+
+// Qt
+#include
+
+class QAction;
+class QDBusPendingCallWatcher;
+class QIcon;
+class QMenu;
+
+class DBusMenuImporterPrivate;
+
+/**
+ * A DBusMenuImporter instance can recreate a menu serialized over DBus by
+ * DBusMenuExporter
+ */
+class DBusMenuImporter : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Creates a DBusMenuImporter listening over DBus on service, path
+ */
+ DBusMenuImporter(const QString &service, const QString &path, QObject *parent = nullptr);
+
+ ~DBusMenuImporter() override;
+
+ QAction *actionForId(int id) const;
+
+ /**
+ * The menu created from listening to the DBusMenuExporter over DBus
+ */
+ QMenu *menu() const;
+
+public Q_SLOTS:
+ /**
+ * Load the menu
+ *
+ * Will emit menuUpdated() when complete.
+ * This should be done before showing a menu
+ */
+ void updateMenu();
+
+ void updateMenu(QMenu *menu);
+
+Q_SIGNALS:
+ /**
+ * Emitted after a call to updateMenu().
+ * @see updateMenu()
+ */
+ void menuUpdated(QMenu *);
+
+ /**
+ * Emitted when the exporter was asked to activate an action
+ */
+ void actionActivationRequested(QAction *);
+
+protected:
+ /**
+ * Must create a menu, may be customized to fit host appearance.
+ * Default implementation creates a simple QMenu.
+ */
+ virtual QMenu *createMenu(QWidget *parent);
+
+ /**
+ * Must convert a name into an icon.
+ * Default implementation returns a null icon.
+ */
+ virtual QIcon iconForName(const QString &);
+
+private Q_SLOTS:
+ void sendClickedEvent(int);
+ void slotMenuAboutToShow();
+ void slotMenuAboutToHide();
+ void slotAboutToShowDBusCallFinished(QDBusPendingCallWatcher *);
+ void slotItemActivationRequested(int id, uint timestamp);
+ void processPendingLayoutUpdates();
+ void slotLayoutUpdated(uint revision, int parentId);
+ void slotGetLayoutFinished(QDBusPendingCallWatcher *);
+
+private:
+ Q_DISABLE_COPY(DBusMenuImporter)
+ DBusMenuImporterPrivate *const d;
+ friend class DBusMenuImporterPrivate;
+
+ // Use Q_PRIVATE_SLOT to avoid exposing DBusMenuItemList
+ Q_PRIVATE_SLOT(d, void slotItemsPropertiesUpdated(const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList))
+};
+
+#endif /* DBUSMENUIMPORTER_H */
diff --git a/src/libdbusmenuqt/dbusmenushortcut_p.cpp b/src/libdbusmenuqt/dbusmenushortcut_p.cpp
new file mode 100644
index 0000000..e67fc0f
--- /dev/null
+++ b/src/libdbusmenuqt/dbusmenushortcut_p.cpp
@@ -0,0 +1,84 @@
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any later
+ version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "dbusmenushortcut_p.h"
+
+// Qt
+#include
+
+static const int QT_COLUMN = 0;
+static const int DM_COLUMN = 1;
+
+static void processKeyTokens(QStringList *tokens, int srcCol, int dstCol)
+{
+ struct Row {
+ const char *zero;
+ const char *one;
+ const char *operator[](int col) const
+ {
+ return col == 0 ? zero : one;
+ }
+ };
+ static const Row table[] = {{"Meta", "Super"},
+ {"Ctrl", "Control"},
+ // Special cases for compatibility with libdbusmenu-glib which uses
+ // "plus" for "+" and "minus" for "-".
+ // cf https://bugs.launchpad.net/libdbusmenu-qt/+bug/712565
+ {"+", "plus"},
+ {"-", "minus"},
+ {nullptr, nullptr}};
+
+ const Row *ptr = table;
+ for (; ptr->zero != nullptr; ++ptr) {
+ const char *from = (*ptr)[srcCol];
+ const char *to = (*ptr)[dstCol];
+ tokens->replaceInStrings(from, to);
+ }
+}
+
+DBusMenuShortcut DBusMenuShortcut::fromKeySequence(const QKeySequence &sequence)
+{
+ QString string = sequence.toString();
+ DBusMenuShortcut shortcut;
+ QStringList tokens = string.split(QStringLiteral(", "));
+ Q_FOREACH (QString token, tokens) {
+ // Hack: Qt::CTRL | Qt::Key_Plus is turned into the string "Ctrl++",
+ // but we don't want the call to token.split() to consider the
+ // second '+' as a separator so we replace it with its final value.
+ token.replace(QLatin1String("++"), QLatin1String("+plus"));
+ QStringList keyTokens = token.split('+');
+ processKeyTokens(&keyTokens, QT_COLUMN, DM_COLUMN);
+ shortcut << keyTokens;
+ }
+ return shortcut;
+}
+
+QKeySequence DBusMenuShortcut::toKeySequence() const
+{
+ QStringList tmp;
+ Q_FOREACH (const QStringList &keyTokens_, *this) {
+ QStringList keyTokens = keyTokens_;
+ processKeyTokens(&keyTokens, DM_COLUMN, QT_COLUMN);
+ tmp << keyTokens.join(QLatin1String("+"));
+ }
+ QString string = tmp.join(QLatin1String(", "));
+ return QKeySequence::fromString(string);
+}
diff --git a/src/libdbusmenuqt/dbusmenushortcut_p.h b/src/libdbusmenuqt/dbusmenushortcut_p.h
new file mode 100644
index 0000000..ca06e7a
--- /dev/null
+++ b/src/libdbusmenuqt/dbusmenushortcut_p.h
@@ -0,0 +1,39 @@
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any later
+ version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUSHORTCUT_H
+#define DBUSMENUSHORTCUT_H
+
+// Qt
+#include
+#include
+
+class QKeySequence;
+
+class DBusMenuShortcut : public QList
+{
+public:
+ QKeySequence toKeySequence() const;
+ static DBusMenuShortcut fromKeySequence(const QKeySequence &);
+};
+
+Q_DECLARE_METATYPE(DBusMenuShortcut)
+
+#endif /* DBUSMENUSHORTCUT_H */
diff --git a/src/libdbusmenuqt/dbusmenutypes_p.cpp b/src/libdbusmenuqt/dbusmenutypes_p.cpp
new file mode 100644
index 0000000..eb16a82
--- /dev/null
+++ b/src/libdbusmenuqt/dbusmenutypes_p.cpp
@@ -0,0 +1,136 @@
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any later
+ version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "dbusmenutypes_p.h"
+
+// Local
+#include "dbusmenushortcut_p.h"
+
+// Qt
+#include
+#include
+
+//// DBusMenuItem
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItem &obj)
+{
+ argument.beginStructure();
+ argument << obj.id << obj.properties;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItem &obj)
+{
+ argument.beginStructure();
+ argument >> obj.id >> obj.properties;
+ argument.endStructure();
+ return argument;
+}
+
+//// DBusMenuItemKeys
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItemKeys &obj)
+{
+ argument.beginStructure();
+ argument << obj.id << obj.properties;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItemKeys &obj)
+{
+ argument.beginStructure();
+ argument >> obj.id >> obj.properties;
+ argument.endStructure();
+ return argument;
+}
+
+//// DBusMenuLayoutItem
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuLayoutItem &obj)
+{
+ argument.beginStructure();
+ argument << obj.id << obj.properties;
+ argument.beginArray(qMetaTypeId());
+ Q_FOREACH (const DBusMenuLayoutItem &child, obj.children) {
+ argument << QDBusVariant(QVariant::fromValue(child));
+ }
+ argument.endArray();
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuLayoutItem &obj)
+{
+ argument.beginStructure();
+ argument >> obj.id >> obj.properties;
+ argument.beginArray();
+ while (!argument.atEnd()) {
+ QDBusVariant dbusVariant;
+ argument >> dbusVariant;
+ QDBusArgument childArgument = dbusVariant.variant().value();
+
+ DBusMenuLayoutItem child;
+ childArgument >> child;
+ obj.children.append(child);
+ }
+ argument.endArray();
+ argument.endStructure();
+ return argument;
+}
+
+//// DBusMenuShortcut
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuShortcut &obj)
+{
+ argument.beginArray(qMetaTypeId());
+ typename QList::ConstIterator it = obj.constBegin();
+ typename QList::ConstIterator end = obj.constEnd();
+ for (; it != end; ++it)
+ argument << *it;
+ argument.endArray();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuShortcut &obj)
+{
+ argument.beginArray();
+ obj.clear();
+ while (!argument.atEnd()) {
+ QStringList item;
+ argument >> item;
+ obj.push_back(item);
+ }
+ argument.endArray();
+ return argument;
+}
+
+void DBusMenuTypes_register()
+{
+ static bool registered = false;
+ if (registered) {
+ return;
+ }
+ qDBusRegisterMetaType();
+ qDBusRegisterMetaType();
+ qDBusRegisterMetaType();
+ qDBusRegisterMetaType();
+ qDBusRegisterMetaType();
+ qDBusRegisterMetaType();
+ qDBusRegisterMetaType();
+ registered = true;
+}
diff --git a/src/libdbusmenuqt/dbusmenutypes_p.h b/src/libdbusmenuqt/dbusmenutypes_p.h
new file mode 100644
index 0000000..28739cf
--- /dev/null
+++ b/src/libdbusmenuqt/dbusmenutypes_p.h
@@ -0,0 +1,96 @@
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any later
+ version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUTYPES_P_H
+#define DBUSMENUTYPES_P_H
+
+// Qt
+#include
+#include
+#include
+
+class QDBusArgument;
+
+//// DBusMenuItem
+/**
+ * Internal struct used to communicate on DBus
+ */
+struct DBusMenuItem {
+ int id;
+ QVariantMap properties;
+};
+
+Q_DECLARE_METATYPE(DBusMenuItem)
+
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItem &item);
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItem &item);
+
+typedef QList DBusMenuItemList;
+
+Q_DECLARE_METATYPE(DBusMenuItemList)
+
+//// DBusMenuItemKeys
+/**
+ * Represents a list of keys for a menu item
+ */
+struct DBusMenuItemKeys {
+ int id;
+ QStringList properties;
+};
+
+Q_DECLARE_METATYPE(DBusMenuItemKeys)
+
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItemKeys &);
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItemKeys &);
+
+typedef QList DBusMenuItemKeysList;
+
+Q_DECLARE_METATYPE(DBusMenuItemKeysList)
+
+//// DBusMenuLayoutItem
+/**
+ * Represents an item with its children. GetLayout() returns a
+ * DBusMenuLayoutItemList.
+ */
+struct DBusMenuLayoutItem;
+struct DBusMenuLayoutItem {
+ int id;
+ QVariantMap properties;
+ QList children;
+};
+
+Q_DECLARE_METATYPE(DBusMenuLayoutItem)
+
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuLayoutItem &);
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuLayoutItem &);
+
+typedef QList DBusMenuLayoutItemList;
+
+Q_DECLARE_METATYPE(DBusMenuLayoutItemList)
+
+//// DBusMenuShortcut
+
+class DBusMenuShortcut;
+
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuShortcut &);
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuShortcut &);
+
+void DBusMenuTypes_register();
+#endif /* DBUSMENUTYPES_P_H */
diff --git a/src/libdbusmenuqt/utils.cpp b/src/libdbusmenuqt/utils.cpp
new file mode 100644
index 0000000..ec83c04
--- /dev/null
+++ b/src/libdbusmenuqt/utils.cpp
@@ -0,0 +1,65 @@
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any later
+ version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "utils.h"
+
+// Qt
+#include
+
+QString swapMnemonicChar(const QString &in, const char src, const char dst)
+{
+ QString out;
+ bool mnemonicFound = false;
+
+ for (int pos = 0; pos < in.length();) {
+ QChar ch = in[pos];
+ if (ch == src) {
+ if (pos == in.length() - 1) {
+ // 'src' at the end of string, skip it
+ ++pos;
+ } else {
+ if (in[pos + 1] == src) {
+ // A real 'src'
+ out += src;
+ pos += 2;
+ } else if (!mnemonicFound) {
+ // We found the mnemonic
+ mnemonicFound = true;
+ out += dst;
+ ++pos;
+ } else {
+ // We already have a mnemonic, just skip the char
+ ++pos;
+ }
+ }
+ } else if (ch == dst) {
+ // Escape 'dst'
+ out += dst;
+ out += dst;
+ ++pos;
+ } else {
+ out += ch;
+ ++pos;
+ }
+ }
+
+ return out;
+}
diff --git a/src/libdbusmenuqt/utils.h b/src/libdbusmenuqt/utils.h
new file mode 100644
index 0000000..329443b
--- /dev/null
+++ b/src/libdbusmenuqt/utils.h
@@ -0,0 +1,31 @@
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any later
+ version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef UTILS_H
+#define UTILS_H
+
+class QString;
+
+/**
+ * Swap mnemonic char: Qt uses '&', while dbusmenu uses '_'
+ */
+QString swapMnemonicChar(const QString &in, const char src, const char dst);
+
+#endif /* UTILS_P_H */
diff --git a/src/systemtray/statusnotifieritemsource.cpp b/src/systemtray/statusnotifieritemsource.cpp
index a6df55e..652e774 100644
--- a/src/systemtray/statusnotifieritemsource.cpp
+++ b/src/systemtray/statusnotifieritemsource.cpp
@@ -1,8 +1,31 @@
+/***************************************************************************
+ * *
+ * Copyright (C) 2021 Reion Wong *
+ * Copyright (C) 2009 Marco Martin *
+ * Copyright (C) 2009 Matthieu Gallien *
+ * *
+ * 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 "statusnotifieritemsource.h"
#include "systemtraytypes.h"
+#include "../libdbusmenuqt/dbusmenuimporter.h"
+
#include
-#include
#include
class MenuImporter : public DBusMenuImporter
@@ -67,7 +90,8 @@ StatusNotifierItemSource::StatusNotifierItemSource(const QString ¬ifierItemId
StatusNotifierItemSource::~StatusNotifierItemSource()
{
- delete m_statusNotifierItemInterface;
+ if (m_statusNotifierItemInterface)
+ delete m_statusNotifierItemInterface;
}
QString StatusNotifierItemSource::id() const
@@ -133,10 +157,10 @@ void StatusNotifierItemSource::contextMenu(int x, int y)
{
if (m_menuImporter) {
// Popup menu
- if (m_menuImporter->menu()) {
- m_menuImporter->updateMenu();
+ m_menuImporter->updateMenu();
+
+ if (m_menuImporter->menu())
m_menuImporter->menu()->popup(QPoint(x, y));
- }
} else {
qWarning() << "Could not find DBusMenu interface, falling back to calling ContextMenu()";
if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
@@ -245,6 +269,11 @@ void StatusNotifierItemSource::refreshCallback(QDBusPendingCallWatcher *call)
} else {
m_menuImporter = new MenuImporter(m_statusNotifierItemInterface->service(),
menuObjectPath, this);
+ connect(m_menuImporter, &MenuImporter::menuUpdated, this, [this](QMenu *menu) {
+ if (menu == m_menuImporter->menu()) {
+ contextMenuReady();
+ }
+ });
}
}
}
diff --git a/src/systemtray/statusnotifieritemsource.h b/src/systemtray/statusnotifieritemsource.h
index 166fdca..6bde5be 100644
--- a/src/systemtray/statusnotifieritemsource.h
+++ b/src/systemtray/statusnotifieritemsource.h
@@ -1,3 +1,25 @@
+/***************************************************************************
+ * *
+ * Copyright (C) 2021 Reion Wong *
+ * Copyright (C) 2009 Marco Martin *
+ * Copyright (C) 2009 Matthieu Gallien *
+ * *
+ * 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 STATUSNOTIFIERITEMSOURCE_H
#define STATUSNOTIFIERITEMSOURCE_H
diff --git a/src/systemtray/statusnotifierwatcher.cpp b/src/systemtray/statusnotifierwatcher.cpp
index 90f501d..ba6ffca 100644
--- a/src/systemtray/statusnotifierwatcher.cpp
+++ b/src/systemtray/statusnotifierwatcher.cpp
@@ -1,3 +1,25 @@
+/***************************************************************************
+ * *
+ * Copyright (C) 2021 Reion Wong *
+ * Copyright (C) 2009 Marco Martin *
+ * Copyright (C) 2009 Matthieu Gallien *
+ * *
+ * 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 "statusnotifierwatcher.h"
#include "statusnotifieritem_interface.h"
#include "statusnotifierwatcheradaptor.h"
diff --git a/src/systemtray/statusnotifierwatcher.h b/src/systemtray/statusnotifierwatcher.h
index 7376f8e..c357f2b 100644
--- a/src/systemtray/statusnotifierwatcher.h
+++ b/src/systemtray/statusnotifierwatcher.h
@@ -1,3 +1,25 @@
+/***************************************************************************
+ * *
+ * Copyright (C) 2021 Reion Wong *
+ * Copyright (C) 2009 Marco Martin *
+ * Copyright (C) 2009 Matthieu Gallien *
+ * *
+ * 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 STATUSNOTIFIERWATCHER_H
#define STATUSNOTIFIERWATCHER_H
diff --git a/src/systemtray/systemtraymodel.cpp b/src/systemtray/systemtraymodel.cpp
index 1bd22c5..c6dc9e0 100644
--- a/src/systemtray/systemtraymodel.cpp
+++ b/src/systemtray/systemtraymodel.cpp
@@ -1,3 +1,23 @@
+/***************************************************************************
+ * Copyright (C) 2021 Reion Wong *
+ * Copyright (C) 2020 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 "systemtraymodel.h"
#include
diff --git a/src/systemtray/systemtraymodel.h b/src/systemtray/systemtraymodel.h
index 3142e2c..291778b 100644
--- a/src/systemtray/systemtraymodel.h
+++ b/src/systemtray/systemtraymodel.h
@@ -1,3 +1,23 @@
+/***************************************************************************
+ * Copyright (C) 2021 Reion Wong *
+ * Copyright (C) 2020 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 SYSTEMTRAYMODEL_H
#define SYSTEMTRAYMODEL_H