diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..ab54f21
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "files.associations": {
+ "*.moc": "cpp"
+ }
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 22c94d4..6e8bfed 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,7 +10,7 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(QT Core Widgets Concurrent Quick QuickControls2 X11Extras DBus LinguistTools)
+set(QT Core Widgets Concurrent Quick QuickControls2 X11Extras DBus Svg LinguistTools)
find_package(Qt5 REQUIRED ${QT})
find_package(KF5WindowSystem REQUIRED)
find_package(dbusmenu-qt5 REQUIRED)
@@ -36,19 +36,36 @@ set(SRCS
src/appearance.cpp
src/fakewindow.cpp
+ src/iconitem.cpp
+ src/managedtexturenode.cpp
- src/statusnotifier/dbustypes.cpp
- src/statusnotifier/sniasync.cpp
- src/statusnotifier/statusnotifieriteminterface.cpp
- src/statusnotifier/statusnotifiermodel.cpp
- src/statusnotifier/statusnotifierwatcher.cpp
- src/statusnotifier/statusnotifieritemsource.cpp
+ src/systemtray/statusnotifieritemjob.cpp
+ src/systemtray/statusnotifieritemsource.cpp
+ src/systemtray/systemtraytypes.cpp
+ src/systemtray/systemtraytypedefs.h
+ src/systemtray/systemtraymodel.cpp
+ src/systemtray/statusnotifierwatcher.cpp
)
set(RESOURCES
resources.qrc
)
+set(statusnotifierwatcher_xml src/systemtray/org.kde.StatusNotifierWatcher.xml)
+qt5_add_dbus_interface(SRCS ${statusnotifierwatcher_xml} statusnotifierwatcher_interface)
+qt5_add_dbus_interface(SRCS src/systemtray/org.freedesktop.DBus.Properties.xml dbusproperties)
+
+set(statusnotifieritem_xml src/systemtray/org.kde.StatusNotifierItem.xml)
+set_source_files_properties(${statusnotifieritem_xml} PROPERTIES
+ NO_NAMESPACE false
+ INCLUDE "src/systemtray/systemtraytypes.h"
+ CLASSNAME OrgKdeStatusNotifierItem
+)
+qt5_add_dbus_interface(SRCS ${statusnotifieritem_xml} statusnotifieritem_interface)
+
+qt5_add_dbus_adaptor(SRCS src/systemtray/org.kde.StatusNotifierWatcher.xml
+ src/systemtray/statusnotifierwatcher.h StatusNotifierWatcher)
+
add_executable(${PROJECT_NAME} ${SRCS} ${DBUS_SRCS} ${RESOURCES})
target_link_libraries(${PROJECT_NAME}
Qt5::Core
@@ -58,6 +75,7 @@ target_link_libraries(${PROJECT_NAME}
Qt5::X11Extras
Qt5::Concurrent
Qt5::DBus
+ Qt5::Svg
MeuiKit
diff --git a/qml/DockItem.qml b/qml/DockItem.qml
index 7b4f5a4..35ba04a 100644
--- a/qml/DockItem.qml
+++ b/qml/DockItem.qml
@@ -50,18 +50,12 @@ Item {
dragStarted = false
}
- Image {
+ IconItem {
id: icon
anchors.centerIn: parent
- source: iconName ? iconName.indexOf("/") === 0 || iconName.indexOf("file://") === 0 || iconName.indexOf("qrc") === 0
- ? iconName : "image://icontheme/" + iconName : iconName
- sourceSize.width: control.iconSize
- sourceSize.height: control.iconSize
- width: sourceSize.width
- height: sourceSize.height
- asynchronous: false
- smooth: true
- cache: true
+ width: control.iconSize
+ height: control.iconSize
+ source: iconName
visible: !dragStarted
diff --git a/qml/StatusNotifierItem.qml b/qml/StatusNotifierItem.qml
new file mode 100644
index 0000000..af62393
--- /dev/null
+++ b/qml/StatusNotifierItem.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.12
+
+Loader {
+ sourceComponent: _item
+
+ Component {
+ id: _item
+ }
+}
diff --git a/qml/main.qml b/qml/main.qml
index 1354bd4..09dac29 100644
--- a/qml/main.qml
+++ b/qml/main.qml
@@ -6,6 +6,7 @@ import QtGraphicalEffects 1.0
import Cyber.NetworkManagement 1.0 as NM
import Cutefish.Dock 1.0
import MeuiKit 1.0 as Meui
+import org.kde.plasma.core 2.0 as PlasmaCore
Item {
id: root
@@ -71,7 +72,7 @@ Item {
Behavior on color {
ColorAnimation {
- duration: 0
+ duration: 200
easing.type: Easing.InOutQuad
}
}
@@ -168,34 +169,22 @@ Item {
orientation: isHorizontal ? Qt.Horizontal : Qt.Vertical
layoutDirection: Qt.RightToLeft
interactive: false
- model: trayModel
+ model: SystemTrayModel { id: trayModel }
spacing: Meui.Units.smallSpacing / 2
clip: true
- StatusNotifierModel {
- id: trayModel
- }
-
onCountChanged: delayCalcIconSize()
delegate: StandardItem {
height: trayView.itemHeight
width: trayView.itemWidth
- Image {
- id: trayIcon
+ IconItem {
+ id: iconItem
anchors.centerIn: parent
-// source: iconName ? "image://icontheme/" + iconName
-// : iconBytes ? "data:image/png;base64," + iconBytes
-// : "image://icontheme/application-x-desktop"
-
- source: iconName ? "image://icontheme/" + iconName : "image://icontheme/application-x-desktop"
-
width: root.trayItemSize
height: root.trayItemSize
- sourceSize.width: root.trayItemSize
- sourceSize.height: root.trayItemSize
- asynchronous: true
+ source: model.icon ? model.icon : model.iconName
}
onClicked: trayModel.leftButtonClick(id)
diff --git a/resources.qrc b/resources.qrc
index 754c47a..4360cf5 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -102,5 +102,6 @@
svg/light/dark-mode.svg
svg/dark/dark-mode.svg
svg/dark/bluetooth-symbolic.svg
+ qml/StatusNotifierItem.qml
diff --git a/src/iconitem.cpp b/src/iconitem.cpp
new file mode 100644
index 0000000..e0b2116
--- /dev/null
+++ b/src/iconitem.cpp
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2021 CutefishOS Team.
+ *
+ * Author: cutefish
+ *
+ * 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 "iconitem.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "managedtexturenode.h"
+
+template
+typename std::enable_if (), bool>::type almost_equal(T x, T y, int ulp)
+{
+ return std::abs(x - y) ::epsilon() * std::abs(x + y) * ulp
+ || std::abs(x - y) ::min();
+}
+
+class IconItemSource
+{
+public:
+ explicit IconItemSource(IconItem *iconItem)
+ : m_iconItem(iconItem)
+ {
+ }
+ virtual ~IconItemSource()
+ {
+ }
+
+ virtual bool isValid() const = 0;
+ virtual const QSize size() const = 0;
+ virtual QPixmap pixmap(const QSize &size) = 0;
+
+protected:
+ QQuickWindow *window() {
+ return m_iconItem->window();
+ }
+
+ IconItem *m_iconItem;
+};
+
+class NullSource : public IconItemSource
+{
+public:
+ explicit NullSource(IconItem *iconItem)
+ : IconItemSource(iconItem)
+ {
+ }
+
+ bool isValid() const override
+ {
+ return false;
+ }
+
+ const QSize size() const override
+ {
+ return QSize();
+ }
+
+ QPixmap pixmap(const QSize &size) override
+ {
+ Q_UNUSED(size)
+ return QPixmap();
+ }
+};
+
+class QIconSource : public IconItemSource
+{
+public:
+ explicit QIconSource(const QIcon &icon, IconItem *iconItem)
+ : IconItemSource(iconItem)
+ {
+ m_icon = icon;
+ }
+
+ bool isValid() const override
+ {
+ return !m_icon.isNull();
+ }
+
+ const QSize size() const override
+ {
+ return QSize();
+ }
+
+ QPixmap pixmap(const QSize &size) override
+ {
+ QPixmap result = m_icon.pixmap(window(), m_icon.actualSize(size));
+ return result;
+ }
+
+private:
+ QIcon m_icon;
+};
+
+class QImageSource : public IconItemSource
+{
+public:
+ explicit QImageSource(const QImage &imageIcon, IconItem *iconItem)
+ : IconItemSource(iconItem)
+ {
+ m_imageIcon = imageIcon;
+ }
+
+ bool isValid() const override
+ {
+ return !m_imageIcon.isNull();
+ }
+
+ const QSize size() const override
+ {
+ const QSize s = m_imageIcon.size();
+ if (s.isValid()) {
+ return s;
+ }
+
+ return QSize();
+ }
+
+ QPixmap pixmap(const QSize &size) override
+ {
+ Q_UNUSED(size)
+ return QPixmap::fromImage(m_imageIcon);
+ }
+
+private:
+ QImage m_imageIcon;
+};
+
+class SvgSource : public IconItemSource
+{
+public:
+ explicit SvgSource(const QString &sourceString, IconItem *iconItem)
+ : IconItemSource(iconItem)
+ {
+
+ }
+
+ const QSize size() const override {
+ return size();
+ }
+
+ QPixmap pixmap(const QSize &size) override {
+ Q_UNUSED(size);
+ return QPixmap();
+ }
+
+private:
+ qreal devicePixelRatio() {
+ return window() ? window()->devicePixelRatio() : qApp->devicePixelRatio();
+ }
+
+ QString m_svgIconName;
+};
+
+IconItem::IconItem(QQuickItem *parent)
+ : QQuickItem(parent)
+ , m_iconItemSource(new NullSource(this))
+ , m_active(false)
+ , m_animated(false)
+ , m_usesPlasmaTheme(false)
+ , m_roundToIconSize(true)
+ , m_textureChanged(false)
+ , m_sizeChanged(false)
+{
+ setFlag(ItemHasContents, true);
+ setSmooth(true);
+}
+
+void IconItem::setSource(const QVariant &source)
+{
+ if (source == m_source) {
+ return;
+ }
+
+ const bool oldValid = isValid();
+
+ m_source = source;
+ QString sourceString = source.toString();
+
+ // If the QIcon was created with QIcon::fromTheme(), try to load it as svg
+ if (source.canConvert() && !source.value().name().isEmpty()) {
+ sourceString = source.value().name();
+ }
+
+ if (!sourceString.isEmpty()) {
+ // If a file:// URL or a absolute path is passed, take the image pointed by that from disk
+ QString localFile;
+ if (sourceString.startsWith(QLatin1String("file:"))) {
+ localFile = QUrl(sourceString).toLocalFile();
+ } else if (sourceString.startsWith(QLatin1Char('/'))) {
+ localFile = sourceString;
+ } else if (sourceString.startsWith("qrc:/")) {
+ localFile = sourceString.remove(0, 3);
+ } else if (sourceString.startsWith(":/")) {
+ localFile = sourceString;
+ }
+
+ if (!localFile.isEmpty()) {
+ if (sourceString.endsWith(QLatin1String(".svg"))
+ || sourceString.endsWith(QLatin1String(".svgz"))
+ || sourceString.endsWith(QLatin1String(".ico"))) {
+ QIcon icon = QIcon(localFile);
+ m_iconItemSource.reset(new QIconSource(icon, this));
+ } else {
+ QImage imageIcon = QImage(localFile);
+ m_iconItemSource.reset(new QImageSource(imageIcon, this));
+ }
+ } else {
+ // m_iconItemSource.reset(new SvgSource(sourceString, this));
+
+ if (!m_iconItemSource->isValid()) {
+ // if we started with a QIcon use that.
+ QIcon icon = source.value();
+ if (icon.isNull()) {
+ icon = QIcon::fromTheme(sourceString, QIcon::fromTheme("application-x-desktop"));
+ }
+ m_iconItemSource.reset(new QIconSource(icon, this));
+ }
+ }
+
+ } else if (source.canConvert()) {
+ m_iconItemSource.reset(new QIconSource(source.value(), this));
+ } else if (source.canConvert()) {
+ m_iconItemSource.reset(new QImageSource(source.value(), this));
+ } else {
+ m_iconItemSource.reset(new NullSource(this));
+ }
+
+ if (width() > 0 && height() > 0) {
+ schedulePixmapUpdate();
+ }
+
+ updateImplicitSize();
+
+ emit sourceChanged();
+
+ if (isValid() != oldValid) {
+ Q_EMIT validChanged();
+ }
+}
+
+QVariant IconItem::source() const
+{
+ return m_source;
+}
+
+void IconItem::updateImplicitSize()
+{
+ if (m_iconItemSource->isValid()) {
+ const QSize s = m_iconItemSource->size();
+
+ if (s.isValid()) {
+ if (!m_implicitWidthSetByUser && !m_implicitHeightSetByUser) {
+ setImplicitSize(s.width(), s.height());
+ } else if (!m_implicitWidthSetByUser) {
+ setImplicitWidth(s.width());
+ } else if (!m_implicitHeightSetByUser) {
+ setImplicitHeight(s.height());
+ }
+
+ return;
+ }
+ }
+
+ // Fall back to initializing implicit size to the Dialog size.
+ const int implicitSize = 16;
+
+ if (!m_implicitWidthSetByUser && !m_implicitHeightSetByUser) {
+ setImplicitSize(implicitSize, implicitSize);
+ } else if (!m_implicitWidthSetByUser) {
+ setImplicitWidth(implicitSize);
+ } else if (!m_implicitHeightSetByUser) {
+ setImplicitHeight(implicitSize);
+ }
+}
+
+bool IconItem::isValid() const
+{
+ return m_iconItemSource->isValid();
+}
+
+int IconItem::paintedWidth() const
+{
+ return boundingRect().size().toSize().width();
+}
+
+int IconItem::paintedHeight() const
+{
+ return boundingRect().size().toSize().height();
+}
+
+void IconItem::updatePolish()
+{
+ QQuickItem::updatePolish();
+ loadPixmap();
+}
+
+QSGNode *IconItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData)
+{
+ Q_UNUSED(updatePaintNodeData)
+
+ if (m_iconPixmap.isNull() || width() == 0.0 || height() == 0.0) {
+ delete oldNode;
+ return nullptr;
+ }
+
+ ManagedTextureNode *textureNode = dynamic_cast(oldNode);
+
+ if (!textureNode || m_textureChanged) {
+ delete oldNode;
+ textureNode = new ManagedTextureNode;
+ textureNode->setTexture(QSharedPointer(window()->createTextureFromImage(m_iconPixmap.toImage(), QQuickWindow::TextureCanUseAtlas)));
+ m_sizeChanged = true;
+ m_textureChanged = false;
+ }
+ textureNode->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+
+ if (m_sizeChanged) {
+ const QSize newSize = QSize(paintedWidth(), paintedHeight());
+ const QRect destRect(QPointF(boundingRect().center() - QPointF(newSize.width(), newSize.height()) / 2).toPoint(), newSize);
+ textureNode->setRect(destRect);
+ m_sizeChanged = false;
+ }
+ return textureNode;
+}
+
+void IconItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+{
+ QQuickItem::itemChange(change, value);
+}
+
+void IconItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ if (newGeometry.size() != oldGeometry.size()) {
+ m_sizeChanged = true;
+
+ if (newGeometry.width() > 1 && newGeometry.height() > 1) {
+ schedulePixmapUpdate();
+ } else {
+ update();
+ }
+
+ const auto oldSize = qMin(oldGeometry.size().width(), oldGeometry.size().height());
+ const auto newSize = qMin(newGeometry.size().width(), newGeometry.size().height());
+
+ if (!almost_equal(oldSize, newSize, 2)) {
+ emit paintedSizeChanged();
+ }
+ }
+
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+void IconItem::componentComplete()
+{
+ QQuickItem::componentComplete();
+ schedulePixmapUpdate();
+}
+
+void IconItem::schedulePixmapUpdate()
+{
+ polish();
+}
+
+void IconItem::loadPixmap()
+{
+ if (!isComponentComplete()) {
+ return;
+ }
+
+ const qreal devicePixelRatio = window() ? window()->devicePixelRatio() : qApp->devicePixelRatio();
+ int size = qMin(qRound(width()), qRound(height()));
+ QPixmap result;
+
+ if (size <= 0) {
+ m_iconPixmap = QPixmap();
+ update();
+ return;
+ }
+
+ if (m_iconItemSource->isValid()) {
+ result = m_iconItemSource->pixmap(QSize(size * devicePixelRatio, size * devicePixelRatio));
+ } else {
+ m_iconPixmap = QPixmap();
+ update();
+ return;
+ }
+
+ m_oldIconPixmap = m_iconPixmap;
+ m_iconPixmap = result;
+ m_textureChanged = true;
+
+ update();
+}
diff --git a/src/iconitem.h b/src/iconitem.h
new file mode 100644
index 0000000..89a2cd3
--- /dev/null
+++ b/src/iconitem.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 CutefishOS Team.
+ *
+ * Author: cutefish
+ *
+ * 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 ICONITEM_H
+#define ICONITEM_H
+
+#include
+#include
+#include
+#include
+
+#include
+
+class IconItemSource;
+class IconItem : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
+ Q_PROPERTY(int paintedWidth READ paintedWidth NOTIFY paintedSizeChanged)
+ Q_PROPERTY(int paintedHeight READ paintedHeight NOTIFY paintedSizeChanged)
+
+public:
+ explicit IconItem(QQuickItem *parent = nullptr);
+
+ void setSource(const QVariant &source);
+ QVariant source() const;
+
+ void updateImplicitSize();
+
+ bool isValid() const;
+
+ int paintedWidth() const;
+ int paintedHeight() const;
+
+ void updatePolish() override;
+ QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override;
+
+ void itemChange(ItemChange change, const ItemChangeData &value) override;
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+
+ void componentComplete() override;
+
+signals:
+ void overlaysChanged();
+ void activeChanged();
+ void sourceChanged();
+ void animatedChanged();
+ void usesPlasmaThemeChanged();
+ void roundToIconSizeChanged();
+ void validChanged();
+ void colorGroupChanged();
+ void paintedSizeChanged();
+ void statusChanged();
+ void implicitHeightChanged2();
+ void implicitWidthChanged2();
+
+private slots:
+ void schedulePixmapUpdate();
+
+private:
+ void loadPixmap();
+
+private:
+ QSharedPointer m_iconItemSource;
+ QVariant m_source;
+
+ bool m_active;
+ bool m_animated;
+ bool m_usesPlasmaTheme;
+ bool m_roundToIconSize;
+
+ bool m_textureChanged;
+ bool m_sizeChanged;
+ bool m_allowNextAnimation;
+ bool m_blockNextAnimation;
+ bool m_implicitHeightSetByUser;
+ bool m_implicitWidthSetByUser;
+
+ QPixmap m_iconPixmap;
+ QPixmap m_oldIconPixmap;
+};
+
+#endif // ICONITEM_H
diff --git a/src/main.cpp b/src/main.cpp
index 3d35b51..63e9c1c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -30,8 +30,9 @@
#include "battery.h"
#include "brightness.h"
#include "controlcenterdialog.h"
-#include "statusnotifier/statusnotifiermodel.h"
+#include "systemtray/systemtraymodel.h"
#include "appearance.h"
+#include "iconitem.h"
int main(int argc, char *argv[])
{
@@ -43,8 +44,9 @@ int main(int argc, char *argv[])
qmlRegisterType("Cutefish.Dock", 1, 0, "Battery");
qmlRegisterType("Cutefish.Dock", 1, 0, "Brightness");
qmlRegisterType("Cutefish.Dock", 1, 0, "ControlCenterDialog");
- qmlRegisterType("Cutefish.Dock", 1, 0, "StatusNotifierModel");
+ qmlRegisterType("Cutefish.Dock", 1, 0, "SystemTrayModel");
qmlRegisterType("Cutefish.Dock", 1, 0, "Appearance");
+ qmlRegisterType("Cutefish.Dock", 1, 0, "IconItem");
QString qmFilePath = QString("%1/%2.qm").arg("/usr/share/cutefish-dock/translations/").arg(QLocale::system().name());
if (QFile::exists(qmFilePath)) {
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index f48dffb..9a7b459 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -51,10 +51,11 @@ MainWindow::MainWindow(QQuickView *parent)
engine()->rootContext()->setContextProperty("Settings", m_settings);
engine()->rootContext()->setContextProperty("mainWindow", this);
+ setSource(QUrl(QStringLiteral("qrc:/qml/main.qml")));
setResizeMode(QQuickView::SizeRootObjectToView);
setScreen(qApp->primaryScreen());
- setSource(QUrl(QStringLiteral("qrc:/qml/main.qml")));
setVisible(true);
+
initSlideWindow();
resizeWindow();
onVisibilityChanged();
diff --git a/src/mainwindow.h b/src/mainwindow.h
index e929f56..89021b9 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -40,9 +40,6 @@ signals:
void iconSizeChanged();
void positionChanged();
-protected:
- void event(QEvent *e, QObject *obj);
-
private:
QRect windowRect() const;
void resizeWindow();
diff --git a/src/managedtexturenode.cpp b/src/managedtexturenode.cpp
new file mode 100644
index 0000000..90d9efb
--- /dev/null
+++ b/src/managedtexturenode.cpp
@@ -0,0 +1,16 @@
+/*
+ SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#include "managedtexturenode.h"
+
+ManagedTextureNode::ManagedTextureNode()
+{
+}
+
+void ManagedTextureNode::setTexture(QSharedPointer texture)
+{
+ m_texture = texture;
+ QSGSimpleTextureNode::setTexture(texture.data());
+}
diff --git a/src/managedtexturenode.h b/src/managedtexturenode.h
new file mode 100644
index 0000000..8adfb82
--- /dev/null
+++ b/src/managedtexturenode.h
@@ -0,0 +1,43 @@
+/*
+ SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#ifndef MANAGEDTEXTURENODE_H
+#define MANAGEDTEXTURENODE_H
+
+#include
+#include
+#include
+#include
+
+/**
+ * @class ManagedTextureNode managedtexturenode.h KQuickAddons/ManagedTextureNode
+ *
+ * @short Node that contains a reference counted texture
+ *
+ * Usually when assigning textures within a node, we'll want to delete the
+ * texture with the node. This class will take a shared texture and display it
+ * within the node.
+ *
+ * It's especially interesting to use this class together with the ImageTexturesCache
+ * that will offer us shareable textures and cache them transparently, when asking
+ * it to create the texture.
+ *
+ * @see ImageTexturesCache
+ */
+
+class ManagedTextureNode : public QSGSimpleTextureNode
+{
+ Q_DISABLE_COPY(ManagedTextureNode)
+
+public:
+ ManagedTextureNode();
+
+ void setTexture(QSharedPointer texture);
+
+private:
+ QSharedPointer m_texture;
+};
+
+#endif
diff --git a/src/statusnotifier/dbustypes.cpp b/src/statusnotifier/dbustypes.cpp
deleted file mode 100644
index a1df260..0000000
--- a/src/statusnotifier/dbustypes.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "dbustypes.h"
-
-// Marshall the IconPixmap data into a D-Bus argument
-QDBusArgument &operator<<(QDBusArgument &argument, const IconPixmap &icon)
-{
- argument.beginStructure();
- argument << icon.width;
- argument << icon.height;
- argument << icon.bytes;
- argument.endStructure();
- return argument;
-}
-
-// Retrieve the ImageStruct data from the D-Bus argument
-const QDBusArgument &operator>>(const QDBusArgument &argument, IconPixmap &icon)
-{
- argument.beginStructure();
- argument >> icon.width;
- argument >> icon.height;
- argument >> icon.bytes;
- argument.endStructure();
- return argument;
-}
-
-// Marshall the ToolTip data into a D-Bus argument
-QDBusArgument &operator<<(QDBusArgument &argument, const ToolTip &toolTip)
-{
- argument.beginStructure();
- argument << toolTip.iconName;
- argument << toolTip.iconPixmap;
- argument << toolTip.title;
- argument << toolTip.description;
- argument.endStructure();
- return argument;
-}
-
-// Retrieve the ToolTip data from the D-Bus argument
-const QDBusArgument &operator>>(const QDBusArgument &argument, ToolTip &toolTip)
-{
- argument.beginStructure();
- argument >> toolTip.iconName;
- argument >> toolTip.iconPixmap;
- argument >> toolTip.title;
- argument >> toolTip.description;
- argument.endStructure();
- return argument;
-}
diff --git a/src/statusnotifier/dbustypes.h b/src/statusnotifier/dbustypes.h
deleted file mode 100644
index 2a5809f..0000000
--- a/src/statusnotifier/dbustypes.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#include
-
-#ifndef DBUSTYPES_H
-#define DBUSTYPES_H
-
-struct IconPixmap {
- int width;
- int height;
- QByteArray bytes;
-};
-
-typedef QList IconPixmapList;
-
-struct ToolTip {
- QString iconName;
- QList iconPixmap;
- QString title;
- QString description;
-};
-
-QDBusArgument &operator<<(QDBusArgument &argument, const IconPixmap &icon);
-const QDBusArgument &operator>>(const QDBusArgument &argument, IconPixmap &icon);
-
-QDBusArgument &operator<<(QDBusArgument &argument, const ToolTip &toolTip);
-const QDBusArgument &operator>>(const QDBusArgument &argument, ToolTip &toolTip);
-
-Q_DECLARE_METATYPE(IconPixmap)
-Q_DECLARE_METATYPE(ToolTip)
-
-#endif // DBUSTYPES_H
diff --git a/src/statusnotifier/sniasync.cpp b/src/statusnotifier/sniasync.cpp
deleted file mode 100644
index 92691eb..0000000
--- a/src/statusnotifier/sniasync.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "sniasync.h"
-
-SniAsync::SniAsync(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent/* = 0*/)
- : QObject(parent)
- , m_sni{service, path, connection}
-{
- //forward StatusNotifierItem signals
- connect(&m_sni, &org::kde::StatusNotifierItem::NewAttentionIcon, this, &SniAsync::NewAttentionIcon);
- connect(&m_sni, &org::kde::StatusNotifierItem::NewIcon, this, &SniAsync::NewIcon);
- connect(&m_sni, &org::kde::StatusNotifierItem::NewOverlayIcon, this, &SniAsync::NewOverlayIcon);
- connect(&m_sni, &org::kde::StatusNotifierItem::NewStatus, this, &SniAsync::NewStatus);
- connect(&m_sni, &org::kde::StatusNotifierItem::NewTitle, this, &SniAsync::NewTitle);
- connect(&m_sni, &org::kde::StatusNotifierItem::NewToolTip, this, &SniAsync::NewToolTip);
-}
-
-QDBusPendingReply SniAsync::asyncPropGet(QString const & property)
-{
- QDBusMessage msg = QDBusMessage::createMethodCall(m_sni.service(), m_sni.path(), QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get"));
- msg << m_sni.interface() << property;
- return m_sni.connection().asyncCall(msg);
-}
diff --git a/src/statusnotifier/sniasync.h b/src/statusnotifier/sniasync.h
deleted file mode 100644
index 848d7da..0000000
--- a/src/statusnotifier/sniasync.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#if !defined(SNIASYNC_H)
-#define SNIASYNC_H
-
-#include
-#include "statusnotifieriteminterface.h"
-
-template
-struct remove_class_type { using type = void; }; // bluff
-template
-struct remove_class_type { using type = R(ArgTypes...); };
-template
-struct remove_class_type { using type = R(ArgTypes...); };
-
-template
-class call_sig_helper
-{
- template
- static decltype(&L1::operator()) test(int);
- template
- static void test(...); //bluff
-public:
- using type = decltype(test(0));
-};
-template
-struct call_signature : public remove_class_type::type> {};
-template
-struct call_signature { using type = R (ArgTypes...); };
-template
-struct call_signature { using type = R (ArgTypes...); };
-template
-struct call_signature { using type = R (ArgTypes...); };
-template
-struct call_signature { using type = R(ArgTypes...); };
-
-template struct is_valid_signature : public std::false_type {};
-template
-struct is_valid_signature : public std::true_type {};
-
-class SniAsync : public QObject
-{
- Q_OBJECT
-public:
- SniAsync(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);
-
- template
- inline void propertyGetAsync(QString const &name, F finished)
- {
- static_assert(is_valid_signature::type>::value, "need callable (lambda, *function, callable obj) (Arg) -> void");
- connect(new QDBusPendingCallWatcher{asyncPropGet(name), this},
- &QDBusPendingCallWatcher::finished,
- [this, finished, name] (QDBusPendingCallWatcher * call)
- {
- QDBusPendingReply reply = *call;
- if (reply.isError())
- qDebug().noquote().nospace() << "Error on DBus request(" << m_sni.service() << ',' << m_sni.path() << "): " << reply.error();
- finished(qdbus_cast::type>::argument_type>(reply.value()));
- call->deleteLater();
- }
- );
- }
-
- //exposed methods from org::kde::StatusNotifierItem
- inline QString service() const { return m_sni.service(); }
-
-public slots:
- //Forwarded slots from org::kde::StatusNotifierItem
- inline QDBusPendingReply<> Activate(int x, int y) { return m_sni.Activate(x, y); }
- inline QDBusPendingReply<> ContextMenu(int x, int y) { return m_sni.ContextMenu(x, y); }
- inline QDBusPendingReply<> Scroll(int delta, const QString &orientation) { return m_sni.Scroll(delta, orientation); }
- inline QDBusPendingReply<> SecondaryActivate(int x, int y) { return m_sni.SecondaryActivate(x, y); }
-
-signals:
- //Forwarded signals from org::kde::StatusNotifierItem
- void NewAttentionIcon();
- void NewIcon();
- void NewOverlayIcon();
- void NewStatus(const QString &status);
- void NewTitle();
- void NewToolTip();
-
-private:
- QDBusPendingReply asyncPropGet(QString const & property);
-
-private:
- org::kde::StatusNotifierItem m_sni;
-
-};
-
-#endif
diff --git a/src/statusnotifier/statusnotifieriteminterface.cpp b/src/statusnotifier/statusnotifieriteminterface.cpp
deleted file mode 100644
index 65a18a9..0000000
--- a/src/statusnotifier/statusnotifieriteminterface.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "statusnotifieriteminterface.h"
-
-/*
- * Implementation of interface class StatusNotifierItemInterface
- */
-
-StatusNotifierItemInterface::StatusNotifierItemInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
- : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
-{
-}
-
-StatusNotifierItemInterface::~StatusNotifierItemInterface()
-{
-}
diff --git a/src/statusnotifier/statusnotifieriteminterface.h b/src/statusnotifier/statusnotifieriteminterface.h
deleted file mode 100644
index baaa3cb..0000000
--- a/src/statusnotifier/statusnotifieriteminterface.h
+++ /dev/null
@@ -1,136 +0,0 @@
-#ifndef STATUSNOTIFIERITEMINTERFACE_H
-#define STATUSNOTIFIERITEMINTERFACE_H
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "dbustypes.h"
-
-/*
- * 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(IconPixmapList AttentionIconPixmap READ attentionIconPixmap)
- inline IconPixmapList attentionIconPixmap() const
- { return qvariant_cast< IconPixmapList >(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(IconPixmapList IconPixmap READ iconPixmap)
- inline IconPixmapList iconPixmap() const
- { return qvariant_cast< IconPixmapList >(property("IconPixmap")); }
-
- Q_PROPERTY(QString IconThemePath READ iconThemePath)
- inline QString iconThemePath() const
- { return qvariant_cast< QString >(property("IconThemePath")); }
-
- 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(IconPixmapList OverlayIconPixmap READ overlayIconPixmap)
- inline IconPixmapList overlayIconPixmap() const
- { return qvariant_cast< IconPixmapList >(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(ToolTip ToolTip READ toolTip)
- inline ToolTip toolTip() const
- { return qvariant_cast< ToolTip >(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(QLatin1String("Activate"), argumentList);
- }
-
- inline QDBusPendingReply<> ContextMenu(int x, int y)
- {
- QList argumentList;
- argumentList << QVariant::fromValue(x) << QVariant::fromValue(y);
- return asyncCallWithArgumentList(QLatin1String("ContextMenu"), argumentList);
- }
-
- inline QDBusPendingReply<> Scroll(int delta, const QString &orientation)
- {
- QList argumentList;
- argumentList << QVariant::fromValue(delta) << QVariant::fromValue(orientation);
- return asyncCallWithArgumentList(QLatin1String("Scroll"), argumentList);
- }
-
- inline QDBusPendingReply<> SecondaryActivate(int x, int y)
- {
- QList argumentList;
- argumentList << QVariant::fromValue(x) << QVariant::fromValue(y);
- return asyncCallWithArgumentList(QLatin1String("SecondaryActivate"), argumentList);
- }
-
-Q_SIGNALS: // SIGNALS
- void NewAttentionIcon();
- void NewIcon();
- void NewOverlayIcon();
- void NewStatus(const QString &status);
- void NewTitle();
- void NewToolTip();
-};
-
-namespace org {
- namespace kde {
- typedef ::StatusNotifierItemInterface StatusNotifierItem;
- }
-}
-#endif
diff --git a/src/statusnotifier/statusnotifieritemsource.cpp b/src/statusnotifier/statusnotifieritemsource.cpp
deleted file mode 100644
index 5fe7eba..0000000
--- a/src/statusnotifier/statusnotifieritemsource.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-#include "statusnotifieritemsource.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-class MenuImporter : public DBusMenuImporter
-{
-public:
- using DBusMenuImporter::DBusMenuImporter;
-
-protected:
- virtual QIcon iconForName(const QString & name) override {
- return QIcon::fromTheme(name);
- }
-};
-
-StatusNotifierItemSource::StatusNotifierItemSource(const QString &id, QObject *parent)
- : QObject(parent)
- , m_id(id)
- , m_refreshing(false)
- , m_needsReRefreshing(false)
- , m_menuImporter(nullptr)
-{
- int slash = id.indexOf('/');
- if (slash == -1) {
- qWarning() << "Invalid notifierItemId:" << id;
- m_valid = false;
- m_statusNotifierItemInterface = nullptr;
- return;
- }
-
- QString service = id.left(slash);
- QString path = id.mid(slash);
-
- m_statusNotifierItemInterface = new org::kde::StatusNotifierItem(service, path,
- QDBusConnection::sessionBus(), this);
- m_refreshTimer.setSingleShot(true);
- m_refreshTimer.setInterval(10);
- connect(&m_refreshTimer, &QTimer::timeout, this, &StatusNotifierItemSource::performRefresh);
-
- m_valid = !service.isEmpty() && m_statusNotifierItemInterface->isValid();
- if (m_valid) {
- connect(m_statusNotifierItemInterface, &StatusNotifierItemInterface::NewTitle, this, &StatusNotifierItemSource::refreshTitle);
- connect(m_statusNotifierItemInterface, &StatusNotifierItemInterface::NewIcon, this, &StatusNotifierItemSource::refreshIcons);
- connect(m_statusNotifierItemInterface, &StatusNotifierItemInterface::NewAttentionIcon, this, &StatusNotifierItemSource::refreshIcons);
- connect(m_statusNotifierItemInterface, &StatusNotifierItemInterface::NewOverlayIcon, this, &StatusNotifierItemSource::refreshIcons);
- connect(m_statusNotifierItemInterface, &StatusNotifierItemInterface::NewToolTip, this, &StatusNotifierItemSource::refreshToolTip);
- refresh();
- }
-}
-
-StatusNotifierItemSource::~StatusNotifierItemSource()
-{
- delete m_statusNotifierItemInterface;
-}
-
-void StatusNotifierItemSource::activate(int x, int y)
-{
- if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
- m_statusNotifierItemInterface->Activate(x, y);
- }
-}
-
-void StatusNotifierItemSource::secondaryActivate(int x, int y)
-{
- if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
- m_statusNotifierItemInterface->call(QDBus::NoBlock, QStringLiteral("SecondaryActivate"), x, y);
- }
-}
-
-void StatusNotifierItemSource::contextMenu(int x, int y)
-{
- if (m_menuImporter) {
- m_menuImporter->updateMenu();
-
- // Popup menu
- 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()) {
- m_statusNotifierItemInterface->call(QDBus::NoBlock, QStringLiteral("ContextMenu"), x, y);
- }
- }
-}
-
-void StatusNotifierItemSource::refresh()
-{
- if (!m_refreshTimer.isActive()) {
- m_refreshTimer.start();
- }
-}
-
-void StatusNotifierItemSource::refreshTitle()
-{
- refresh();
-}
-
-void StatusNotifierItemSource::refreshToolTip()
-{
- refresh();
-}
-
-void StatusNotifierItemSource::refreshIcons()
-{
- refresh();
-}
-
-void StatusNotifierItemSource::performRefresh()
-{
- if (m_refreshing) {
- m_needsReRefreshing = true;
- return;
- }
-
- m_refreshing = true;
- QDBusMessage message = QDBusMessage::createMethodCall(m_statusNotifierItemInterface->service(),
- m_statusNotifierItemInterface->path(),
- QStringLiteral("org.freedesktop.DBus.Properties"),
- QStringLiteral("GetAll"));
-
- message << m_statusNotifierItemInterface->interface();
- QDBusPendingCall call = m_statusNotifierItemInterface->connection().asyncCall(message);
- QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
- connect(watcher, &QDBusPendingCallWatcher::finished, this, &StatusNotifierItemSource::refreshCallback);
-}
-
-void StatusNotifierItemSource::refreshCallback(QDBusPendingCallWatcher *call)
-{
- m_refreshing = false;
- if (m_needsReRefreshing) {
- m_needsReRefreshing = false;
- performRefresh();
- call->deleteLater();
- return;
- }
-
- QDBusPendingReply reply = *call;
- if (reply.isError()) {
- m_valid = false;
- } else {
- QVariantMap properties = reply.argumentAt<0>();
- QString path = properties[QStringLiteral("IconThemePath")].toString();
-
- m_title = properties[QStringLiteral("Title")].toString();
- m_iconName = properties[QStringLiteral("IconName")].toString();
-
- // ToolTip
- ToolTip toolTip;
- properties[QStringLiteral("ToolTip")].value() >> toolTip;
- m_tooltip = toolTip.title;
-
- // Menu
- QString menuObjectPath = properties[QStringLiteral("Menu")].value().path();
- if (!menuObjectPath.isEmpty()) {
- if (menuObjectPath.startsWith(QLatin1String("/NO_DBUSMENU"))) {
- qWarning() << "DBusMenu disabled for this application";
- } else {
- m_menuImporter = new MenuImporter(m_statusNotifierItemInterface->service(), menuObjectPath, this);
- }
- }
-
- // Icon
- IconPixmapList iconPixmaps;
- properties[QStringLiteral("IconPixmap")].value() >> iconPixmaps;
-
- QImage image = IconPixmapListToImage(iconPixmaps);
- if (!image.isNull()) {
- QByteArray byteArray;
- QBuffer buffer(&byteArray);
- image.save(&buffer, "PNG");
- m_iconBytes = byteArray.toBase64();
- }
-
- emit updated(this);
- }
-
- call->deleteLater();
-}
-
-QImage StatusNotifierItemSource::IconPixmapListToImage(const IconPixmapList &list) const
-{
- QIcon icon;
-
- for (IconPixmap iconPixmap: list) {
- if (!iconPixmap.bytes.isNull()) {
- QImage image((uchar*) iconPixmap.bytes.data(), iconPixmap.width,
- iconPixmap.height, QImage::Format_ARGB32);
-
- const uchar *end = image.constBits() + image.sizeInBytes();
- uchar *dest = reinterpret_cast(iconPixmap.bytes.data());
- for (const uchar *src = image.constBits(); src < end; src += 4, dest += 4)
- qToUnaligned(qToBigEndian(qFromUnaligned(src)), dest);
-
- icon.addPixmap(QPixmap::fromImage(image));
- }
- }
-
- return icon.pixmap(QSize(24, 24)).toImage();
-}
diff --git a/src/statusnotifier/statusnotifieritemsource.h b/src/statusnotifier/statusnotifieritemsource.h
deleted file mode 100644
index a5cdf20..0000000
--- a/src/statusnotifier/statusnotifieritemsource.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#ifndef STATUSNOTIFIERITEMSOURCE_H
-#define STATUSNOTIFIERITEMSOURCE_H
-
-#include
-#include
-
-#include "statusnotifieriteminterface.h"
-
-class MenuImporter;
-class StatusNotifierItemSource : public QObject
-{
- Q_OBJECT
-
-public:
- explicit StatusNotifierItemSource(const QString &id, QObject *parent = nullptr);
- ~StatusNotifierItemSource();
-
- QString id() const { return m_id; }
- QString title() const { return m_title; }
- QString tooltip() const { return m_tooltip; }
- QString iconName() const { return m_iconName; }
- QString iconBytes() const { return m_iconBytes; }
-
- void activate(int x, int y);
- void secondaryActivate(int x, int y);
- void contextMenu(int x, int y);
-
- MenuImporter *menuImporter() { return m_menuImporter; }
-
-signals:
- void updated(StatusNotifierItemSource *);
-
-private slots:
- void refresh();
- void refreshTitle();
- void refreshToolTip();
- void refreshIcons();
- void performRefresh();
- void refreshCallback(QDBusPendingCallWatcher *);
- QImage IconPixmapListToImage(const IconPixmapList &list) const;
-
-private:
- QString m_id;
- QString m_title;
- QString m_tooltip;
- QString m_iconName;
- QString m_iconBytes;
-
- bool m_valid;
- bool m_refreshing;
- bool m_needsReRefreshing;
- StatusNotifierItemInterface *m_statusNotifierItemInterface;
- MenuImporter *m_menuImporter;
- QTimer m_refreshTimer;
-};
-
-#endif // STATUSNOTIFIERITEMSOURCE_H
diff --git a/src/statusnotifier/statusnotifiermodel.cpp b/src/statusnotifier/statusnotifiermodel.cpp
deleted file mode 100644
index fe84b99..0000000
--- a/src/statusnotifier/statusnotifiermodel.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-#include "statusnotifiermodel.h"
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-StatusNotifierModel::StatusNotifierModel(QObject *parent)
- : QAbstractListModel(parent)
- , m_watcher(nullptr)
-{
- QFutureWatcher * futureWatcher = new QFutureWatcher;
- connect(futureWatcher, &QFutureWatcher::finished, this, [this, futureWatcher] {
- m_watcher = futureWatcher->future().result();
-
- connect(m_watcher, &StatusNotifierWatcher::StatusNotifierItemRegistered,
- this, &StatusNotifierModel::itemAdded);
- connect(m_watcher, &StatusNotifierWatcher::StatusNotifierItemUnregistered,
- this, &StatusNotifierModel::itemRemoved);
-
- qDebug() << m_watcher->RegisteredStatusNotifierItems();
-
- futureWatcher->deleteLater();
- });
-
- QFuture future = QtConcurrent::run([=] {
- QString dbusName = QStringLiteral("org.kde.StatusNotifierHost-%1-%2").arg(QApplication::applicationPid()).arg(1);
- if (QDBusConnectionInterface::ServiceNotRegistered == QDBusConnection::sessionBus().interface()->registerService(dbusName, QDBusConnectionInterface::DontQueueService))
- qDebug() << "unable to register service for " << dbusName;
-
- StatusNotifierWatcher * watcher = new StatusNotifierWatcher;
- watcher->RegisterStatusNotifierHost(dbusName);
- watcher->moveToThread(QApplication::instance()->thread());
- return watcher;
- });
-
- futureWatcher->setFuture(future);
-}
-
-StatusNotifierModel::~StatusNotifierModel()
-{
- delete m_watcher;
-}
-
-int StatusNotifierModel::rowCount(const QModelIndex &parent) const
-{
- Q_UNUSED(parent)
-
- return m_items.size();
-}
-
-QHash StatusNotifierModel::roleNames() const
-{
- QHash roles;
- roles[IdRole] = "id";
- roles[IconNameRole] = "iconName";
- roles[IconBytesRole] = "iconBytes";
- roles[TitleRole] = "title";
- roles[ToolTipRole] = "toolTip";
- return roles;
-}
-
-QVariant StatusNotifierModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return QVariant();
-
- StatusNotifierItemSource *item = m_items.at(index.row());
-
- switch (role) {
- case IdRole:
- return item->id();
- case IconNameRole:
- return item->iconName();
- case IconBytesRole:
- return item->iconBytes();
- case TitleRole:
- return item->title();
- case ToolTipRole:
- return item->tooltip();
- }
-
- return QVariant();
-}
-
-int StatusNotifierModel::indexOf(const QString &id)
-{
- for (StatusNotifierItemSource *item : m_items) {
- if (item->id() == id)
- return m_items.indexOf(item);
- }
-
- return -1;
-}
-
-StatusNotifierItemSource *StatusNotifierModel::findItemById(const QString &id)
-{
- int index = indexOf(id);
-
- if (index == -1)
- return nullptr;
-
- return m_items.at(index);
-}
-
-void StatusNotifierModel::leftButtonClick(const QString &id)
-{
- StatusNotifierItemSource *item = findItemById(id);
- if (item) {
- QPoint p(QCursor::pos());
- item->activate(p.x(), p.y());
- }
-}
-
-void StatusNotifierModel::rightButtonClick(const QString &id)
-{
- StatusNotifierItemSource *item = findItemById(id);
- if (item) {
- QPoint p(QCursor::pos());
- item->contextMenu(p.x(), p.y());
- }
-}
-
-void StatusNotifierModel::itemAdded(QString serviceAndPath)
-{
- StatusNotifierItemSource *source = new StatusNotifierItemSource(serviceAndPath, this);
-
- connect(source, &StatusNotifierItemSource::updated, this, &StatusNotifierModel::updated);
-
- beginInsertRows(QModelIndex(), rowCount(), rowCount());
- m_items.append(source);
- endInsertRows();
-}
-
-void StatusNotifierModel::itemRemoved(const QString &serviceAndPath)
-{
- int index = indexOf(serviceAndPath);
-
- if (index != -1) {
- beginRemoveRows(QModelIndex(), index, index);
- StatusNotifierItemSource *item = m_items.at(index);
- m_items.removeAll(item);
- endRemoveRows();
- }
-}
-
-void StatusNotifierModel::updated(StatusNotifierItemSource *item)
-{
- if (!item)
- return;
-
- int idx = indexOf(item->id());
-
- // update
- if (idx != -1) {
- dataChanged(index(idx, 0), index(idx, 0));
- }
-}
diff --git a/src/statusnotifier/statusnotifierwatcher.cpp b/src/statusnotifier/statusnotifierwatcher.cpp
deleted file mode 100644
index d8b5189..0000000
--- a/src/statusnotifier/statusnotifierwatcher.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "statusnotifierwatcher.h"
-#include
-#include
-
-StatusNotifierWatcher::StatusNotifierWatcher(QObject *parent) : QObject(parent)
-{
- qRegisterMetaType("IconPixmap");
- qDBusRegisterMetaType();
- qRegisterMetaType("IconPixmapList");
- qDBusRegisterMetaType();
- qRegisterMetaType("ToolTip");
- qDBusRegisterMetaType();
-
- QDBusConnection dbus = QDBusConnection::sessionBus();
- switch (dbus.interface()->registerService(QStringLiteral("org.kde.StatusNotifierWatcher"), QDBusConnectionInterface::QueueService).value())
- {
- case QDBusConnectionInterface::ServiceNotRegistered:
- qWarning() << "StatusNotifier: unable to register service for org.kde.StatusNotifierWatcher";
- break;
- case QDBusConnectionInterface::ServiceQueued:
- qWarning() << "StatusNotifier: registration of service org.kde.StatusNotifierWatcher queued, we can become primary after existing one deregisters";
- break;
- case QDBusConnectionInterface::ServiceRegistered:
- break;
- }
-
- dbus.registerObject(QStringLiteral("/StatusNotifierWatcher"), this, QDBusConnection::ExportScriptableContents);
-
-// if (!dbus.registerObject(QStringLiteral("/StatusNotifierWatcher"), this, QDBusConnection::ExportScriptableContents))
-// qDebug() << QDBusConnection::sessionBus().lastError().message();
-
- mWatcher = new QDBusServiceWatcher(this);
- mWatcher->setConnection(dbus);
- mWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
-
- connect(mWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &StatusNotifierWatcher::serviceUnregistered);
-}
-
-StatusNotifierWatcher::~StatusNotifierWatcher()
-{
- QDBusConnection::sessionBus().unregisterService(QStringLiteral("org.kde.StatusNotifierWatcher"));
-}
-
-void StatusNotifierWatcher::RegisterStatusNotifierItem(const QString &serviceOrPath)
-{
- QString service = serviceOrPath;
- QString path = QStringLiteral("/StatusNotifierItem");
-
- // workaround for sni-qt
- if (service.startsWith(QLatin1Char('/'))) {
- path = service;
- service = message().service();
- }
-
- QString notifierItemId = service + path;
-
- if (QDBusConnection::sessionBus().interface()->isServiceRegistered(service).value()
- && !mServices.contains(notifierItemId)) {
- mServices << notifierItemId;
- mWatcher->addWatchedService(service);
- emit StatusNotifierItemRegistered(notifierItemId);
- }
-}
-
-void StatusNotifierWatcher::RegisterStatusNotifierHost(const QString &service)
-{
- if (!mHosts.contains(service))
- {
- mHosts.append(service);
- mWatcher->addWatchedService(service);
- }
-}
-
-void StatusNotifierWatcher::serviceUnregistered(const QString &service)
-{
- // qDebug() << "Service" << service << "unregistered";
-
- mWatcher->removeWatchedService(service);
-
- if (mHosts.contains(service))
- {
- mHosts.removeAll(service);
- return;
- }
-
- QString match = service + QLatin1Char('/');
- QStringList::Iterator it = mServices.begin();
- while (it != mServices.end())
- {
- if (it->startsWith(match))
- {
- QString name = *it;
- it = mServices.erase(it);
- emit StatusNotifierItemUnregistered(name);
- }
- else
- ++it;
- }
-}
diff --git a/src/statusnotifier/statusnotifierwatcher.h b/src/statusnotifier/statusnotifierwatcher.h
deleted file mode 100644
index a4d2f98..0000000
--- a/src/statusnotifier/statusnotifierwatcher.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef STATUSNOTIFIERWATCHER_H
-#define STATUSNOTIFIERWATCHER_H
-
-#include
-#include
-#include
-#include
-#include
-
-#include "dbustypes.h"
-
-class StatusNotifierWatcher : public QObject, protected QDBusContext
-{
- Q_OBJECT
- Q_CLASSINFO("D-Bus Interface", "org.kde.StatusNotifierWatcher")
- Q_SCRIPTABLE Q_PROPERTY(bool IsStatusNotifierHostRegistered READ isStatusNotifierHostRegistered)
- Q_SCRIPTABLE Q_PROPERTY(int ProtocolVersion READ protocolVersion)
- Q_SCRIPTABLE Q_PROPERTY(QStringList RegisteredStatusNotifierItems READ RegisteredStatusNotifierItems)
-
-public:
- explicit StatusNotifierWatcher(QObject *parent = nullptr);
- ~StatusNotifierWatcher();
-
- bool isStatusNotifierHostRegistered() { return mHosts.count() > 0; }
- int protocolVersion() const { return 0; }
- QStringList RegisteredStatusNotifierItems() const { return mServices; }
-
-signals:
- Q_SCRIPTABLE void StatusNotifierItemRegistered(const QString &service);
- Q_SCRIPTABLE void StatusNotifierItemUnregistered(const QString &service);
- Q_SCRIPTABLE void StatusNotifierHostRegistered();
-
-public slots:
- Q_SCRIPTABLE void RegisterStatusNotifierItem(const QString &serviceOrPath);
- Q_SCRIPTABLE void RegisterStatusNotifierHost(const QString &service);
-
- void serviceUnregistered(const QString &service);
-
-private:
- QStringList mServices;
- QStringList mHosts;
- QDBusServiceWatcher *mWatcher;
-};
-
-#endif // STATUSNOTIFIERWATCHER_H
diff --git a/src/systemtray/CMakeLists.txt b/src/systemtray/CMakeLists.txt
new file mode 100644
index 0000000..1b659b7
--- /dev/null
+++ b/src/systemtray/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(TRAY_SRCS
+ statusnotifieritemjob.cpp
+ statusnotifieritemsource.cpp
+ systemtraytypes.cpp
+ systemtraytypedefs.h
+)
+
+set(statusnotifierwatcher_xml org.kde.StatusNotifierWatcher.xml)
+qt5_add_dbus_interface(SRCS ${statusnotifierwatcher_xml} statusnotifierwatcher_interface)
+qt5_add_dbus_interface(SRCS org.freedesktop.DBus.Properties.xml dbusproperties)
+
+set(statusnotifieritem_xml org.kde.StatusNotifierItem.xml)
+set_source_files_properties(${statusnotifieritem_xml} PROPERTIES
+ NO_NAMESPACE false
+ INCLUDE "systemtraytypes.h"
+ CLASSNAME OrgKdeStatusNotifierItem
+)
+qt5_add_dbus_interface(SRCS ${statusnotifieritem_xml} statusnotifieritem_interface)
\ No newline at end of file
diff --git a/src/systemtray/org.freedesktop.DBus.Properties.xml b/src/systemtray/org.freedesktop.DBus.Properties.xml
new file mode 100644
index 0000000..3bbf826
--- /dev/null
+++ b/src/systemtray/org.freedesktop.DBus.Properties.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/systemtray/org.kde.StatusNotifierItem.xml b/src/systemtray/org.kde.StatusNotifierItem.xml
new file mode 100644
index 0000000..d378c74
--- /dev/null
+++ b/src/systemtray/org.kde.StatusNotifierItem.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/systemtray/org.kde.StatusNotifierWatcher.xml b/src/systemtray/org.kde.StatusNotifierWatcher.xml
new file mode 100644
index 0000000..2eb1a7a
--- /dev/null
+++ b/src/systemtray/org.kde.StatusNotifierWatcher.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/systemtray/statusnotifieritemjob.cpp b/src/systemtray/statusnotifieritemjob.cpp
new file mode 100644
index 0000000..e198a65
--- /dev/null
+++ b/src/systemtray/statusnotifieritemjob.cpp
@@ -0,0 +1,25 @@
+#include "statusnotifieritemjob.h"
+
+StatusNotifierItemJob::StatusNotifierItemJob(StatusNotifierItemSource *source, QObject *parent)
+ : QObject(parent)
+ , m_source(source)
+{
+ // Queue connection, so that all 'deleteLater' are performed before we use updated menu.
+ connect(source, SIGNAL(contextMenuReady(QMenu *)), this, SLOT(contextMenuReady(QMenu *)), Qt::QueuedConnection);
+ connect(source, &StatusNotifierItemSource::activateResult, this, &StatusNotifierItemJob::activateCallback);
+}
+
+void StatusNotifierItemJob::start()
+{
+
+}
+
+void StatusNotifierItemJob::activateCallback(bool success)
+{
+
+}
+
+void StatusNotifierItemJob::contextMenuReady(QMenu *menu)
+{
+
+}
diff --git a/src/systemtray/statusnotifieritemjob.h b/src/systemtray/statusnotifieritemjob.h
new file mode 100644
index 0000000..19bb572
--- /dev/null
+++ b/src/systemtray/statusnotifieritemjob.h
@@ -0,0 +1,27 @@
+#ifndef STATUSNOTIFIERITEMJOB_H
+#define STATUSNOTIFIERITEMJOB_H
+
+#include
+#include
+
+#include "statusnotifieritemsource.h"
+
+class StatusNotifierItemJob : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit StatusNotifierItemJob(StatusNotifierItemSource *source, QObject *parent = nullptr);
+
+protected:
+ void start();
+
+private Q_SLOTS:
+ void activateCallback(bool success);
+ void contextMenuReady(QMenu *menu);
+
+private:
+ StatusNotifierItemSource *m_source;
+};
+
+#endif // STATUSNOTIFIERITEMJOB_H
diff --git a/src/systemtray/statusnotifieritemsource.cpp b/src/systemtray/statusnotifieritemsource.cpp
new file mode 100644
index 0000000..d40cc92
--- /dev/null
+++ b/src/systemtray/statusnotifieritemsource.cpp
@@ -0,0 +1,355 @@
+#include "statusnotifieritemsource.h"
+#include "systemtraytypes.h"
+
+#include
+#include
+#include
+
+class MenuImporter : public DBusMenuImporter
+{
+public:
+ using DBusMenuImporter::DBusMenuImporter;
+
+protected:
+ QIcon iconForName(const QString & name) override {
+ return QIcon::fromTheme(name);
+ }
+};
+
+StatusNotifierItemSource::StatusNotifierItemSource(const QString ¬ifierItemId, QObject *parent)
+ : QObject(parent)
+ , m_menuImporter(nullptr)
+ , m_refreshing(false)
+ , m_needsReRefreshing(false)
+ , m_titleUpdate(true)
+ , m_iconUpdate(true)
+ , m_tooltipUpdate(true)
+ , m_statusUpdate(true)
+ , m_id(notifierItemId)
+{
+ setObjectName(notifierItemId);
+
+ qDBusRegisterMetaType();
+ qDBusRegisterMetaType();
+ qDBusRegisterMetaType();
+
+ m_name = notifierItemId;
+
+ int slash = notifierItemId.indexOf('/');
+ if (slash == -1) {
+ qWarning() << "Invalid notifierItemId:" << notifierItemId;
+ m_valid = false;
+ m_statusNotifierItemInterface = nullptr;
+ return;
+ }
+
+ QString service = notifierItemId.left(slash);
+ QString path = notifierItemId.mid(slash);
+
+ m_statusNotifierItemInterface = new org::kde::StatusNotifierItem(service, path, QDBusConnection::sessionBus(), this);
+
+ m_refreshTimer.setSingleShot(true);
+ m_refreshTimer.setInterval(10);
+ connect(&m_refreshTimer, &QTimer::timeout, this, &StatusNotifierItemSource::performRefresh);
+
+ m_valid = !service.isEmpty() && m_statusNotifierItemInterface->isValid();
+
+ if (m_valid) {
+ connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItem::NewTitle, this, &StatusNotifierItemSource::refreshTitle);
+ connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItem::NewIcon, this, &StatusNotifierItemSource::refreshIcons);
+ connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItem::NewAttentionIcon, this, &StatusNotifierItemSource::refreshIcons);
+ connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItem::NewOverlayIcon, this, &StatusNotifierItemSource::refreshIcons);
+ connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItem::NewToolTip, this, &StatusNotifierItemSource::refreshToolTip);
+ connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItem::NewStatus, this, &StatusNotifierItemSource::syncStatus);
+ refresh();
+ }
+}
+
+StatusNotifierItemSource::~StatusNotifierItemSource()
+{
+ delete m_statusNotifierItemInterface;
+}
+
+QString StatusNotifierItemSource::id() const
+{
+ return m_id;
+}
+
+QString StatusNotifierItemSource::title() const
+{
+ return m_title;
+}
+
+QString StatusNotifierItemSource::tooltip() const
+{
+ return m_tooltip;
+}
+
+QString StatusNotifierItemSource::subtitle() const
+{
+ return m_subTitle;
+}
+
+QString StatusNotifierItemSource::iconName() const
+{
+ return m_iconName;
+}
+
+QIcon StatusNotifierItemSource::icon() const
+{
+ return m_icon;
+}
+
+void StatusNotifierItemSource::activate(int x, int y)
+{
+ if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
+ QDBusMessage message = QDBusMessage::createMethodCall(m_statusNotifierItemInterface->service(),
+ m_statusNotifierItemInterface->path(),
+ m_statusNotifierItemInterface->interface(),
+ QStringLiteral("Activate"));
+
+ message << x << y;
+ QDBusPendingCall call = m_statusNotifierItemInterface->connection().asyncCall(message);
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
+ connect(watcher, &QDBusPendingCallWatcher::finished, this, &StatusNotifierItemSource::activateCallback);
+ }
+}
+
+void StatusNotifierItemSource::secondaryActivate(int x, int y)
+{
+ if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
+ m_statusNotifierItemInterface->call(QDBus::NoBlock, QStringLiteral("SecondaryActivate"), x, y);
+ }
+}
+
+void StatusNotifierItemSource::scroll(int delta, const QString &direction)
+{
+ if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
+ m_statusNotifierItemInterface->call(QDBus::NoBlock, QStringLiteral("Scroll"), delta, direction);
+ }
+}
+
+void StatusNotifierItemSource::contextMenu(int x, int y)
+{
+ if (m_menuImporter) {
+ m_menuImporter->updateMenu();
+
+ // Popup menu
+ 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()) {
+ m_statusNotifierItemInterface->call(QDBus::NoBlock, QStringLiteral("ContextMenu"), x, y);
+ }
+ }
+}
+
+void StatusNotifierItemSource::contextMenuReady()
+{
+ emit contextMenuReady(m_menuImporter->menu());
+}
+
+void StatusNotifierItemSource::refreshTitle()
+{
+ m_titleUpdate = true;
+ refresh();
+}
+
+void StatusNotifierItemSource::refreshIcons()
+{
+ m_iconUpdate = true;
+ refresh();
+}
+
+void StatusNotifierItemSource::refreshToolTip()
+{
+ m_tooltipUpdate = true;
+ refresh();
+}
+
+void StatusNotifierItemSource::refresh()
+{
+ if (!m_refreshTimer.isActive()) {
+ m_refreshTimer.start();
+ }
+}
+
+void StatusNotifierItemSource::performRefresh()
+{
+ if (m_refreshing) {
+ m_needsReRefreshing = true;
+ return;
+ }
+
+ m_refreshing = true;
+ QDBusMessage message = QDBusMessage::createMethodCall(m_statusNotifierItemInterface->service(),
+ m_statusNotifierItemInterface->path(),
+ QStringLiteral("org.freedesktop.DBus.Properties"),
+ QStringLiteral("GetAll"));
+
+ message << m_statusNotifierItemInterface->interface();
+ QDBusPendingCall call = m_statusNotifierItemInterface->connection().asyncCall(message);
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
+ connect(watcher, &QDBusPendingCallWatcher::finished, this, &StatusNotifierItemSource::refreshCallback);
+}
+
+void StatusNotifierItemSource::syncStatus(QString)
+{
+
+}
+
+void StatusNotifierItemSource::refreshCallback(QDBusPendingCallWatcher *call)
+{
+ m_refreshing = false;
+ if (m_needsReRefreshing) {
+ m_needsReRefreshing = false;
+ performRefresh();
+ call->deleteLater();
+ return;
+ }
+
+ QDBusPendingReply reply = *call;
+ if (reply.isError()) {
+ m_valid = false;
+ } else {
+ QVariantMap properties = reply.argumentAt<0>();
+ QString path = properties[QStringLiteral("IconThemePath")].toString();
+
+ m_title = properties[QStringLiteral("Title")].toString();
+ m_iconName = properties[QStringLiteral("IconName")].toString();
+
+ // ToolTip
+ KDbusToolTipStruct toolTip;
+ properties[QStringLiteral("ToolTip")].value() >> toolTip;
+ m_tooltip = toolTip.title;
+
+ // Icon
+ KDbusImageVector image;
+ properties[QStringLiteral("IconPixmap")].value() >> image;
+ if (!image.isEmpty()) {
+ m_icon = imageVectorToPixmap(image);
+ }
+
+// QString newTitle;
+// QString newIconName;
+// QString newToolTip;
+
+// QString overlayIconName = properties[QStringLiteral("OverlayIconName")].toString();
+// QString iconName = properties[QStringLiteral("IconName")].toString();
+
+// bool changed = false;
+
+// newTitle = properties[QStringLiteral("Title")].toString();
+
+// if (!overlayIconName.isEmpty())
+// newIconName = iconName;
+// if (!iconName.isEmpty())
+// newIconName = iconName;
+
+// KDbusToolTipStruct toolTip;
+// properties[QStringLiteral("ToolTip")].value() >> toolTip;
+// // newToolTip = !toolTip.title.isEmpty() ? toolTip.title : toolTip.subTitle;
+
+// if (newTitle != m_title) {
+// m_title = newTitle;
+// changed = true;
+// }
+
+// if (newIconName != m_iconName) {
+// m_iconName = iconName;
+// changed = true;
+// }
+
+// if (newToolTip != m_tooltip) {
+// m_tooltip = newToolTip;
+// changed = true;
+// }
+
+// // Icon
+// KDbusImageVector image;
+// properties[QStringLiteral("AttentionIconPixmap")].value() >> image;
+// if (!image.isEmpty()) {
+// m_icon = imageVectorToPixmap(image);
+// changed = true;
+// }
+
+// properties[QStringLiteral("IconPixmap")].value() >> image;
+// if (!image.isEmpty()) {
+// m_icon = imageVectorToPixmap(image);
+// changed = true;
+// }
+
+ // Menu
+ if (!m_menuImporter) {
+ QString menuObjectPath = properties[QStringLiteral("Menu")].value().path();
+ if (!menuObjectPath.isEmpty()) {
+ if (menuObjectPath.startsWith(QLatin1String("/NO_DBUSMENU"))) {
+ // This is a hack to make it possible to disable DBusMenu in an
+ // application. The string "/NO_DBUSMENU" must be the same as in
+ // KStatusNotifierItem::setContextMenu().
+ qWarning() << "DBusMenu disabled for this application";
+ } else {
+ m_menuImporter = new MenuImporter(m_statusNotifierItemInterface->service(),
+ menuObjectPath, this);
+ }
+ }
+ }
+
+ // qDebug() << newTitle << newIconName << newToolTip << image.isEmpty();
+
+ emit updated(this);
+ }
+
+ call->deleteLater();
+}
+
+void StatusNotifierItemSource::activateCallback(QDBusPendingCallWatcher *call)
+{
+ QDBusPendingReply reply = *call;
+ emit activateResult(!reply.isError());
+ call->deleteLater();
+}
+
+QPixmap StatusNotifierItemSource::KDbusImageStructToPixmap(const KDbusImageStruct &image) const
+{
+ // swap from network byte order if we are little endian
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ uint *uintBuf = (uint *)image.data.data();
+ for (uint i = 0; i < image.data.size() / sizeof(uint); ++i) {
+ *uintBuf = ntohl(*uintBuf);
+ ++uintBuf;
+ }
+ }
+ if (image.width == 0 || image.height == 0) {
+ return QPixmap();
+ }
+
+ // avoid a deep copy of the image data
+ // we need to keep a reference to the image.data alive for the lifespan of the image, even if the image is copied
+ // we create a new QByteArray with a shallow copy of the original data on the heap, then delete this in the QImage cleanup
+ auto dataRef = new QByteArray(image.data);
+
+ QImage iconImage(
+ reinterpret_cast(dataRef->data()),
+ image.width,
+ image.height,
+ QImage::Format_ARGB32,
+ [](void *ptr) {
+ delete static_cast(ptr);
+ },
+ dataRef);
+ return QPixmap::fromImage(iconImage);
+}
+
+QIcon StatusNotifierItemSource::imageVectorToPixmap(const KDbusImageVector &vector) const
+{
+ QIcon icon;
+
+ for (int i = 0; i < vector.size(); ++i) {
+ icon.addPixmap(KDbusImageStructToPixmap(vector[i]));
+ }
+
+ return icon;
+}
diff --git a/src/systemtray/statusnotifieritemsource.h b/src/systemtray/statusnotifieritemsource.h
new file mode 100644
index 0000000..166fdca
--- /dev/null
+++ b/src/systemtray/statusnotifieritemsource.h
@@ -0,0 +1,72 @@
+#ifndef STATUSNOTIFIERITEMSOURCE_H
+#define STATUSNOTIFIERITEMSOURCE_H
+
+#include
+#include
+#include
+
+#include "statusnotifieritem_interface.h"
+
+class DBusMenuImporter;
+class StatusNotifierItemSource : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit StatusNotifierItemSource(const QString &service, QObject *parent = nullptr);
+ ~StatusNotifierItemSource();
+
+ QString id() const;
+ QString title() const;
+ QString tooltip() const;
+ QString subtitle() const;
+ QString iconName() const;
+ QIcon icon() const;
+
+ void activate(int x, int y);
+ void secondaryActivate(int x, int y);
+ void scroll(int delta, const QString &direction);
+ void contextMenu(int x, int y);
+
+signals:
+ void contextMenuReady(QMenu *menu);
+ void activateResult(bool success);
+ void updated(StatusNotifierItemSource *);
+
+private slots:
+ void contextMenuReady();
+ void refreshTitle();
+ void refreshIcons();
+ void refreshToolTip();
+ void refresh();
+ void performRefresh();
+ void syncStatus(QString);
+ void refreshCallback(QDBusPendingCallWatcher *);
+ void activateCallback(QDBusPendingCallWatcher *);
+
+private:
+ QPixmap KDbusImageStructToPixmap(const KDbusImageStruct &image) const;
+ QIcon imageVectorToPixmap(const KDbusImageVector &vector) const;
+
+private:
+ bool m_valid;
+ QString m_name;
+ QTimer m_refreshTimer;
+ DBusMenuImporter *m_menuImporter;
+ org::kde::StatusNotifierItem *m_statusNotifierItemInterface;
+ bool m_refreshing : 1;
+ bool m_needsReRefreshing : 1;
+ bool m_titleUpdate : 1;
+ bool m_iconUpdate : 1;
+ bool m_tooltipUpdate : 1;
+ bool m_statusUpdate : 1;
+
+ QString m_id;
+ QString m_title;
+ QString m_tooltip;
+ QString m_subTitle;
+ QString m_iconName;
+ QIcon m_icon;
+};
+
+#endif // STATUSNOTIFIERITEMSOURCE_H
diff --git a/src/systemtray/statusnotifierwatcher.cpp b/src/systemtray/statusnotifierwatcher.cpp
new file mode 100644
index 0000000..90f501d
--- /dev/null
+++ b/src/systemtray/statusnotifierwatcher.cpp
@@ -0,0 +1,104 @@
+#include "statusnotifierwatcher.h"
+#include "statusnotifieritem_interface.h"
+#include "statusnotifierwatcheradaptor.h"
+
+#include
+#include
+#include
+
+StatusNotifierWatcher::StatusNotifierWatcher(QObject *parent)
+ : 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"));
+}
+
+QStringList StatusNotifierWatcher::RegisteredStatusNotifierItems() const
+{
+ return m_registeredServices;
+}
+
+bool StatusNotifierWatcher::IsStatusNotifierHostRegistered() const
+{
+ return !m_statusNotifierHostServices.isEmpty();
+}
+
+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 (m_registeredServices.contains(notifierItemId)) {
+ return;
+ }
+ m_serviceWatcher->addWatchedService(service);
+ if (QDBusConnection::sessionBus().interface()->isServiceRegistered(service).value()) {
+ // check if the service has registered a SystemTray object
+ org::kde::StatusNotifierItem trayclient(service, path, QDBusConnection::sessionBus());
+ if (trayclient.isValid()) {
+ qDebug() << "Registering" << notifierItemId << "to system tray";
+ m_registeredServices.append(notifierItemId);
+ emit StatusNotifierItemRegistered(notifierItemId);
+ } else {
+ m_serviceWatcher->removeWatchedService(service);
+ }
+ } else {
+ m_serviceWatcher->removeWatchedService(service);
+ }
+}
+
+void StatusNotifierWatcher::RegisterStatusNotifierHost(const QString &service)
+{
+ if (service.contains(QLatin1String("org.kde.StatusNotifierHost-")) && QDBusConnection::sessionBus().interface()->isServiceRegistered(service).value()
+ && !m_statusNotifierHostServices.contains(service)) {
+ qDebug() << "Registering" << service << "as system tray";
+
+ m_statusNotifierHostServices.insert(service);
+ m_serviceWatcher->addWatchedService(service);
+ emit StatusNotifierHostRegistered();
+ }
+}
+
+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();
+ }
+}
diff --git a/src/systemtray/statusnotifierwatcher.h b/src/systemtray/statusnotifierwatcher.h
new file mode 100644
index 0000000..7376f8e
--- /dev/null
+++ b/src/systemtray/statusnotifierwatcher.h
@@ -0,0 +1,45 @@
+#ifndef STATUSNOTIFIERWATCHER_H
+#define STATUSNOTIFIERWATCHER_H
+
+#include
+#include
+#include
+#include
+
+class QDBusServiceWatcher;
+class StatusNotifierWatcher : public QObject, protected QDBusContext
+{
+ Q_OBJECT
+ Q_SCRIPTABLE Q_PROPERTY(bool IsStatusNotifierHostRegistered READ IsStatusNotifierHostRegistered)
+ Q_SCRIPTABLE Q_PROPERTY(int ProtocolVersion READ protocolVersion)
+ Q_SCRIPTABLE Q_PROPERTY(QStringList RegisteredStatusNotifierItems READ RegisteredStatusNotifierItems)
+
+public:
+ explicit StatusNotifierWatcher(QObject *parent = nullptr);
+ ~StatusNotifierWatcher();
+
+ QStringList RegisteredStatusNotifierItems() const;
+ bool IsStatusNotifierHostRegistered() const;
+ int protocolVersion() const { return 0; }
+
+public 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 // STATUSNOTIFIERWATCHER_H
diff --git a/src/systemtray/systemtraymodel.cpp b/src/systemtray/systemtraymodel.cpp
new file mode 100644
index 0000000..1bd22c5
--- /dev/null
+++ b/src/systemtray/systemtraymodel.cpp
@@ -0,0 +1,144 @@
+#include "systemtraymodel.h"
+
+#include
+#include
+
+SystemTrayModel::SystemTrayModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+ m_hostName = "org.kde.StatusNotifierHost-" + QString::number(QCoreApplication::applicationPid());
+ QDBusConnection::sessionBus().interface()->registerService(m_hostName, QDBusConnectionInterface::DontQueueService);
+
+ m_watcher = new StatusNotifierWatcher;
+ m_watcher->RegisterStatusNotifierHost(m_hostName);
+ m_watcher->moveToThread(QApplication::instance()->thread());
+
+ connect(m_watcher, &StatusNotifierWatcher::StatusNotifierItemRegistered, this, &SystemTrayModel::onItemAdded);
+ connect(m_watcher, &StatusNotifierWatcher::StatusNotifierItemUnregistered, this, &SystemTrayModel::onItemRemoved);
+}
+
+SystemTrayModel::~SystemTrayModel()
+{
+ QDBusConnection::sessionBus().unregisterService(m_hostName);
+
+ delete m_watcher;
+}
+
+int SystemTrayModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return m_items.size();
+}
+
+QHash SystemTrayModel::roleNames() const
+{
+ QHash roles;
+ roles[IdRole] = "id";
+ roles[IconNameRole] = "iconName";
+ roles[IconRole] = "icon";
+ roles[TitleRole] = "title";
+ roles[ToolTipRole] = "toolTip";
+ return roles;
+}
+
+QVariant SystemTrayModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ StatusNotifierItemSource *item = m_items.at(index.row());
+
+ switch (role) {
+ case IdRole:
+ return item->id();
+ case IconNameRole:
+ return item->iconName();
+ case IconRole: {
+ if (!item->icon().isNull())
+ return item->icon();
+ else
+ return QVariant();
+ }
+ case TitleRole:
+ return item->title();
+ case ToolTipRole:
+ return item->tooltip();
+ }
+
+ return QVariant();
+}
+
+int SystemTrayModel::indexOf(const QString &id)
+{
+ for (StatusNotifierItemSource *item : m_items) {
+ if (item->id() == id)
+ return m_items.indexOf(item);
+ }
+
+ return -1;
+}
+
+StatusNotifierItemSource *SystemTrayModel::findItemById(const QString &id)
+{
+ int index = indexOf(id);
+
+ if (index == -1)
+ return nullptr;
+
+ return m_items.at(index);
+}
+
+void SystemTrayModel::leftButtonClick(const QString &id)
+{
+ StatusNotifierItemSource *item = findItemById(id);
+
+ if (item) {
+ QPoint p(QCursor::pos());
+ item->activate(p.x(), p.y());
+ }
+}
+
+void SystemTrayModel::rightButtonClick(const QString &id)
+{
+ StatusNotifierItemSource *item = findItemById(id);
+ if (item) {
+ QPoint p(QCursor::pos());
+ item->contextMenu(p.x(), p.y());
+ }
+}
+
+void SystemTrayModel::onItemAdded(const QString &service)
+{
+ StatusNotifierItemSource *source = new StatusNotifierItemSource(service, this);
+
+ connect(source, &StatusNotifierItemSource::updated, this, &SystemTrayModel::updated);
+
+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
+ m_items.append(source);
+ endInsertRows();
+}
+
+void SystemTrayModel::onItemRemoved(const QString &service)
+{
+ int index = indexOf(service);
+
+ if (index != -1) {
+ beginRemoveRows(QModelIndex(), index, index);
+ StatusNotifierItemSource *item = m_items.at(index);
+ m_items.removeAll(item);
+ endRemoveRows();
+ }
+}
+
+void SystemTrayModel::updated(StatusNotifierItemSource *item)
+{
+ if (!item)
+ return;
+
+ int idx = indexOf(item->id());
+
+ // update
+ if (idx != -1) {
+ dataChanged(index(idx, 0), index(idx, 0));
+ }
+}
diff --git a/src/statusnotifier/statusnotifiermodel.h b/src/systemtray/systemtraymodel.h
similarity index 63%
rename from src/statusnotifier/statusnotifiermodel.h
rename to src/systemtray/systemtraymodel.h
index 40245e1..3142e2c 100644
--- a/src/statusnotifier/statusnotifiermodel.h
+++ b/src/systemtray/systemtraymodel.h
@@ -1,17 +1,12 @@
-#ifndef STATUSNOTIFIERMODEL_H
-#define STATUSNOTIFIERMODEL_H
+#ifndef SYSTEMTRAYMODEL_H
+#define SYSTEMTRAYMODEL_H
#include
-#include
-#include
-#include
-
-#include "statusnotifieritemsource.h"
#include "statusnotifierwatcher.h"
-#include "sniasync.h"
+#include "statusnotifieritemsource.h"
-class StatusNotifierModel : public QAbstractListModel
+class SystemTrayModel : public QAbstractListModel
{
Q_OBJECT
@@ -19,16 +14,14 @@ public:
enum Roles {
IdRole = Qt::UserRole + 1,
IconNameRole,
- IconBytesRole,
IconRole,
TitleRole,
ToolTipRole
};
- explicit StatusNotifierModel(QObject *parent = nullptr);
- ~StatusNotifierModel();
+ explicit SystemTrayModel(QObject *parent = nullptr);
+ ~SystemTrayModel();
- // Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QHash roleNames() const override;
@@ -40,14 +33,15 @@ public:
Q_INVOKABLE void leftButtonClick(const QString &id);
Q_INVOKABLE void rightButtonClick(const QString &id);
-public slots:
- void itemAdded(QString serviceAndPath);
- void itemRemoved(const QString &serviceAndPath);
+private slots:
+ void onItemAdded(const QString &service);
+ void onItemRemoved(const QString &service);
void updated(StatusNotifierItemSource *item);
private:
StatusNotifierWatcher *m_watcher;
QList m_items;
+ QString m_hostName;
};
-#endif // STATUSNOTIFIERMODEL_H
+#endif // SYSTEMTRAYMODEL_H
diff --git a/src/systemtray/systemtraytypedefs.h b/src/systemtray/systemtraytypedefs.h
new file mode 100644
index 0000000..0024f39
--- /dev/null
+++ b/src/systemtray/systemtraytypedefs.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * *
+ * Copyright (C) 2009 Marco Martin *
+ * *
+ * 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 SYSTEMTRAYTYPEDEFS_H
+#define SYSTEMTRAYTYPEDEFS_H
+
+#include
+#include
+#include
+#include
+
+struct KDbusImageStruct {
+ int width;
+ int height;
+ QByteArray data;
+};
+
+typedef QVector KDbusImageVector;
+
+struct KDbusToolTipStruct {
+ QString icon;
+ KDbusImageVector image;
+ QString title;
+ QString subTitle;
+};
+
+Q_DECLARE_METATYPE(KDbusImageStruct)
+Q_DECLARE_METATYPE(KDbusImageVector)
+Q_DECLARE_METATYPE(KDbusToolTipStruct)
+
+#endif
\ No newline at end of file
diff --git a/src/systemtray/systemtraytypes.cpp b/src/systemtray/systemtraytypes.cpp
new file mode 100644
index 0000000..c690c58
--- /dev/null
+++ b/src/systemtray/systemtraytypes.cpp
@@ -0,0 +1,128 @@
+/***************************************************************************
+ * *
+ * Copyright (C) 2009 Marco Martin *
+ * *
+ * 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
+#include "systemtraytypes.h"
+
+// Marshall the ImageStruct data into a D-BUS argument
+const QDBusArgument &operator<<(QDBusArgument &argument, const KDbusImageStruct &icon)
+{
+ argument.beginStructure();
+ argument << icon.width;
+ argument << icon.height;
+ argument << icon.data;
+ argument.endStructure();
+ return argument;
+}
+
+// Retrieve the ImageStruct data from the D-BUS argument
+const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusImageStruct &icon)
+{
+ qint32 width = 0;
+ qint32 height = 0;
+ QByteArray data;
+
+ if (argument.currentType() == QDBusArgument::StructureType) {
+ argument.beginStructure();
+ // qCDebug(DATAENGINE_SNI)() << "begun structure";
+ argument >> width;
+ // qCDebug(DATAENGINE_SNI)() << width;
+ argument >> height;
+ // qCDebug(DATAENGINE_SNI)() << height;
+ argument >> data;
+ // qCDebug(DATAENGINE_SNI)() << data.size();
+ argument.endStructure();
+ }
+
+ icon.width = width;
+ icon.height = height;
+ icon.data = data;
+
+ return argument;
+}
+
+// Marshall the ImageVector data into a D-BUS argument
+const QDBusArgument &operator<<(QDBusArgument &argument, const KDbusImageVector &iconVector)
+{
+ argument.beginArray(qMetaTypeId());
+ for (int i = 0; i < iconVector.size(); ++i) {
+ argument << iconVector[i];
+ }
+ argument.endArray();
+ return argument;
+}
+
+// Retrieve the ImageVector data from the D-BUS argument
+const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusImageVector &iconVector)
+{
+ iconVector.clear();
+
+ if (argument.currentType() == QDBusArgument::ArrayType) {
+ argument.beginArray();
+
+ while (!argument.atEnd()) {
+ KDbusImageStruct element;
+ argument >> element;
+ iconVector.append(element);
+ }
+
+ argument.endArray();
+ }
+
+ return argument;
+}
+
+// Marshall the ToolTipStruct data into a D-BUS argument
+const QDBusArgument &operator<<(QDBusArgument &argument, const KDbusToolTipStruct &toolTip)
+{
+ argument.beginStructure();
+ argument << toolTip.icon;
+ argument << toolTip.image;
+ argument << toolTip.title;
+ argument << toolTip.subTitle;
+ argument.endStructure();
+
+ return argument;
+}
+
+// Retrieve the ToolTipStruct data from the D-BUS argument
+const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusToolTipStruct &toolTip)
+{
+ QString icon;
+ KDbusImageVector image;
+ QString title;
+ QString subTitle;
+
+ if (argument.currentType() == QDBusArgument::StructureType) {
+ argument.beginStructure();
+ argument >> icon;
+ argument >> image;
+ argument >> title;
+ argument >> subTitle;
+ argument.endStructure();
+ }
+
+ toolTip.icon = icon;
+ toolTip.image = image;
+ toolTip.title = title;
+ toolTip.subTitle = subTitle;
+
+ return argument;
+}
diff --git a/src/systemtray/systemtraytypes.h b/src/systemtray/systemtraytypes.h
new file mode 100644
index 0000000..8e1b64f
--- /dev/null
+++ b/src/systemtray/systemtraytypes.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * *
+ * Copyright (C) 2009 Marco Martin *
+ * *
+ * 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 SYSTEMTRAYTYPES_H
+#define SYSTEMTRAYTYPES_H
+
+#include
+
+#include "systemtraytypedefs.h"
+
+const QDBusArgument &operator<<(QDBusArgument &argument, const KDbusImageStruct &icon);
+const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusImageStruct &icon);
+
+const QDBusArgument &operator<<(QDBusArgument &argument, const KDbusImageVector &iconVector);
+const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusImageVector &iconVector);
+
+const QDBusArgument &operator<<(QDBusArgument &argument, const KDbusToolTipStruct &toolTip);
+const QDBusArgument &operator>>(const QDBusArgument &argument, KDbusToolTipStruct &toolTip);
+
+#endif
diff --git a/svg/light/bluetooth-symbolic.svg b/svg/light/bluetooth-symbolic.svg
index 7e9605c..e5c5f2f 100644
--- a/svg/light/bluetooth-symbolic.svg
+++ b/svg/light/bluetooth-symbolic.svg
@@ -1,16 +1,62 @@
-
-