diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 513eaeb..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build -*.kdev4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 4123d62..6c3bcb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ set(CMAKE_AUTOMOC ON) find_package(Qt5Widgets REQUIRED QUIET) find_package(Qt5LinguistTools REQUIRED QUIET) +find_package(Qt5DBus REQUIRED QUIET) +find_package(dbusmenu-qt5 REQUIRED QUIET) find_package(lxqt REQUIRED QUIET) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d65d813..7922b83 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,15 +3,34 @@ include_directories( "${CMAKE_CURRENT_BINARY_DIR}" ) +set(qtlxqt_HDRS + lxqtplatformtheme.h + lxqtsystemtrayicon.h + statusnotifieritem/statusnotifieritem.h + statusnotifieritem/dbustypes.h +) + set(qtlxqt_SRCS main.cpp lxqtplatformtheme.cpp + lxqtsystemtrayicon.cpp + statusnotifieritem/statusnotifieritem.cpp + statusnotifieritem/dbustypes.cpp +) + +qt5_add_dbus_adaptor(qtlxqt_SRCS + statusnotifieritem/org.kde.StatusNotifierItem.xml + statusnotifieritem/statusnotifieritem.h + StatusNotifierItem ) -add_library(qtlxqt SHARED ${qtlxqt_SRCS}) +add_library(qtlxqt SHARED ${qtlxqt_HDRS} ${qtlxqt_SRCS}) target_link_libraries(qtlxqt Qt5::Widgets + Qt5::DBus + dbusmenu-qt5 + lxqt ) # there is no standard way to get the plugin dir of Qt5 with cmake diff --git a/src/lxqtplatformtheme.h b/src/lxqtplatformtheme.h index 42f8ccf..5ad6d34 100644 --- a/src/lxqtplatformtheme.h +++ b/src/lxqtplatformtheme.h @@ -28,6 +28,8 @@ #ifndef LXQTPLATFORMTHEME_H #define LXQTPLATFORMTHEME_H +#include "lxqtsystemtrayicon.h" + #include // this private header is subject to changes #include #include @@ -54,6 +56,18 @@ public: virtual QVariant themeHint(ThemeHint hint) const; + QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const + { + auto trayIcon = new LXQtSystemTrayIcon; + if (trayIcon->isSystemTrayAvailable()) + return trayIcon; + else + { + delete trayIcon; + return nullptr; + } + } + // virtual QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const; // virtual QPixmap fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size, // QPlatformTheme::IconOptions iconOptions = 0) const; diff --git a/src/lxqtsystemtrayicon.cpp b/src/lxqtsystemtrayicon.cpp new file mode 100644 index 0000000..1df4dd0 --- /dev/null +++ b/src/lxqtsystemtrayicon.cpp @@ -0,0 +1,382 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * http://lxqt.org/ + * + * Copyright: 2015 LXQt team + * Authors: + * Paulo Lieuthier + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "lxqtsystemtrayicon.h" +#include +#include +#include +#include +#include +#include +#include + +SystemTrayMenu::SystemTrayMenu() + : QPlatformMenu(), + m_tag(0), + m_menu(new QMenu()) +{ + connect(m_menu.data(), &QMenu::aboutToShow, this, &QPlatformMenu::aboutToShow); + connect(m_menu.data(), &QMenu::aboutToHide, this, &QPlatformMenu::aboutToHide); +} + +SystemTrayMenu::~SystemTrayMenu() +{ + if (m_menu) + m_menu->deleteLater(); +} + +QPlatformMenuItem *SystemTrayMenu::createMenuItem() const +{ + return new SystemTrayMenuItem(); +} + +void SystemTrayMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) +{ + if (SystemTrayMenuItem *ours = qobject_cast(menuItem)) + { + bool inserted = false; + + if (SystemTrayMenuItem *oursBefore = qobject_cast(before)) + { + for (auto it = m_items.begin(); it != m_items.end(); ++it) + { + if (*it == oursBefore) + { + m_items.insert(it, ours); + if (m_menu) + m_menu->insertAction(oursBefore->action(), ours->action()); + + inserted = true; + break; + } + } + } + + if (!inserted) + { + m_items.append(ours); + if (m_menu) + m_menu->addAction(ours->action()); + } + } +} + +QPlatformMenuItem *SystemTrayMenu::menuItemAt(int position) const +{ + if (position < m_items.size()) + return m_items.at(position); + + return nullptr; +} + +QPlatformMenuItem *SystemTrayMenu::menuItemForTag(quintptr tag) const +{ + auto it = std::find_if(m_items.constBegin(), m_items.constEnd(), [tag] (SystemTrayMenuItem *item) + { + return item->tag() == tag; + }); + + if (it != m_items.constEnd()) + return *it; + + return nullptr; +} + +void SystemTrayMenu::removeMenuItem(QPlatformMenuItem *menuItem) +{ + if (SystemTrayMenuItem *ours = qobject_cast(menuItem)) + { + m_items.removeOne(ours); + if (ours->action() && m_menu) + m_menu->removeAction(ours->action()); + } +} + +void SystemTrayMenu::setEnabled(bool enabled) +{ + if (!m_menu) + return; + + m_menu->setEnabled(enabled); +} + +void SystemTrayMenu::setIcon(const QIcon &icon) +{ + if (!m_menu) + return; + + m_menu->setIcon(icon); +} + +void SystemTrayMenu::setTag(quintptr tag) +{ + m_tag = tag; +} + +void SystemTrayMenu::setText(const QString &text) +{ + if (!m_menu) + return; + + m_menu->setTitle(text); +} + +void SystemTrayMenu::setVisible(bool visible) +{ + if (!m_menu) + return; + + m_menu->setVisible(visible); +} + +void SystemTrayMenu::syncMenuItem(QPlatformMenuItem *) +{ + // Nothing to do +} + +void SystemTrayMenu::syncSeparatorsCollapsible(bool enable) +{ + if (!m_menu) + return; + + m_menu->setSeparatorsCollapsible(enable); +} + +quintptr SystemTrayMenu::tag() const +{ + return m_tag; +} + +QMenu *SystemTrayMenu::menu() const +{ + return m_menu.data(); +} + +SystemTrayMenuItem::SystemTrayMenuItem() + : QPlatformMenuItem(), + m_tag(0), + m_action(new QAction(this)) +{ + connect(m_action, &QAction::triggered, this, &QPlatformMenuItem::activated); + connect(m_action, &QAction::hovered, this, &QPlatformMenuItem::hovered); +} + +SystemTrayMenuItem::~SystemTrayMenuItem() +{ +} + +void SystemTrayMenuItem::setCheckable(bool checkable) +{ + m_action->setCheckable(checkable); +} + +void SystemTrayMenuItem::setChecked(bool isChecked) +{ + m_action->setChecked(isChecked); +} + +void SystemTrayMenuItem::setEnabled(bool enabled) +{ + m_action->setEnabled(enabled); +} + +void SystemTrayMenuItem::setFont(const QFont &font) +{ + m_action->setFont(font); +} + +void SystemTrayMenuItem::setIcon(const QIcon &icon) +{ + m_action->setIcon(icon); +} + +void SystemTrayMenuItem::setIsSeparator(bool isSeparator) +{ + m_action->setSeparator(isSeparator); +} + +void SystemTrayMenuItem::setMenu(QPlatformMenu *menu) +{ + if (SystemTrayMenu *ourMenu = qobject_cast(menu)) + m_action->setMenu(ourMenu->menu()); +} + +void SystemTrayMenuItem::setRole(QPlatformMenuItem::MenuRole) +{ +} + +void SystemTrayMenuItem::setShortcut(const QKeySequence &shortcut) +{ + m_action->setShortcut(shortcut); +} + +void SystemTrayMenuItem::setTag(quintptr tag) +{ + m_tag = tag; +} + +void SystemTrayMenuItem::setText(const QString &text) +{ + m_action->setText(text); +} + +void SystemTrayMenuItem::setVisible(bool isVisible) +{ + m_action->setVisible(isVisible); +} + +void SystemTrayMenuItem::setIconSize(int) +{ +} + +quintptr SystemTrayMenuItem::tag() const +{ + return m_tag; +} + +QAction *SystemTrayMenuItem::action() const +{ + return m_action; +} + +LXQtSystemTrayIcon::LXQtSystemTrayIcon() + : QPlatformSystemTrayIcon(), + mSni(nullptr) +{ + // register types + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); +} + +LXQtSystemTrayIcon::~LXQtSystemTrayIcon() +{ +} + +void LXQtSystemTrayIcon::init() +{ + if (!mSni) + { + mSni = new StatusNotifierItem(QString::number(QCoreApplication::applicationPid()), this); + mSni->setTitle(QApplication::applicationDisplayName()); + + // default menu + QPlatformMenu *menu = createMenu(); + menu->setParent(this); + QPlatformMenuItem *menuItem = menu->createMenuItem(); + menuItem->setParent(menu); + menuItem->setText(tr("Quit")); + menuItem->setIcon(QIcon::fromTheme("application-exit")); + connect(menuItem, &QPlatformMenuItem::activated, qApp, &QApplication::quit); + menu->insertMenuItem(menuItem, nullptr); + updateMenu(menu); + + connect(mSni, &StatusNotifierItem::activateRequested, [this](const QPoint &) + { + emit activated(QPlatformSystemTrayIcon::Trigger); + }); + + connect(mSni, &StatusNotifierItem::secondaryActivateRequested, [this](const QPoint &) + { + emit activated(QPlatformSystemTrayIcon::MiddleClick); + }); + } +} + +void LXQtSystemTrayIcon::cleanup() +{ + delete mSni; + mSni = nullptr; +} + +void LXQtSystemTrayIcon::updateIcon(const QIcon &icon) +{ + if (!mSni) + return; + + if (icon.name().isEmpty()) + { + mSni->setIconByPixmap(icon); + mSni->setToolTipIconByPixmap(icon); + } + else + { + mSni->setIconByName(icon.name()); + mSni->setToolTipIconByName(icon.name()); + } +} + +void LXQtSystemTrayIcon::updateToolTip(const QString &tooltip) +{ + if (!mSni) + return; + + mSni->setToolTipTitle(tooltip); +} + +void LXQtSystemTrayIcon::updateMenu(QPlatformMenu *menu) +{ + if (!mSni) + return; + + if (SystemTrayMenu *ourMenu = qobject_cast(menu)) + mSni->setContextMenu(ourMenu->menu()); +} + +QPlatformMenu *LXQtSystemTrayIcon::createMenu() const +{ + return new SystemTrayMenu(); +} + +QRect LXQtSystemTrayIcon::geometry() const +{ + // StatusNotifierItem doesn't provide the geometry + return QRect(); +} + +void LXQtSystemTrayIcon::showMessage(const QString &title, const QString &msg, + const QIcon &icon, MessageIcon, int secs) +{ + if (!mSni) + return; + + mSni->showMessage(title, msg, icon.name(), secs); +} + +bool LXQtSystemTrayIcon::isSystemTrayAvailable() const +{ + QDBusInterface systrayHost("org.kde.StatusNotifierWatcher", + "/StatusNotifierWatcher", + "org.kde.StatusNotifierWatcher"); + + return systrayHost.isValid() && systrayHost.property("IsStatusNotifierHostRegistered").toBool(); +} + +bool LXQtSystemTrayIcon::supportsMessages() const +{ + return true; +} diff --git a/src/lxqtsystemtrayicon.h b/src/lxqtsystemtrayicon.h new file mode 100644 index 0000000..4aed10c --- /dev/null +++ b/src/lxqtsystemtrayicon.h @@ -0,0 +1,124 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * http://lxqt.org/ + * + * Copyright: 2015 LXQt team + * Authors: + * Paulo Lieuthier + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef LXQTSYSTEMTRAYICON_H +#define LXQTSYSTEMTRAYICON_H + +#include +#include + +#include "statusnotifieritem/statusnotifieritem.h" + +class SystemTrayMenuItem; +class QAction; +class QMenu; + +class SystemTrayMenu : public QPlatformMenu +{ + Q_OBJECT +public: + SystemTrayMenu(); + ~SystemTrayMenu() Q_DECL_OVERRIDE; + void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) Q_DECL_OVERRIDE; + QPlatformMenuItem *menuItemAt(int position) const Q_DECL_OVERRIDE; + QPlatformMenuItem *menuItemForTag(quintptr tag) const Q_DECL_OVERRIDE; + void removeMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE; + void setEnabled(bool enabled) Q_DECL_OVERRIDE; + void setIcon(const QIcon &icon) Q_DECL_OVERRIDE; + void setTag(quintptr tag) Q_DECL_OVERRIDE; + void setText(const QString &text) Q_DECL_OVERRIDE; + void setVisible(bool visible) Q_DECL_OVERRIDE; + void syncMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE; + void syncSeparatorsCollapsible(bool enable) Q_DECL_OVERRIDE; + quintptr tag() const Q_DECL_OVERRIDE; + QPlatformMenuItem *createMenuItem() const Q_DECL_OVERRIDE; + + QMenu *menu() const; + +private: + quintptr m_tag; + QPointer m_menu; + QList m_items; +}; + +class SystemTrayMenuItem : public QPlatformMenuItem +{ + Q_OBJECT +public: + SystemTrayMenuItem(); + ~SystemTrayMenuItem() Q_DECL_OVERRIDE; + void setCheckable(bool checkable) Q_DECL_OVERRIDE; + void setChecked(bool isChecked) Q_DECL_OVERRIDE; + void setEnabled(bool enabled) Q_DECL_OVERRIDE; + void setFont(const QFont &font) Q_DECL_OVERRIDE; + void setIcon(const QIcon &icon) Q_DECL_OVERRIDE; + void setIsSeparator(bool isSeparator) Q_DECL_OVERRIDE; + void setMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE; + void setRole(MenuRole role) Q_DECL_OVERRIDE; + void setShortcut(const QKeySequence &shortcut) Q_DECL_OVERRIDE; + void setTag(quintptr tag) Q_DECL_OVERRIDE; + void setText(const QString &text) Q_DECL_OVERRIDE; + void setVisible(bool isVisible) Q_DECL_OVERRIDE; + quintptr tag() const Q_DECL_OVERRIDE; + void setIconSize(int size) + #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) + Q_DECL_OVERRIDE + #endif + ; + + QAction *action() const; + +private: + quintptr m_tag; + QAction *m_action; +}; + +class LXQtSystemTrayIcon : public QPlatformSystemTrayIcon +{ +public: + LXQtSystemTrayIcon(); + ~LXQtSystemTrayIcon() Q_DECL_OVERRIDE; + + void init() Q_DECL_OVERRIDE; + void cleanup() Q_DECL_OVERRIDE; + void updateIcon(const QIcon &icon) Q_DECL_OVERRIDE; + void updateToolTip(const QString &tooltip) Q_DECL_OVERRIDE; + void updateMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE; + QRect geometry() const Q_DECL_OVERRIDE; + void showMessage(const QString &title, const QString &msg, + const QIcon &icon, MessageIcon iconType, int secs) Q_DECL_OVERRIDE; + + bool isSystemTrayAvailable() const Q_DECL_OVERRIDE; + bool supportsMessages() const Q_DECL_OVERRIDE; + + QPlatformMenu *createMenu() const Q_DECL_OVERRIDE; + +private: + StatusNotifierItem *mSni; +}; + +#endif diff --git a/src/statusnotifieritem/dbustypes.cpp b/src/statusnotifieritem/dbustypes.cpp new file mode 100644 index 0000000..758bfed --- /dev/null +++ b/src/statusnotifieritem/dbustypes.cpp @@ -0,0 +1,75 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://lxqt.org + * + * Copyright: 2015 LXQt team + * Authors: + * Balázs Béla + * Paulo Lieuthier + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "dbustypes.h" + +// Marshall the IconPixmap data into a D-Bus argument +QDBusArgument &operator<<(QDBusArgument &argument, const IconPixmap &icon) +{ + argument.beginStructure(); + argument << icon.width; + argument << icon.height; + argument << icon.bytes; + argument.endStructure(); + return argument; +} + +// Retrieve the ImageStruct data from the D-Bus argument +const QDBusArgument &operator>>(const QDBusArgument &argument, IconPixmap &icon) +{ + argument.beginStructure(); + argument >> icon.width; + argument >> icon.height; + argument >> icon.bytes; + argument.endStructure(); + return argument; +} + +// Marshall the ToolTip data into a D-Bus argument +QDBusArgument &operator<<(QDBusArgument &argument, const ToolTip &toolTip) +{ + argument.beginStructure(); + argument << toolTip.iconName; + argument << toolTip.iconPixmap; + argument << toolTip.title; + argument << toolTip.description; + argument.endStructure(); + return argument; +} + +// Retrieve the ToolTip data from the D-Bus argument +const QDBusArgument &operator>>(const QDBusArgument &argument, ToolTip &toolTip) +{ + argument.beginStructure(); + argument >> toolTip.iconName; + argument >> toolTip.iconPixmap; + argument >> toolTip.title; + argument >> toolTip.description; + argument.endStructure(); + return argument; +} diff --git a/src/statusnotifieritem/dbustypes.h b/src/statusnotifieritem/dbustypes.h new file mode 100644 index 0000000..cc6f9a5 --- /dev/null +++ b/src/statusnotifieritem/dbustypes.h @@ -0,0 +1,60 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://lxqt.org + * + * Copyright: 2015 LXQt team + * Authors: + * Balázs Béla + * Paulo Lieuthier + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include + +#ifndef DBUSTYPES_H +#define DBUSTYPES_H + +struct IconPixmap { + int width; + int height; + QByteArray bytes; +}; + +typedef QList IconPixmapList; + +Q_DECLARE_METATYPE(IconPixmap) +Q_DECLARE_METATYPE(IconPixmapList) + +struct ToolTip { + QString iconName; + QList iconPixmap; + QString title; + QString description; +}; + +Q_DECLARE_METATYPE(ToolTip) + +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); + +#endif // DBUSTYPES_H diff --git a/src/statusnotifieritem/org.kde.StatusNotifierItem.xml b/src/statusnotifieritem/org.kde.StatusNotifierItem.xml new file mode 100644 index 0000000..0a563c5 --- /dev/null +++ b/src/statusnotifieritem/org.kde.StatusNotifierItem.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/statusnotifieritem/statusnotifieritem.cpp b/src/statusnotifieritem/statusnotifieritem.cpp new file mode 100644 index 0000000..8d24f97 --- /dev/null +++ b/src/statusnotifieritem/statusnotifieritem.cpp @@ -0,0 +1,299 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * http://lxqt.org/ + * + * Copyright: 2015 LXQt team + * Authors: + * Paulo Lieuthier + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "statusnotifieritem.h" +#include "statusnotifieritemadaptor.h" +#include +#include +#include + +int StatusNotifierItem::mServiceCounter = 0; + +StatusNotifierItem::StatusNotifierItem(QString id, QObject *parent) + : QObject(parent), + mAdaptor(new StatusNotifierItemAdaptor(this)), + mService(QString("org.freedesktop.StatusNotifierItem-%1-%2") + .arg(QCoreApplication::applicationPid()) + .arg(++mServiceCounter)), + mId(id), + mTitle("Test"), + mStatus("Active"), + mMenu(nullptr) +{ + // register service + QDBusConnection::sessionBus().registerService(mService); + QDBusConnection::sessionBus().registerObject("/StatusNotifierItem", this); + + registerToHost(); + + // monitor the watcher service in case the host restarts + QDBusServiceWatcher *watcher = new QDBusServiceWatcher("org.kde.StatusNotifierWatcher", + QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForOwnerChange, + this); + connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, + this, &StatusNotifierItem::onServiceOwnerChanged); +} + +StatusNotifierItem::~StatusNotifierItem() +{ + QDBusConnection::sessionBus().unregisterObject("/StatusNotifierItem"); + QDBusConnection::sessionBus().unregisterService(mService); + QDBusConnection::sessionBus().disconnectFromBus(mService); + + delete mMenu; +} + +void StatusNotifierItem::registerToHost() +{ + QDBusInterface interface("org.kde.StatusNotifierWatcher", + "/StatusNotifierWatcher", + "org.kde.StatusNotifierWatcher", + QDBusConnection::sessionBus()); + interface.asyncCall("RegisterStatusNotifierItem", mService); +} + +void StatusNotifierItem::onServiceOwnerChanged(const QString& service, const QString& oldOwner, + const QString& newOwner) +{ + if (!newOwner.isEmpty()) + registerToHost(); +} + +void StatusNotifierItem::setTitle(const QString &title) +{ + if (mTitle == title) + return; + + mTitle = title; + emit mAdaptor->NewTitle(); +} + +void StatusNotifierItem::setStatus(const QString &status) +{ + if (mStatus == status) + return; + + mStatus = status; + emit mAdaptor->NewStatus(mStatus); +} + +void StatusNotifierItem::setMenuPath(const QString& path) +{ + mMenuPath.setPath(path); +} + +void StatusNotifierItem::setIconByName(const QString &name) +{ + if (mIconName == name) + return; + + mIconName = name; + emit mAdaptor->NewIcon(); +} + +void StatusNotifierItem::setIconByPixmap(const QIcon &icon) +{ + if (mIconCacheKey == icon.cacheKey()) + return; + + mIconCacheKey = icon.cacheKey(); + mIcon = iconToPixmapList(icon); + mIconName.clear(); + emit mAdaptor->NewIcon(); +} + +void StatusNotifierItem::setOverlayIconByName(const QString &name) +{ + if (mOverlayIconName == name) + return; + + mOverlayIconName = name; + emit mAdaptor->NewOverlayIcon(); +} + +void StatusNotifierItem::setOverlayIconByPixmap(const QIcon &icon) +{ + if (mOverlayIconCacheKey == icon.cacheKey()) + return; + + mOverlayIconCacheKey = icon.cacheKey(); + mOverlayIcon = iconToPixmapList(icon); + mOverlayIconName.clear(); + emit mAdaptor->NewOverlayIcon(); +} + +void StatusNotifierItem::setAttentionIconByName(const QString &name) +{ + if (mAttentionIconName == name) + return; + + mAttentionIconName = name; + emit mAdaptor->NewAttentionIcon(); +} + +void StatusNotifierItem::setAttentionIconByPixmap(const QIcon &icon) +{ + if (mAttentionIconCacheKey == icon.cacheKey()) + return; + + mAttentionIconCacheKey = icon.cacheKey(); + mAttentionIcon = iconToPixmapList(icon); + mAttentionIconName.clear(); + emit mAdaptor->NewAttentionIcon(); +} + +void StatusNotifierItem::setToolTipTitle(const QString &title) +{ + if (mTooltipTitle == title) + return; + + mTooltipTitle = title; + emit mAdaptor->NewToolTip(); +} + +void StatusNotifierItem::setToolTipSubTitle(const QString &subTitle) +{ + if (mTooltipSubtitle == subTitle) + return; + + mTooltipSubtitle = subTitle; + emit mAdaptor->NewToolTip(); +} + +void StatusNotifierItem::setToolTipIconByName(const QString &name) +{ + if (mTooltipIconName == name) + return; + + mTooltipIconName = name; + emit mAdaptor->NewToolTip(); +} + +void StatusNotifierItem::setToolTipIconByPixmap(const QIcon &icon) +{ + if (mTooltipIconCacheKey == icon.cacheKey()) + return; + + mTooltipIconCacheKey = icon.cacheKey(); + mTooltipIcon = iconToPixmapList(icon); + mTooltipIconName.clear(); + emit mAdaptor->NewToolTip(); +} + +void StatusNotifierItem::setContextMenu(QMenu* menu) +{ + if (mMenu == menu) + return; + + delete mMenu; + mMenu = menu; + mMenu->setParent(nullptr); + + setMenuPath("/MenuBar"); + new DBusMenuExporter(this->menu().path(), mMenu); +} + +void StatusNotifierItem::Activate(int x, int y) +{ + if (mStatus == "NeedsAttention") + mStatus = "Active"; + + emit activateRequested(QPoint(x, y)); +} + +void StatusNotifierItem::SecondaryActivate(int x, int y) +{ + if (mStatus == "NeedsAttention") + mStatus = "Active"; + + emit secondaryActivateRequested(QPoint(x, y)); +} + +void StatusNotifierItem::ContextMenu(int x, int y) +{ + if (mMenu) + { + if (mMenu->isVisible()) + mMenu->popup(QPoint(x, y)); + else + mMenu->hide(); + } +} + +void StatusNotifierItem::Scroll(int delta, const QString &orientation) +{ + Qt::Orientation orient = Qt::Vertical; + if (orientation.toLower() == "horizontal") + orient = Qt::Horizontal; + + emit scrollRequested(delta, orient); +} + +void StatusNotifierItem::showMessage(const QString& title, const QString& msg, + const QString& iconName, int secs) +{ + QDBusInterface interface("org.freedesktop.Notifications", "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", QDBusConnection::sessionBus()); + interface.call("Notify", mTitle, (uint) 0, iconName, title, + msg, QStringList(), QVariantMap(), secs); +} + +IconPixmapList StatusNotifierItem::iconToPixmapList(const QIcon& icon) +{ + IconPixmapList pixmapList; + + // long live KDE! + for (const QSize &size : icon.availableSizes()) + { + QImage image = icon.pixmap(size).toImage(); + + IconPixmap pix; + pix.height = image.height(); + pix.width = image.width(); + + if (image.format() != QImage::Format_ARGB32) + image = image.convertToFormat(QImage::Format_ARGB32); + + pix.bytes = QByteArray((char *) image.bits(), image.byteCount()); + + // swap to network byte order if we are little endian + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) + { + quint32 *uintBuf = (quint32 *) pix.bytes.data(); + for (uint i = 0; i < pix.bytes.size() / sizeof(quint32); ++i) + { + *uintBuf = qToBigEndian(*uintBuf); + ++uintBuf; + } + } + + pixmapList.append(pix); + } + + return pixmapList; +} diff --git a/src/statusnotifieritem/statusnotifieritem.h b/src/statusnotifieritem/statusnotifieritem.h new file mode 100644 index 0000000..e55eedd --- /dev/null +++ b/src/statusnotifieritem/statusnotifieritem.h @@ -0,0 +1,177 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * http://lxqt.org/ + * + * Copyright: 2015 LXQt team + * Authors: + * Paulo Lieuthier + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#ifndef STATUS_NOTIFIER_ITEM_H +#define STATUS_NOTIFIER_ITEM_H + +#include +#include +#include + +#include "dbustypes.h" + +class StatusNotifierItemAdaptor; + +class StatusNotifierItem : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString Title READ title) + Q_PROPERTY(QString Id READ id) + Q_PROPERTY(QString Status READ status) + Q_PROPERTY(QDBusObjectPath Menu READ menu) + + Q_PROPERTY(QString IconName READ iconName) + Q_PROPERTY(IconPixmapList IconPixmap READ iconPixmap) + + Q_PROPERTY(QString OverlayIconName READ overlayIconName) + Q_PROPERTY(IconPixmapList OverlayIconPixmap READ overlayIconPixmap) + + Q_PROPERTY(QString AttentionIconName READ attentionIconName) + Q_PROPERTY(IconPixmapList AttentionIconPixmap READ attentionIconPixmap) + + Q_PROPERTY(ToolTip ToolTip READ toolTip) + +public: + StatusNotifierItem(QString id, QObject *parent = nullptr); + ~StatusNotifierItem(); + + QString id() const + { return mId; } + + QString title() const + { return mTitle; } + void setTitle(const QString &title); + + QString status() const + { return mStatus; } + void setStatus(const QString &status); + + QDBusObjectPath menu() const + { return mMenuPath; } + void setMenuPath(const QString &path); + + QString iconName() const + { return mIconName; } + void setIconByName(const QString &name); + + IconPixmapList iconPixmap() const + { return mIcon; } + void setIconByPixmap(const QIcon &icon); + + QString overlayIconName() const + { return mOverlayIconName; } + void setOverlayIconByName(const QString &name); + + IconPixmapList overlayIconPixmap() const + { return mOverlayIcon; } + void setOverlayIconByPixmap(const QIcon &icon); + + QString attentionIconName() const + { return mAttentionIconName; } + void setAttentionIconByName(const QString &name); + + IconPixmapList attentionIconPixmap() const + { return mAttentionIcon; } + void setAttentionIconByPixmap(const QIcon &icon); + + QString toolTipTitle() const + { return mTooltipTitle; } + void setToolTipTitle(const QString &title); + + QString toolTipSubTitle() const + { return mTooltipSubtitle; } + void setToolTipSubTitle(const QString &subTitle); + + QString toolTipIconName() const + { return mTooltipIconName; } + void setToolTipIconByName(const QString &name); + + IconPixmapList toolTipIconPixmap() const + { return mTooltipIcon; } + void setToolTipIconByPixmap(const QIcon &icon); + + ToolTip toolTip() const + { + ToolTip tt; + tt.title = mTooltipTitle; + tt.description = mTooltipSubtitle; + tt.iconName = mTooltipIconName; + tt.iconPixmap = mTooltipIcon; + return tt; + } + + void setContextMenu(QMenu *menu); + +public slots: + void Activate(int x, int y); + void SecondaryActivate(int x, int y); + void ContextMenu(int x, int y); + void Scroll(int delta, const QString &orientation); + + void showMessage(const QString &title, const QString &msg, const QString &iconName, int secs); + +private: + void registerToHost(); + IconPixmapList iconToPixmapList(const QIcon &icon); + +private slots: + void onServiceOwnerChanged(const QString &service, const QString &oldOwner, + const QString &newOwner); + +signals: + void activateRequested(const QPoint &pos); + void secondaryActivateRequested(const QPoint &pos); + void scrollRequested(int delta, Qt::Orientation orientation); + +private: + StatusNotifierItemAdaptor *mAdaptor; + + QString mService; + QString mId; + QString mTitle; + QString mStatus; + + // icons + QString mIconName, mOverlayIconName, mAttentionIconName; + IconPixmapList mIcon, mOverlayIcon, mAttentionIcon; + qint64 mIconCacheKey, mOverlayIconCacheKey, mAttentionIconCacheKey; + + // tooltip + QString mTooltipTitle, mTooltipSubtitle, mTooltipIconName; + IconPixmapList mTooltipIcon; + qint64 mTooltipIconCacheKey; + + // menu + QMenu *mMenu; + QDBusObjectPath mMenuPath; + + static int mServiceCounter; +}; + +#endif