diff --git a/AUTHORS b/AUTHORS index 8e41b63..06a7892 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,7 +5,6 @@ Upstream Authors: Copyright: Copyright (c) 2013-2014 LXQt team -License: GPL-2 and LGPL-2.1+ +License: LGPL-2.1+ The full text of the licenses can be found in the 'COPYING' file. - -Based on source code from the Qt 4.8 Project by Digia Plc +src/qiconloader_p.h is under LGPL-2.1 or 3 with Digia Qt LGPL Exception version 1.1 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/Digia-Qt-LGPL-Exception-version-1.1 b/Digia-Qt-LGPL-Exception-version-1.1 new file mode 100644 index 0000000..6e6fb3a --- /dev/null +++ b/Digia-Qt-LGPL-Exception-version-1.1 @@ -0,0 +1,25 @@ +Digia Qt LGPL Exception version 1.1 +=================================== + +As an additional permission to the GNU Lesser General Public License version +2.1, the object code form of a "work that uses the Library" may incorporate +material from a header file that is part of the Library. You may distribute +such object code under terms of your choice, provided that: + + (i) the header files of the Library have not been modified; and + (ii) the incorporated material is limited to numerical parameters, data + structure layouts, accessors, macros, inline functions and + templates; and + (iii) you comply with the terms of Section 6 of the GNU Lesser General + Public License version 2.1. + +Moreover, you may apply this exception to a modified version of the Library, +provided that such modification does not involve copying material from the +Library into the modified Library's header files unless such material is +limited to (i) numerical parameters; (ii) data structure layouts; +(iii) accessors; and (iv) small macros, templates and inline functions of +five lines or less in length. + +Furthermore, you are not required to apply this additional permission to a +modified version of the Library. + diff --git a/COPYING b/LICENSE similarity index 96% rename from COPYING rename to LICENSE index a8dd823..20fb9c7 100644 --- a/COPYING +++ b/LICENSE @@ -1,9 +1,8 @@ - GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -23,8 +22,7 @@ specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations -below. +strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that @@ -89,9 +87,9 @@ libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it -becomes a de-facto standard. To achieve this, non-free programs must -be allowed to use the library. A more frequent case is that a free +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. @@ -138,8 +136,8 @@ included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control -compilation and installation of the library. +interface definition files, plus the scripts used to control compilation +and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of @@ -305,10 +303,10 @@ of these things: the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. - c) Accompany the work with a written offer, valid for at least - three years, to give the same user the materials specified in - Subsection 6a, above, for a charge no more than the cost of - performing this distribution. + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above @@ -386,10 +384,9 @@ all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply, and the section as a whole is intended to apply in other -circumstances. +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any @@ -407,11 +404,11 @@ be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License -may add an explicit geographical distribution limitation excluding those -countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. diff --git a/README b/README.md similarity index 90% rename from README rename to README.md index 65f0fbb..47be4e2 100644 --- a/README +++ b/README.md @@ -1,4 +1,6 @@ -libqtlxqt - LXQt system integration plugin for Qt. +# libqtlxqt + +LXQt system integration plugin for Qt. With this plugin, all Qt-based programs can adopt settings of LXQt, such as the icon theme. diff --git a/debian/changelog b/debian/changelog index 803d5c4..b2de0e3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +lxqt-qtplugin (0.10.0-2) unstable; urgency=medium + + * Merge from experimental. + * New upstream release. + * Set new minimum version for liblxqt. + + -- ChangZhuo Chen (陳昌倬) Thu, 19 Nov 2015 22:55:24 +0800 + lxqt-qtplugin (0.9.0+20150819-2) unstable; urgency=medium * Fixed czchen's mail address in control and copyright diff --git a/debian/docs b/debian/docs index 6f12db5..0b6e0f3 100644 --- a/debian/docs +++ b/debian/docs @@ -1,2 +1,2 @@ AUTHORS -README +README.md diff --git a/debian/upstream/signing-key.asc b/debian/upstream/signing-key.asc new file mode 100644 index 0000000..05cd1df --- /dev/null +++ b/debian/upstream/signing-key.asc @@ -0,0 +1,63 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2.0.22 (GNU/Linux) + +mQINBFJevCYBEACx+Hvy+Vsuf+V5jeLUnzjAmHoy8DfTeGWr3ts30IapLHrfi0+U +WpzNnISO77yTr4VNboVZH+GHM/rnPfieODfB4ZW6CZLlInMSKUXcgQsEqXpyBZhA +Ib/SPy2bOfHly1uRJes0uRDsH5+v/hD74sByfnjQlrvI68O6wvGZmDFMNNPVO8+/ +OWBSBNkBuVrrZOMSPsLwQGJ4UtUQ4whburaPJG4VZJc5DLbzJGbEuACc0IAEYJS3 +7AfXVXn4j4Gc9F3o1xTUnbOBnwGPquWwUIm3FM7Ec2OdkvMt3EwvnkMAfeVrq3iE +FDD/KZTxdL0BZH3QD8gB7Jm4v4f3Nkobg6JCvCbcH3wBdZW4mASbwWzfRaDC2zHb +ErTglD7PpShLKZZ0pr9okWZEGw4Ku3q8ALi1JXK/ePTmsBlvkVskOJ3Nnd0avgH4 ++Q/vZoKfH8EhNY745rI+8CE9iv6V9XiSUt4CKEWAENt4A8hq6U2vV+jZv3B6AgD7 +ZjiI59yD4YuYubu8rCnNizTgh1voVw3ietknn/x2H5yH8fByWZ5uL87C0ky/uma6 +ZGbiiAtM4kdkyDMrfRV5nlEG9EKAGPVu5mjeSCrfkETwZ9OFPz1AuDye4ZEXrrcC +iRQ7RX6/GtW18aHER0kzGnfwx5KJzkDrRBY8A2PdXLBcrsN4WpK9EX01PQARAQAB +tCNKZXJvbWUgTGVjbGFuY2hlIDxqZXJvbWVAbGVjbGFuLmNoPokCPwQTAQIAKQUC +Ul68JgIbAwUJAeEzgAcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEDfgrx/a +SPNzSHIP/1ewXcC0TFBcvDD7MrIP7anyNfiWfW7cxkR8GSamkg6HTa6Ndyr1FFjJ +OoDFUP37jWhu59CsHxs2D0zRWJktezfvElscRgqbHcdpIznqsGdI8hXCZafhBGVb +sdAB2LRawcXGxnXt7XajPcSVwLWRE62caBqohznU2iWvI780WNjEbZoA0LhZwaFF +UUPJm8ea9v0IkZVKUyg9WONZ1U7FEG9SaEiSpI8kJdx1fvCwZVDV/NRO5GqnJaho +P1LCne4YdwS6pt1/fRgk32IHxxZfHlLzLHxb6v1JmIg72x28qCmGyK9oFBDbbnYu +6Aq8XbHogOrD5vJM2Pfm2IhV0+JHOjfQbddv8tsAH1M+LI+tToXmg5st1AU3wnTn +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 +=fF8P +-----END PGP PUBLIC KEY BLOCK----- diff --git a/debian/watch b/debian/watch index 88b56dc..51d59f3 100644 --- a/debian/watch +++ b/debian/watch @@ -1,2 +1,3 @@ version=3 -https://github.com/lxde/lxqt-qtplugin/releases .*/([\d\.]+).tar.gz +opts="pgpsigurlmangle=s/$/.asc/" \ + https://github.com/lxde/lxqt-qtplugin/releases .*/([\d\.]+).tar.gz 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.cpp b/src/lxqtplatformtheme.cpp index 51f0166..679ed2a 100644 --- a/src/lxqtplatformtheme.cpp +++ b/src/lxqtplatformtheme.cpp @@ -49,7 +49,6 @@ LXQtPlatformTheme::LXQtPlatformTheme(): settingsWatcher_(NULL) { - // qDebug() << "LXQT Platform Theme loaded"; // When the plugin is loaded, it seems that the app is not yet running and // QThread environment is not completely set up. So creating filesystem watcher // does not work since it uses QSocketNotifier internally which can only be @@ -106,14 +105,12 @@ void LXQtPlatformTheme::loadSettings() { if(!fontStr_.isEmpty()) { if(font_.fromString(fontStr_)) QApplication::setFont(font_); // it seems that we need to do this manually. - // qDebug() << "font: " << font_.toString(); } // FixedFont fixedFontStr_ = settings.value(QLatin1String("fixedFont")).toString(); if(!fixedFontStr_.isEmpty()) { fixedFont_.fromString(fixedFontStr_); - // qDebug() << "fixedFont: " << fixedFont_.toString(); } // mouse @@ -214,10 +211,9 @@ const QPalette *LXQtPlatformTheme::palette(Palette type) const { } const QFont *LXQtPlatformTheme::font(Font type) const { - // qDebug() << "font()" << type << SystemFont; if(type == SystemFont && !fontStr_.isEmpty()) { - // NOTE: for some reason, this is not called by Qt when program startup. - // So we do QApplication::setFont() manually. + // NOTE: for some reason, this is not called by Qt when program startup. + // So we do QApplication::setFont() manually. return &font_; } else if(type == FixedFont && !fixedFontStr_.isEmpty()) { @@ -227,7 +223,6 @@ const QFont *LXQtPlatformTheme::font(Font type) const { } QVariant LXQtPlatformTheme::themeHint(ThemeHint hint) const { - // qDebug() << "themeHint" << hint; switch (hint) { case CursorFlashTime: return cursorFlashTime_; @@ -258,14 +253,12 @@ QVariant LXQtPlatformTheme::themeHint(ThemeHint hint) const { case ItemViewActivateItemOnSingleClick: return QVariant(singleClickActivate_); case SystemIconThemeName: - // qDebug() << "iconTheme" << iconTheme_; return iconTheme_; case SystemIconFallbackThemeName: return "hicolor"; case IconThemeSearchPaths: return xdgIconThemePaths(); case StyleNames: - // qDebug() << "StyleNames"; return QStringList() << style_; case WindowAutoPlacement: break; @@ -291,13 +284,10 @@ QVariant LXQtPlatformTheme::themeHint(ThemeHint hint) const { break; case ContextMenuOnMouseRelease: break; -#if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) - // this was introduced in Qt 5.3. case MousePressAndHoldInterval: break; case MouseDoubleClickDistance: break; -#endif default: break; } 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