diff --git a/CMakeLists.txt b/CMakeLists.txt index 04c50a6..f5bcefb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(META_APP_AUTHOR "Martchus") set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") set(META_APP_DESCRIPTION "Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models") set(META_VERSION_MAJOR 5) -set(META_VERSION_MINOR 9) +set(META_VERSION_MINOR 10) set(META_VERSION_PATCH 0) set(META_APP_VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}) diff --git a/misc/dbusnotification.cpp b/misc/dbusnotification.cpp index 03338ca..2135d9b 100644 --- a/misc/dbusnotification.cpp +++ b/misc/dbusnotification.cpp @@ -36,7 +36,7 @@ namespace MiscUtils { /// \cond static std::map pendingNotifications; -OrgFreedesktopNotificationsInterface *DBusNotification::m_dbusInterface = nullptr; +OrgFreedesktopNotificationsInterface *DBusNotification::s_dbusInterface = nullptr; /// \endcond /*! @@ -186,11 +186,11 @@ DBusNotification::DBusNotification(const QString &title, const QString &icon, in */ void DBusNotification::initInterface() { - if (!m_dbusInterface) { - m_dbusInterface = new OrgFreedesktopNotificationsInterface( + if (!s_dbusInterface) { + s_dbusInterface = new OrgFreedesktopNotificationsInterface( QStringLiteral("org.freedesktop.Notifications"), QStringLiteral("/org/freedesktop/Notifications"), QDBusConnection::sessionBus()); - connect(m_dbusInterface, &OrgFreedesktopNotificationsInterface::ActionInvoked, &DBusNotification::handleActionInvoked); - connect(m_dbusInterface, &OrgFreedesktopNotificationsInterface::NotificationClosed, &DBusNotification::handleNotificationClosed); + connect(s_dbusInterface, &OrgFreedesktopNotificationsInterface::ActionInvoked, &DBusNotification::handleActionInvoked); + connect(s_dbusInterface, &OrgFreedesktopNotificationsInterface::NotificationClosed, &DBusNotification::handleNotificationClosed); } } @@ -212,7 +212,7 @@ DBusNotification::~DBusNotification() bool DBusNotification::isAvailable() { initInterface(); - return m_dbusInterface->isValid(); + return s_dbusInterface->isValid(); } /*! @@ -273,14 +273,14 @@ void DBusNotification::deleteOnCloseOrError() */ bool DBusNotification::show() { - if (!m_dbusInterface->isValid()) { + if (!s_dbusInterface->isValid()) { emit error(); return false; } delete m_watcher; m_watcher = new QDBusPendingCallWatcher( - m_dbusInterface->Notify(QCoreApplication::applicationName(), m_id, m_icon, m_title, m_msg, m_actions, m_hints, m_timeout), this); + s_dbusInterface->Notify(QCoreApplication::applicationName(), m_id, m_icon, m_title, m_msg, m_actions, m_hints, m_timeout), this); connect(m_watcher, &QDBusPendingCallWatcher::finished, this, &DBusNotification::handleNotifyResult); return true; } @@ -324,15 +324,38 @@ bool DBusNotification::update(const QString &line) return show(); } +bool DBusNotification::queryCapabilities(const std::function &callback) +{ + // ensure DBus-interface is initialized and valid + initInterface(); + if (!s_dbusInterface->isValid()) { + return false; + } + + // invoke GetCapabilities() and pass the return value to the callback when available + const auto *const watcher = new QDBusPendingCallWatcher(s_dbusInterface->GetCapabilities()); + connect(watcher, &QDBusPendingCallWatcher::finished, [&callback](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + const QDBusPendingReply returnValue(*watcher); + if (returnValue.isError()) { + callback(Capabilities()); + } else { + callback(Capabilities(move(returnValue.value()))); + } + }); + return true; +} + /*! * \brief Hides the notification (if still visible). * \remarks On success, the signal closed() is emitted with the reason * NotificationCloseReason::Manually. + * \todo Add return value in v6. */ void DBusNotification::hide() { if (m_id) { - m_dbusInterface->CloseNotification(m_id); + s_dbusInterface->CloseNotification(m_id); } } @@ -389,7 +412,7 @@ void DBusNotification::handleActionInvoked(uint id, const QString &action) pendingNotifications.erase(i); // however, lxqt-notificationd does not close the notification // -> close manually for consistent behaviour - m_dbusInterface->CloseNotification(i->first); + s_dbusInterface->CloseNotification(i->first); } } diff --git a/misc/dbusnotification.h b/misc/dbusnotification.h index 0f1fd17..55a02f6 100644 --- a/misc/dbusnotification.h +++ b/misc/dbusnotification.h @@ -4,6 +4,7 @@ #include "../global.h" #include +#include #include QT_FORWARD_DECLARE_CLASS(QDBusPendingCallWatcher) @@ -26,6 +27,26 @@ class QT_UTILITIES_EXPORT DBusNotification : public QObject { Q_PROPERTY(bool visible READ isVisible) public: + class QT_UTILITIES_EXPORT Capabilities : public QSet { + public: + explicit Capabilities(); + explicit Capabilities(const QStringList &capabilities); + bool isValid() const; + bool supportsBody() const; + bool supportsLinks() const; + bool supportsMarkup() const; + bool supportsImages() const; + bool supportsIcon() const; + bool supportsActions() const; + bool supportsAnimatedIcon() const; + bool supportsActionIcons() const; + bool supportsSound() const; + bool supportsPercistence() const; + + private: + bool m_valid; + }; + explicit DBusNotification( const QString &title, NotificationIcon icon = NotificationIcon::Information, int timeout = 10000, QObject *parent = nullptr); explicit DBusNotification(const QString &title, const QString &icon, int timeout = 10000, QObject *parent = nullptr); @@ -53,6 +74,7 @@ public: QVariant hint(const QString &name, const QString &fallbackNames...) const; bool isVisible() const; void deleteOnCloseOrError(); + static bool queryCapabilities(const std::function &callback); public Q_SLOTS: bool show(); @@ -86,9 +108,75 @@ private: int m_timeout; QStringList m_actions; QVariantMap m_hints; - static OrgFreedesktopNotificationsInterface *m_dbusInterface; + static OrgFreedesktopNotificationsInterface *s_dbusInterface; }; +inline DBusNotification::Capabilities::Capabilities() + : m_valid(false) +{ +} + +inline DBusNotification::Capabilities::Capabilities(const QStringList &capabilities) + : QSet(capabilities.toSet()) + , m_valid(true) +{ +} + +inline bool DBusNotification::Capabilities::isValid() const +{ + return m_valid; +} + +inline bool DBusNotification::Capabilities::supportsBody() const +{ + return contains(QStringLiteral("body")); +} + +inline bool DBusNotification::Capabilities::supportsLinks() const +{ + return contains(QStringLiteral("body-hyperlinks")); +} + +inline bool DBusNotification::Capabilities::supportsMarkup() const +{ + return contains(QStringLiteral("body-markup")); +} + +inline bool DBusNotification::Capabilities::supportsImages() const +{ + return contains(QStringLiteral("body-images")); +} + +inline bool DBusNotification::Capabilities::supportsIcon() const +{ + return contains(QStringLiteral("icon-static")) || supportsAnimatedIcon(); +} + +inline bool DBusNotification::Capabilities::supportsActions() const +{ + return contains(QStringLiteral("actions")); +} + +inline bool DBusNotification::Capabilities::supportsAnimatedIcon() const +{ + return contains(QStringLiteral("icon-multi")); +} + +inline bool DBusNotification::Capabilities::supportsActionIcons() const +{ + return contains(QStringLiteral("action-icons")); +} + +inline bool DBusNotification::Capabilities::supportsSound() const +{ + return contains(QStringLiteral("sound")); +} + +inline bool DBusNotification::Capabilities::supportsPercistence() const +{ + return contains(QStringLiteral("persistence")); +} + inline const QString &DBusNotification::title() const { return m_title;