Fix the switch between dark mode
This commit is contained in:
parent
4e2003f36f
commit
360764be8d
41 changed files with 1873 additions and 986 deletions
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"*.moc": "cpp"
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ set(CMAKE_AUTORCC ON)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
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(Qt5 REQUIRED ${QT})
|
||||||
find_package(KF5WindowSystem REQUIRED)
|
find_package(KF5WindowSystem REQUIRED)
|
||||||
find_package(dbusmenu-qt5 REQUIRED)
|
find_package(dbusmenu-qt5 REQUIRED)
|
||||||
|
@ -36,19 +36,36 @@ set(SRCS
|
||||||
|
|
||||||
src/appearance.cpp
|
src/appearance.cpp
|
||||||
src/fakewindow.cpp
|
src/fakewindow.cpp
|
||||||
|
src/iconitem.cpp
|
||||||
|
src/managedtexturenode.cpp
|
||||||
|
|
||||||
src/statusnotifier/dbustypes.cpp
|
src/systemtray/statusnotifieritemjob.cpp
|
||||||
src/statusnotifier/sniasync.cpp
|
src/systemtray/statusnotifieritemsource.cpp
|
||||||
src/statusnotifier/statusnotifieriteminterface.cpp
|
src/systemtray/systemtraytypes.cpp
|
||||||
src/statusnotifier/statusnotifiermodel.cpp
|
src/systemtray/systemtraytypedefs.h
|
||||||
src/statusnotifier/statusnotifierwatcher.cpp
|
src/systemtray/systemtraymodel.cpp
|
||||||
src/statusnotifier/statusnotifieritemsource.cpp
|
src/systemtray/statusnotifierwatcher.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(RESOURCES
|
set(RESOURCES
|
||||||
resources.qrc
|
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})
|
add_executable(${PROJECT_NAME} ${SRCS} ${DBUS_SRCS} ${RESOURCES})
|
||||||
target_link_libraries(${PROJECT_NAME}
|
target_link_libraries(${PROJECT_NAME}
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
|
@ -58,6 +75,7 @@ target_link_libraries(${PROJECT_NAME}
|
||||||
Qt5::X11Extras
|
Qt5::X11Extras
|
||||||
Qt5::Concurrent
|
Qt5::Concurrent
|
||||||
Qt5::DBus
|
Qt5::DBus
|
||||||
|
Qt5::Svg
|
||||||
|
|
||||||
MeuiKit
|
MeuiKit
|
||||||
|
|
||||||
|
|
|
@ -50,18 +50,12 @@ Item {
|
||||||
dragStarted = false
|
dragStarted = false
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
IconItem {
|
||||||
id: icon
|
id: icon
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
source: iconName ? iconName.indexOf("/") === 0 || iconName.indexOf("file://") === 0 || iconName.indexOf("qrc") === 0
|
width: control.iconSize
|
||||||
? iconName : "image://icontheme/" + iconName : iconName
|
height: control.iconSize
|
||||||
sourceSize.width: control.iconSize
|
source: iconName
|
||||||
sourceSize.height: control.iconSize
|
|
||||||
width: sourceSize.width
|
|
||||||
height: sourceSize.height
|
|
||||||
asynchronous: false
|
|
||||||
smooth: true
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
visible: !dragStarted
|
visible: !dragStarted
|
||||||
|
|
||||||
|
|
9
qml/StatusNotifierItem.qml
Normal file
9
qml/StatusNotifierItem.qml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
sourceComponent: _item
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: _item
|
||||||
|
}
|
||||||
|
}
|
23
qml/main.qml
23
qml/main.qml
|
@ -6,6 +6,7 @@ import QtGraphicalEffects 1.0
|
||||||
import Cyber.NetworkManagement 1.0 as NM
|
import Cyber.NetworkManagement 1.0 as NM
|
||||||
import Cutefish.Dock 1.0
|
import Cutefish.Dock 1.0
|
||||||
import MeuiKit 1.0 as Meui
|
import MeuiKit 1.0 as Meui
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
@ -71,7 +72,7 @@ Item {
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: 0
|
duration: 200
|
||||||
easing.type: Easing.InOutQuad
|
easing.type: Easing.InOutQuad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,34 +169,22 @@ Item {
|
||||||
orientation: isHorizontal ? Qt.Horizontal : Qt.Vertical
|
orientation: isHorizontal ? Qt.Horizontal : Qt.Vertical
|
||||||
layoutDirection: Qt.RightToLeft
|
layoutDirection: Qt.RightToLeft
|
||||||
interactive: false
|
interactive: false
|
||||||
model: trayModel
|
model: SystemTrayModel { id: trayModel }
|
||||||
spacing: Meui.Units.smallSpacing / 2
|
spacing: Meui.Units.smallSpacing / 2
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
StatusNotifierModel {
|
|
||||||
id: trayModel
|
|
||||||
}
|
|
||||||
|
|
||||||
onCountChanged: delayCalcIconSize()
|
onCountChanged: delayCalcIconSize()
|
||||||
|
|
||||||
delegate: StandardItem {
|
delegate: StandardItem {
|
||||||
height: trayView.itemHeight
|
height: trayView.itemHeight
|
||||||
width: trayView.itemWidth
|
width: trayView.itemWidth
|
||||||
|
|
||||||
Image {
|
IconItem {
|
||||||
id: trayIcon
|
id: iconItem
|
||||||
anchors.centerIn: parent
|
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
|
width: root.trayItemSize
|
||||||
height: root.trayItemSize
|
height: root.trayItemSize
|
||||||
sourceSize.width: root.trayItemSize
|
source: model.icon ? model.icon : model.iconName
|
||||||
sourceSize.height: root.trayItemSize
|
|
||||||
asynchronous: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: trayModel.leftButtonClick(id)
|
onClicked: trayModel.leftButtonClick(id)
|
||||||
|
|
|
@ -102,5 +102,6 @@
|
||||||
<file>svg/light/dark-mode.svg</file>
|
<file>svg/light/dark-mode.svg</file>
|
||||||
<file>svg/dark/dark-mode.svg</file>
|
<file>svg/dark/dark-mode.svg</file>
|
||||||
<file>svg/dark/bluetooth-symbolic.svg</file>
|
<file>svg/dark/bluetooth-symbolic.svg</file>
|
||||||
|
<file>qml/StatusNotifierItem.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
414
src/iconitem.cpp
Normal file
414
src/iconitem.cpp
Normal file
|
@ -0,0 +1,414 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 CutefishOS Team.
|
||||||
|
*
|
||||||
|
* Author: cutefish <cutefishos@foxmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "iconitem.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPaintEngine>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QQuickWindow>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QImageReader>
|
||||||
|
|
||||||
|
#include "managedtexturenode.h"
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
typename std::enable_if <!std::is_integral<T>(), bool>::type almost_equal(T x, T y, int ulp)
|
||||||
|
{
|
||||||
|
return std::abs(x - y) <std::numeric_limits<T>::epsilon() * std::abs(x + y) * ulp
|
||||||
|
|| std::abs(x - y) <std::numeric_limits<T>::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<QIcon>() && !source.value<QIcon>().name().isEmpty()) {
|
||||||
|
sourceString = source.value<QIcon>().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<QIcon>();
|
||||||
|
if (icon.isNull()) {
|
||||||
|
icon = QIcon::fromTheme(sourceString, QIcon::fromTheme("application-x-desktop"));
|
||||||
|
}
|
||||||
|
m_iconItemSource.reset(new QIconSource(icon, this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (source.canConvert<QIcon>()) {
|
||||||
|
m_iconItemSource.reset(new QIconSource(source.value<QIcon>(), this));
|
||||||
|
} else if (source.canConvert<QImage>()) {
|
||||||
|
m_iconItemSource.reset(new QImageSource(source.value<QImage>(), 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<ManagedTextureNode *>(oldNode);
|
||||||
|
|
||||||
|
if (!textureNode || m_textureChanged) {
|
||||||
|
delete oldNode;
|
||||||
|
textureNode = new ManagedTextureNode;
|
||||||
|
textureNode->setTexture(QSharedPointer<QSGTexture>(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();
|
||||||
|
}
|
100
src/iconitem.h
Normal file
100
src/iconitem.h
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 CutefishOS Team.
|
||||||
|
*
|
||||||
|
* Author: cutefish <cutefishos@foxmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ICONITEM_H
|
||||||
|
#define ICONITEM_H
|
||||||
|
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
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<IconItemSource> 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
|
|
@ -30,8 +30,9 @@
|
||||||
#include "battery.h"
|
#include "battery.h"
|
||||||
#include "brightness.h"
|
#include "brightness.h"
|
||||||
#include "controlcenterdialog.h"
|
#include "controlcenterdialog.h"
|
||||||
#include "statusnotifier/statusnotifiermodel.h"
|
#include "systemtray/systemtraymodel.h"
|
||||||
#include "appearance.h"
|
#include "appearance.h"
|
||||||
|
#include "iconitem.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -43,8 +44,9 @@ int main(int argc, char *argv[])
|
||||||
qmlRegisterType<Battery>("Cutefish.Dock", 1, 0, "Battery");
|
qmlRegisterType<Battery>("Cutefish.Dock", 1, 0, "Battery");
|
||||||
qmlRegisterType<Brightness>("Cutefish.Dock", 1, 0, "Brightness");
|
qmlRegisterType<Brightness>("Cutefish.Dock", 1, 0, "Brightness");
|
||||||
qmlRegisterType<ControlCenterDialog>("Cutefish.Dock", 1, 0, "ControlCenterDialog");
|
qmlRegisterType<ControlCenterDialog>("Cutefish.Dock", 1, 0, "ControlCenterDialog");
|
||||||
qmlRegisterType<StatusNotifierModel>("Cutefish.Dock", 1, 0, "StatusNotifierModel");
|
qmlRegisterType<SystemTrayModel>("Cutefish.Dock", 1, 0, "SystemTrayModel");
|
||||||
qmlRegisterType<Appearance>("Cutefish.Dock", 1, 0, "Appearance");
|
qmlRegisterType<Appearance>("Cutefish.Dock", 1, 0, "Appearance");
|
||||||
|
qmlRegisterType<IconItem>("Cutefish.Dock", 1, 0, "IconItem");
|
||||||
|
|
||||||
QString qmFilePath = QString("%1/%2.qm").arg("/usr/share/cutefish-dock/translations/").arg(QLocale::system().name());
|
QString qmFilePath = QString("%1/%2.qm").arg("/usr/share/cutefish-dock/translations/").arg(QLocale::system().name());
|
||||||
if (QFile::exists(qmFilePath)) {
|
if (QFile::exists(qmFilePath)) {
|
||||||
|
|
|
@ -51,10 +51,11 @@ MainWindow::MainWindow(QQuickView *parent)
|
||||||
engine()->rootContext()->setContextProperty("Settings", m_settings);
|
engine()->rootContext()->setContextProperty("Settings", m_settings);
|
||||||
engine()->rootContext()->setContextProperty("mainWindow", this);
|
engine()->rootContext()->setContextProperty("mainWindow", this);
|
||||||
|
|
||||||
|
setSource(QUrl(QStringLiteral("qrc:/qml/main.qml")));
|
||||||
setResizeMode(QQuickView::SizeRootObjectToView);
|
setResizeMode(QQuickView::SizeRootObjectToView);
|
||||||
setScreen(qApp->primaryScreen());
|
setScreen(qApp->primaryScreen());
|
||||||
setSource(QUrl(QStringLiteral("qrc:/qml/main.qml")));
|
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
|
|
||||||
initSlideWindow();
|
initSlideWindow();
|
||||||
resizeWindow();
|
resizeWindow();
|
||||||
onVisibilityChanged();
|
onVisibilityChanged();
|
||||||
|
|
|
@ -40,9 +40,6 @@ signals:
|
||||||
void iconSizeChanged();
|
void iconSizeChanged();
|
||||||
void positionChanged();
|
void positionChanged();
|
||||||
|
|
||||||
protected:
|
|
||||||
void event(QEvent *e, QObject *obj);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QRect windowRect() const;
|
QRect windowRect() const;
|
||||||
void resizeWindow();
|
void resizeWindow();
|
||||||
|
|
16
src/managedtexturenode.cpp
Normal file
16
src/managedtexturenode.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||||
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "managedtexturenode.h"
|
||||||
|
|
||||||
|
ManagedTextureNode::ManagedTextureNode()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ManagedTextureNode::setTexture(QSharedPointer<QSGTexture> texture)
|
||||||
|
{
|
||||||
|
m_texture = texture;
|
||||||
|
QSGSimpleTextureNode::setTexture(texture.data());
|
||||||
|
}
|
43
src/managedtexturenode.h
Normal file
43
src/managedtexturenode.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||||
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MANAGEDTEXTURENODE_H
|
||||||
|
#define MANAGEDTEXTURENODE_H
|
||||||
|
|
||||||
|
#include <QSGSimpleTextureNode>
|
||||||
|
#include <QSGTexture>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <qglobal.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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<QSGTexture> texture);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<QSGTexture> m_texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
#include <QDBusArgument>
|
|
||||||
|
|
||||||
#ifndef DBUSTYPES_H
|
|
||||||
#define DBUSTYPES_H
|
|
||||||
|
|
||||||
struct IconPixmap {
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
QByteArray bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef QList<IconPixmap> IconPixmapList;
|
|
||||||
|
|
||||||
struct ToolTip {
|
|
||||||
QString iconName;
|
|
||||||
QList<IconPixmap> 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
|
|
|
@ -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<QDBusVariant> 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);
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
#if !defined(SNIASYNC_H)
|
|
||||||
#define SNIASYNC_H
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include "statusnotifieriteminterface.h"
|
|
||||||
|
|
||||||
template<typename>
|
|
||||||
struct remove_class_type { using type = void; }; // bluff
|
|
||||||
template<typename C, typename R, typename... ArgTypes>
|
|
||||||
struct remove_class_type<R (C::*)(ArgTypes...)> { using type = R(ArgTypes...); };
|
|
||||||
template<typename C, typename R, typename... ArgTypes>
|
|
||||||
struct remove_class_type<R (C::*)(ArgTypes...) const> { using type = R(ArgTypes...); };
|
|
||||||
|
|
||||||
template <typename L>
|
|
||||||
class call_sig_helper
|
|
||||||
{
|
|
||||||
template <typename L1>
|
|
||||||
static decltype(&L1::operator()) test(int);
|
|
||||||
template <typename L1>
|
|
||||||
static void test(...); //bluff
|
|
||||||
public:
|
|
||||||
using type = decltype(test<L>(0));
|
|
||||||
};
|
|
||||||
template <typename L>
|
|
||||||
struct call_signature : public remove_class_type<typename call_sig_helper<L>::type> {};
|
|
||||||
template <typename R, typename... ArgTypes>
|
|
||||||
struct call_signature<R (ArgTypes...)> { using type = R (ArgTypes...); };
|
|
||||||
template <typename R, typename... ArgTypes>
|
|
||||||
struct call_signature<R (*)(ArgTypes...)> { using type = R (ArgTypes...); };
|
|
||||||
template <typename C, typename R, typename... ArgTypes>
|
|
||||||
struct call_signature<R (C::*)(ArgTypes...)> { using type = R (ArgTypes...); };
|
|
||||||
template<typename C, typename R, typename... ArgTypes>
|
|
||||||
struct call_signature<R (C::*)(ArgTypes...) const> { using type = R(ArgTypes...); };
|
|
||||||
|
|
||||||
template <typename> struct is_valid_signature : public std::false_type {};
|
|
||||||
template <typename Arg>
|
|
||||||
struct is_valid_signature<void (Arg)> : 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 <typename F>
|
|
||||||
inline void propertyGetAsync(QString const &name, F finished)
|
|
||||||
{
|
|
||||||
static_assert(is_valid_signature<typename call_signature<F>::type>::value, "need callable (lambda, *function, callable obj) (Arg) -> void");
|
|
||||||
connect(new QDBusPendingCallWatcher{asyncPropGet(name), this},
|
|
||||||
&QDBusPendingCallWatcher::finished,
|
|
||||||
[this, finished, name] (QDBusPendingCallWatcher * call)
|
|
||||||
{
|
|
||||||
QDBusPendingReply<QVariant> reply = *call;
|
|
||||||
if (reply.isError())
|
|
||||||
qDebug().noquote().nospace() << "Error on DBus request(" << m_sni.service() << ',' << m_sni.path() << "): " << reply.error();
|
|
||||||
finished(qdbus_cast<typename std::function<typename call_signature<F>::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<QDBusVariant> asyncPropGet(QString const & property);
|
|
||||||
|
|
||||||
private:
|
|
||||||
org::kde::StatusNotifierItem m_sni;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -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()
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
#ifndef STATUSNOTIFIERITEMINTERFACE_H
|
|
||||||
#define STATUSNOTIFIERITEMINTERFACE_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QList>
|
|
||||||
#include <QMap>
|
|
||||||
#include <QString>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QtDBus>
|
|
||||||
#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<QVariant> argumentList;
|
|
||||||
argumentList << QVariant::fromValue(x) << QVariant::fromValue(y);
|
|
||||||
return asyncCallWithArgumentList(QLatin1String("Activate"), argumentList);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QDBusPendingReply<> ContextMenu(int x, int y)
|
|
||||||
{
|
|
||||||
QList<QVariant> argumentList;
|
|
||||||
argumentList << QVariant::fromValue(x) << QVariant::fromValue(y);
|
|
||||||
return asyncCallWithArgumentList(QLatin1String("ContextMenu"), argumentList);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QDBusPendingReply<> Scroll(int delta, const QString &orientation)
|
|
||||||
{
|
|
||||||
QList<QVariant> argumentList;
|
|
||||||
argumentList << QVariant::fromValue(delta) << QVariant::fromValue(orientation);
|
|
||||||
return asyncCallWithArgumentList(QLatin1String("Scroll"), argumentList);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QDBusPendingReply<> SecondaryActivate(int x, int y)
|
|
||||||
{
|
|
||||||
QList<QVariant> 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
|
|
|
@ -1,211 +0,0 @@
|
||||||
#include "statusnotifieritemsource.h"
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDBusMessage>
|
|
||||||
#include <QDBusPendingCall>
|
|
||||||
#include <QDBusPendingReply>
|
|
||||||
#include <QVariantMap>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QPixmap>
|
|
||||||
|
|
||||||
#include <dbusmenu-qt5/dbusmenuimporter.h>
|
|
||||||
|
|
||||||
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<QVariantMap> 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<QDBusArgument>() >> toolTip;
|
|
||||||
m_tooltip = toolTip.title;
|
|
||||||
|
|
||||||
// Menu
|
|
||||||
QString menuObjectPath = properties[QStringLiteral("Menu")].value<QDBusObjectPath>().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<QDBusArgument>() >> 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<uchar*>(iconPixmap.bytes.data());
|
|
||||||
for (const uchar *src = image.constBits(); src < end; src += 4, dest += 4)
|
|
||||||
qToUnaligned(qToBigEndian<quint32>(qFromUnaligned<quint32>(src)), dest);
|
|
||||||
|
|
||||||
icon.addPixmap(QPixmap::fromImage(image));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return icon.pixmap(QSize(24, 24)).toImage();
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
#ifndef STATUSNOTIFIERITEMSOURCE_H
|
|
||||||
#define STATUSNOTIFIERITEMSOURCE_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QIcon>
|
|
||||||
|
|
||||||
#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
|
|
|
@ -1,162 +0,0 @@
|
||||||
#include "statusnotifiermodel.h"
|
|
||||||
#include <QFutureWatcher>
|
|
||||||
#include <QtConcurrent>
|
|
||||||
#include <QDBusConnectionInterface>
|
|
||||||
#include <QApplication>
|
|
||||||
|
|
||||||
#include <QPixmap>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QCursor>
|
|
||||||
|
|
||||||
StatusNotifierModel::StatusNotifierModel(QObject *parent)
|
|
||||||
: QAbstractListModel(parent)
|
|
||||||
, m_watcher(nullptr)
|
|
||||||
{
|
|
||||||
QFutureWatcher<StatusNotifierWatcher *> * futureWatcher = new QFutureWatcher<StatusNotifierWatcher *>;
|
|
||||||
connect(futureWatcher, &QFutureWatcher<StatusNotifierWatcher *>::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<StatusNotifierWatcher *> 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<int, QByteArray> StatusNotifierModel::roleNames() const
|
|
||||||
{
|
|
||||||
QHash<int, QByteArray> 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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
#include "statusnotifierwatcher.h"
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDBusConnectionInterface>
|
|
||||||
|
|
||||||
StatusNotifierWatcher::StatusNotifierWatcher(QObject *parent) : QObject(parent)
|
|
||||||
{
|
|
||||||
qRegisterMetaType<IconPixmap>("IconPixmap");
|
|
||||||
qDBusRegisterMetaType<IconPixmap>();
|
|
||||||
qRegisterMetaType<IconPixmapList>("IconPixmapList");
|
|
||||||
qDBusRegisterMetaType<IconPixmapList>();
|
|
||||||
qRegisterMetaType<ToolTip>("ToolTip");
|
|
||||||
qDBusRegisterMetaType<ToolTip>();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
#ifndef STATUSNOTIFIERWATCHER_H
|
|
||||||
#define STATUSNOTIFIERWATCHER_H
|
|
||||||
|
|
||||||
#include <QDBusConnection>
|
|
||||||
#include <QDBusContext>
|
|
||||||
#include <QDBusMessage>
|
|
||||||
#include <QDBusMetaType>
|
|
||||||
#include <QDBusServiceWatcher>
|
|
||||||
|
|
||||||
#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
|
|
18
src/systemtray/CMakeLists.txt
Normal file
18
src/systemtray/CMakeLists.txt
Normal file
|
@ -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)
|
27
src/systemtray/org.freedesktop.DBus.Properties.xml
Normal file
27
src/systemtray/org.freedesktop.DBus.Properties.xml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
<node>
|
||||||
|
<interface name="org.freedesktop.DBus.Properties">
|
||||||
|
<method name="Get">
|
||||||
|
<arg type="s" name="interface_name" direction="in"/>
|
||||||
|
<arg type="s" name="property_name" direction="in"/>
|
||||||
|
<arg type="v" name="value" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="GetAll">
|
||||||
|
<arg type="s" name="interface_name" direction="in"/>
|
||||||
|
<arg type="a{sv}" name="properties" direction="out"/>
|
||||||
|
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||||
|
</method>
|
||||||
|
<method name="Set">
|
||||||
|
<arg type="s" name="interface_name" direction="in"/>
|
||||||
|
<arg type="s" name="property_name" direction="in"/>
|
||||||
|
<arg type="v" name="value" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<signal name="PropertiesChanged">
|
||||||
|
<arg type="s" name="interface_name"/>
|
||||||
|
<arg type="a{sv}" name="changed_properties"/>
|
||||||
|
<arg type="as" name="invalidated_properties"/>
|
||||||
|
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
|
<!-- vim:set sw=2 sts=2 et ft=xml: -->
|
96
src/systemtray/org.kde.StatusNotifierItem.xml
Normal file
96
src/systemtray/org.kde.StatusNotifierItem.xml
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
<interface name="org.kde.StatusNotifierItem">
|
||||||
|
|
||||||
|
<property name="Category" type="s" access="read"/>
|
||||||
|
<property name="Id" type="s" access="read"/>
|
||||||
|
<property name="Title" type="s" access="read"/>
|
||||||
|
<property name="Status" type="s" access="read"/>
|
||||||
|
<property name="WindowId" type="i" access="read"/>
|
||||||
|
|
||||||
|
<!-- An additional path to add to the theme search path to find the icons specified above. -->
|
||||||
|
<!-- <property name="IconThemePath" type="s" access="read"/> -->
|
||||||
|
<!-- <property name="Menu" type="o" access="read"/> -->
|
||||||
|
<property name="ItemIsMenu" type="b" access="read"/>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- main icon -->
|
||||||
|
<!-- names are preferred over pixmaps -->
|
||||||
|
<!-- <property name="IconName" type="s" access="read"/> -->
|
||||||
|
|
||||||
|
<!--struct containing width, height and image data-->
|
||||||
|
<property name="IconPixmap" type="(iiay)" access="read">
|
||||||
|
<annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusImageVector"/>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<!-- <property name="OverlayIconName" type="s" access="read"/> -->
|
||||||
|
|
||||||
|
<!-- <property name="OverlayIconPixmap" type="(iiay)" access="read"> -->
|
||||||
|
<!-- <annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusImageVector"/> -->
|
||||||
|
<!-- </property> -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Requesting attention icon -->
|
||||||
|
<!-- <property name="AttentionIconName" type="s" access="read"/> -->
|
||||||
|
|
||||||
|
<!--same definition as image-->
|
||||||
|
<!-- <property name="AttentionIconPixmap" type="(iiay)" access="read"> -->
|
||||||
|
<!-- <annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusImageVector"/> -->
|
||||||
|
<!-- </property> -->
|
||||||
|
|
||||||
|
<!-- <property name="AttentionMovieName" type="s" access="read"/> -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- tooltip data -->
|
||||||
|
|
||||||
|
<!--(iiay) is an image-->
|
||||||
|
<!-- <property name="ToolTip" type="(s(iiay)ss)" access="read"> -->
|
||||||
|
<!-- <annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusToolTipStruct"/> -->
|
||||||
|
<!-- </property> -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- interaction: the systemtray wants the application to do something -->
|
||||||
|
<method name="ContextMenu">
|
||||||
|
<!-- we're passing the coordinates of the icon, so the app knows where to put the popup window -->
|
||||||
|
<arg name="x" type="i" direction="in"/>
|
||||||
|
<arg name="y" type="i" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="Activate">
|
||||||
|
<arg name="x" type="i" direction="in"/>
|
||||||
|
<arg name="y" type="i" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="SecondaryActivate">
|
||||||
|
<arg name="x" type="i" direction="in"/>
|
||||||
|
<arg name="y" type="i" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="Scroll">
|
||||||
|
<arg name="delta" type="i" direction="in"/>
|
||||||
|
<arg name="orientation" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!-- Signals: the client wants to change something in the status-->
|
||||||
|
<signal name="NewTitle">
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<signal name="NewIcon">
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<signal name="NewAttentionIcon">
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<signal name="NewOverlayIcon">
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<signal name="NewToolTip">
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<signal name="NewStatus">
|
||||||
|
<arg name="status" type="s"/>
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
</node>
|
42
src/systemtray/org.kde.StatusNotifierWatcher.xml
Normal file
42
src/systemtray/org.kde.StatusNotifierWatcher.xml
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
<interface name="org.kde.StatusNotifierWatcher">
|
||||||
|
|
||||||
|
<!-- methods -->
|
||||||
|
<method name="RegisterStatusNotifierItem">
|
||||||
|
<arg name="service" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="RegisterStatusNotifierHost">
|
||||||
|
<arg name="service" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- properties -->
|
||||||
|
|
||||||
|
<property name="RegisteredStatusNotifierItems" type="as" access="read">
|
||||||
|
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QStringList"/>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="IsStatusNotifierHostRegistered" type="b" access="read"/>
|
||||||
|
|
||||||
|
<property name="ProtocolVersion" type="i" access="read"/>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- signals -->
|
||||||
|
|
||||||
|
<signal name="StatusNotifierItemRegistered">
|
||||||
|
<arg type="s"/>
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<signal name="StatusNotifierItemUnregistered">
|
||||||
|
<arg type="s"/>
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<signal name="StatusNotifierHostRegistered">
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<signal name="StatusNotifierHostUnregistered">
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
</node>
|
25
src/systemtray/statusnotifieritemjob.cpp
Normal file
25
src/systemtray/statusnotifieritemjob.cpp
Normal file
|
@ -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)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
27
src/systemtray/statusnotifieritemjob.h
Normal file
27
src/systemtray/statusnotifieritemjob.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef STATUSNOTIFIERITEMJOB_H
|
||||||
|
#define STATUSNOTIFIERITEMJOB_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
|
#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
|
355
src/systemtray/statusnotifieritemsource.cpp
Normal file
355
src/systemtray/statusnotifieritemsource.cpp
Normal file
|
@ -0,0 +1,355 @@
|
||||||
|
#include "statusnotifieritemsource.h"
|
||||||
|
#include "systemtraytypes.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <dbusmenuimporter.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
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<KDbusImageStruct>();
|
||||||
|
qDBusRegisterMetaType<KDbusImageVector>();
|
||||||
|
qDBusRegisterMetaType<KDbusToolTipStruct>();
|
||||||
|
|
||||||
|
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<QVariantMap> 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<QDBusArgument>() >> toolTip;
|
||||||
|
m_tooltip = toolTip.title;
|
||||||
|
|
||||||
|
// Icon
|
||||||
|
KDbusImageVector image;
|
||||||
|
properties[QStringLiteral("IconPixmap")].value<QDBusArgument>() >> 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<QDBusArgument>() >> 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<QDBusArgument>() >> image;
|
||||||
|
// if (!image.isEmpty()) {
|
||||||
|
// m_icon = imageVectorToPixmap(image);
|
||||||
|
// changed = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// properties[QStringLiteral("IconPixmap")].value<QDBusArgument>() >> image;
|
||||||
|
// if (!image.isEmpty()) {
|
||||||
|
// m_icon = imageVectorToPixmap(image);
|
||||||
|
// changed = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Menu
|
||||||
|
if (!m_menuImporter) {
|
||||||
|
QString menuObjectPath = properties[QStringLiteral("Menu")].value<QDBusObjectPath>().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<void> 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<const uchar *>(dataRef->data()),
|
||||||
|
image.width,
|
||||||
|
image.height,
|
||||||
|
QImage::Format_ARGB32,
|
||||||
|
[](void *ptr) {
|
||||||
|
delete static_cast<QByteArray *>(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;
|
||||||
|
}
|
72
src/systemtray/statusnotifieritemsource.h
Normal file
72
src/systemtray/statusnotifieritemsource.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#ifndef STATUSNOTIFIERITEMSOURCE_H
|
||||||
|
#define STATUSNOTIFIERITEMSOURCE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QDBusPendingCallWatcher>
|
||||||
|
|
||||||
|
#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
|
104
src/systemtray/statusnotifierwatcher.cpp
Normal file
104
src/systemtray/statusnotifierwatcher.cpp
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#include "statusnotifierwatcher.h"
|
||||||
|
#include "statusnotifieritem_interface.h"
|
||||||
|
#include "statusnotifierwatcheradaptor.h"
|
||||||
|
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <QDBusServiceWatcher>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
45
src/systemtray/statusnotifierwatcher.h
Normal file
45
src/systemtray/statusnotifierwatcher.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef STATUSNOTIFIERWATCHER_H
|
||||||
|
#define STATUSNOTIFIERWATCHER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDBusContext>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
|
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<QString> m_statusNotifierHostServices;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STATUSNOTIFIERWATCHER_H
|
144
src/systemtray/systemtraymodel.cpp
Normal file
144
src/systemtray/systemtraymodel.cpp
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
#include "systemtraymodel.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
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<int, QByteArray> SystemTrayModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> 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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,12 @@
|
||||||
#ifndef STATUSNOTIFIERMODEL_H
|
#ifndef SYSTEMTRAYMODEL_H
|
||||||
#define STATUSNOTIFIERMODEL_H
|
#define SYSTEMTRAYMODEL_H
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QIcon>
|
|
||||||
#include <QMenu>
|
|
||||||
|
|
||||||
#include <dbusmenu-qt5/dbusmenuimporter.h>
|
|
||||||
|
|
||||||
#include "statusnotifieritemsource.h"
|
|
||||||
#include "statusnotifierwatcher.h"
|
#include "statusnotifierwatcher.h"
|
||||||
#include "sniasync.h"
|
#include "statusnotifieritemsource.h"
|
||||||
|
|
||||||
class StatusNotifierModel : public QAbstractListModel
|
class SystemTrayModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -19,16 +14,14 @@ public:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
IdRole = Qt::UserRole + 1,
|
IdRole = Qt::UserRole + 1,
|
||||||
IconNameRole,
|
IconNameRole,
|
||||||
IconBytesRole,
|
|
||||||
IconRole,
|
IconRole,
|
||||||
TitleRole,
|
TitleRole,
|
||||||
ToolTipRole
|
ToolTipRole
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit StatusNotifierModel(QObject *parent = nullptr);
|
explicit SystemTrayModel(QObject *parent = nullptr);
|
||||||
~StatusNotifierModel();
|
~SystemTrayModel();
|
||||||
|
|
||||||
// Basic functionality:
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
@ -40,14 +33,15 @@ public:
|
||||||
Q_INVOKABLE void leftButtonClick(const QString &id);
|
Q_INVOKABLE void leftButtonClick(const QString &id);
|
||||||
Q_INVOKABLE void rightButtonClick(const QString &id);
|
Q_INVOKABLE void rightButtonClick(const QString &id);
|
||||||
|
|
||||||
public slots:
|
private slots:
|
||||||
void itemAdded(QString serviceAndPath);
|
void onItemAdded(const QString &service);
|
||||||
void itemRemoved(const QString &serviceAndPath);
|
void onItemRemoved(const QString &service);
|
||||||
void updated(StatusNotifierItemSource *item);
|
void updated(StatusNotifierItemSource *item);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StatusNotifierWatcher *m_watcher;
|
StatusNotifierWatcher *m_watcher;
|
||||||
QList<StatusNotifierItemSource *> m_items;
|
QList<StatusNotifierItemSource *> m_items;
|
||||||
|
QString m_hostName;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // STATUSNOTIFIERMODEL_H
|
#endif // SYSTEMTRAYMODEL_H
|
48
src/systemtray/systemtraytypedefs.h
Normal file
48
src/systemtray/systemtraytypedefs.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* *
|
||||||
|
* Copyright (C) 2009 Marco Martin <notmart@gmail.com> *
|
||||||
|
* *
|
||||||
|
* 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 <QByteArray>
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
struct KDbusImageStruct {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QVector<KDbusImageStruct> KDbusImageVector;
|
||||||
|
|
||||||
|
struct KDbusToolTipStruct {
|
||||||
|
QString icon;
|
||||||
|
KDbusImageVector image;
|
||||||
|
QString title;
|
||||||
|
QString subTitle;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(KDbusImageStruct)
|
||||||
|
Q_DECLARE_METATYPE(KDbusImageVector)
|
||||||
|
Q_DECLARE_METATYPE(KDbusToolTipStruct)
|
||||||
|
|
||||||
|
#endif
|
128
src/systemtray/systemtraytypes.cpp
Normal file
128
src/systemtray/systemtraytypes.cpp
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* *
|
||||||
|
* Copyright (C) 2009 Marco Martin <notmart@gmail.com> *
|
||||||
|
* *
|
||||||
|
* 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 <QDebug>
|
||||||
|
#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<KDbusImageStruct>());
|
||||||
|
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;
|
||||||
|
}
|
37
src/systemtray/systemtraytypes.h
Normal file
37
src/systemtray/systemtraytypes.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* *
|
||||||
|
* Copyright (C) 2009 Marco Martin <notmart@gmail.com> *
|
||||||
|
* *
|
||||||
|
* 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 <QDBusArgument>
|
||||||
|
|
||||||
|
#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
|
|
@ -1,16 +1,62 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
<svg
|
||||||
<metadata>
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
<rdf:RDF>
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
<cc:Work rdf:about="">
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
<dc:format>image/svg+xml</dc:format>
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<dc:title/>
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
</cc:Work>
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
</rdf:RDF>
|
width="16"
|
||||||
</metadata>
|
height="16"
|
||||||
<defs>
|
version="1.1"
|
||||||
<style id="current-color-scheme" type="text/css">.ColorScheme-Text { color:#363636; } .ColorScheme-Highlight { color:#5294e2; }</style>
|
id="svg9"
|
||||||
</defs>
|
sodipodi:docname="bluetooth-symbolic.svg"
|
||||||
<path class="ColorScheme-Text" d="m7.9981 2e-5v7.041l-3.293-3.293-2e-3 2e-3c-0.35575-0.35595-0.99605-0.99609-0.99605-0.99609l-0.70703 0.70703 4.5449 4.541-4.5449 4.541 0.70703 0.70703s0.64035-0.64009 0.99609-0.99609h2e-3l3.2949-3.293v7.0391h1l4-4-4-4 4-4-4-4zm1.002 1.416 2.5859 2.5859-2.5859 2.5859zm0 8 2.5859 2.5859-2.5859 2.5859z" color="#363636" fill="#363636"/>
|
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2160"
|
||||||
|
inkscape:window-height="1304"
|
||||||
|
id="namedview11"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="45.9375"
|
||||||
|
inkscape:cx="8"
|
||||||
|
inkscape:cy="8"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg9"
|
||||||
|
inkscape:document-rotation="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata2">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs5">
|
||||||
|
<style
|
||||||
|
id="current-color-scheme"
|
||||||
|
type="text/css">.ColorScheme-Text { color:#363636; } .ColorScheme-Highlight { color:#5294e2; }</style>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
d="m7.9981 2e-5v7.041l-3.293-3.293-2e-3 2e-3c-0.35575-0.35595-0.99605-0.99609-0.99605-0.99609l-0.70703 0.70703 4.5449 4.541-4.5449 4.541 0.70703 0.70703s0.64035-0.64009 0.99609-0.99609h2e-3l3.2949-3.293v7.0391h1l4-4-4-4 4-4-4-4zm1.002 1.416 2.5859 2.5859-2.5859 2.5859zm0 8 2.5859 2.5859-2.5859 2.5859z"
|
||||||
|
color="#363636"
|
||||||
|
fill="#363636"
|
||||||
|
id="path7"
|
||||||
|
style="fill-opacity:1;fill:#363636" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 1,006 B After Width: | Height: | Size: 2 KiB |
|
@ -23,7 +23,7 @@
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="2160"
|
inkscape:window-width="2160"
|
||||||
inkscape:window-height="1307"
|
inkscape:window-height="1304"
|
||||||
id="namedview11"
|
id="namedview11"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="45.9375"
|
inkscape:zoom="45.9375"
|
||||||
|
@ -32,7 +32,8 @@
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg9" />
|
inkscape:current-layer="svg9"
|
||||||
|
inkscape:document-rotation="0" />
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata2">
|
id="metadata2">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
|
@ -54,5 +55,5 @@
|
||||||
<path
|
<path
|
||||||
d="M 8.00028,1 C 4.140704,1 1,4.13971 1,7.9993 1,11.859394 4.140732,15 8.00028,15 11.859814,15 15,11.859394 15,7.9993 15,4.13971 11.859842,1 8.00028,1 Z m 0,12.593196 c -0.007,0 -0.014,-9.62e-4 -0.02058,-9.62e-4 V 2.407812 c 0.007,0 0.014,-0.00101 0.02058,-0.00101 3.083822,0 5.592902,2.508702 5.592902,5.592496 0,3.084326 -2.50908,5.593896 -5.592902,5.593896 z"
|
d="M 8.00028,1 C 4.140704,1 1,4.13971 1,7.9993 1,11.859394 4.140732,15 8.00028,15 11.859814,15 15,11.859394 15,7.9993 15,4.13971 11.859842,1 8.00028,1 Z m 0,12.593196 c -0.007,0 -0.014,-9.62e-4 -0.02058,-9.62e-4 V 2.407812 c 0.007,0 0.014,-0.00101 0.02058,-0.00101 3.083822,0 5.592902,2.508702 5.592902,5.592496 0,3.084326 -2.50908,5.593896 -5.592902,5.593896 z"
|
||||||
id="path2"
|
id="path2"
|
||||||
style="stroke-width:0.0287544" />
|
style="stroke-width:0.0287544;fill:#363636;fill-opacity:1" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
Loading…
Add table
Reference in a new issue