From f76d4044aa4f6643e5da2f233f587264b0468d12 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sun, 18 Mar 2018 03:07:03 +0100 Subject: [PATCH] Support setting image and image path of DBus notifications --- CMakeLists.txt | 4 +- misc/dbusnotification.cpp | 135 ++++++++++++++++++++++++++++++++++++++ misc/dbusnotification.h | 42 ++++++++++++ 3 files changed, 179 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea95f0d..04c50a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,8 @@ 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 8) -set(META_VERSION_PATCH 3) +set(META_VERSION_MINOR 9) +set(META_VERSION_PATCH 0) set(META_APP_VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}) # add project files diff --git a/misc/dbusnotification.cpp b/misc/dbusnotification.cpp index 97e54c6..03338ca 100644 --- a/misc/dbusnotification.cpp +++ b/misc/dbusnotification.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -38,6 +39,120 @@ static std::map pendingNotifications; OrgFreedesktopNotificationsInterface *DBusNotification::m_dbusInterface = nullptr; /// \endcond +/*! + * \brief The SwappedImage struct represents RGB-interved version of the image specified on construction. + */ +struct SwappedImage : public QImage { + SwappedImage(const QImage &image); +}; + +inline SwappedImage::SwappedImage(const QImage &image) + : QImage(image.rgbSwapped()) +{ +} + +/*! + * \brief The ImageData struct is a raw data image format. + * + * It describes the width, height, rowstride, has alpha, bits per sample, channels and image data respectively. + */ +struct NotificationImage { + NotificationImage(); + NotificationImage(const QVariant &imageData); + NotificationImage(SwappedImage image); + QImage toQImage() const; + QVariant toDBusArgument() const; + + qint32 width; + qint32 height; + qint32 rowstride; + bool hasAlpha; + qint32 channels; + qint32 bitsPerSample; + QByteArray data; + bool isValid; + +private: + NotificationImage(const QImage &image); +}; + +QDBusArgument &operator<<(QDBusArgument &argument, const NotificationImage &img) +{ + argument.beginStructure(); + argument << img.width; + argument << img.height; + argument << img.rowstride; + argument << img.hasAlpha; + argument << img.bitsPerSample; + argument << img.channels; + argument << img.data; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, NotificationImage &img) +{ + argument.beginStructure(); + argument >> img.width; + argument >> img.height; + argument >> img.rowstride; + argument >> img.hasAlpha; + argument >> img.bitsPerSample; + argument >> img.channels; + argument >> img.data; + argument.endStructure(); + return argument; +} + +inline NotificationImage::NotificationImage() + : isValid(false) +{ +} + +inline NotificationImage::NotificationImage(const QVariant &imageData) + : isValid(imageData.canConvert()) +{ + if (isValid) { + imageData.value() >> *this; + } +} + +NotificationImage::NotificationImage(SwappedImage image) + : NotificationImage(static_cast(image)) +{ +} + +inline NotificationImage::NotificationImage(const QImage &image) + : width(image.width()) + , height(image.height()) + , rowstride(image.bytesPerLine()) + , hasAlpha(image.hasAlphaChannel()) + , channels(image.isGrayscale() ? 1 : hasAlpha ? 4 : 3) + , bitsPerSample(image.depth() / channels) + , data(reinterpret_cast(image.bits()), image.byteCount()) + , isValid(!image.isNull()) +{ +} + +inline QImage NotificationImage::toQImage() const +{ + return isValid ? QImage(reinterpret_cast(data.constData()), width, height, hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32) + .rgbSwapped() + : QImage(); +} + +inline QVariant NotificationImage::toDBusArgument() const +{ + QDBusArgument arg; + return QVariant::fromValue(isValid ? arg << *this : arg); +} + +} // namespace MiscUtils + +Q_DECLARE_METATYPE(MiscUtils::NotificationImage); + +namespace MiscUtils { + /*! * \brief Creates a new notification (which is *not* shown instantly). */ @@ -119,6 +234,26 @@ void DBusNotification::setIcon(NotificationIcon icon) } } +/*! + * \brief Returns the image. + * \sa setImage() for more details + */ +const QImage DBusNotification::image() const +{ + return NotificationImage(hint(QStringLiteral("image-data"), QStringLiteral("image_data"))).toQImage(); +} + +/*! + * \brief Sets the image. + * \remarks + * \a image is a raw data image format which describes the width, height, rowstride, + * has alpha, bits per sample, channels and image data respectively. + */ +void DBusNotification::setImage(const QImage &image) +{ + m_hints[QStringLiteral("image-data")] = NotificationImage(SwappedImage(image)).toDBusArgument(); +} + /*! * \brief Makes the notification object delete itself when the notification has * been closed or an error occured. diff --git a/misc/dbusnotification.h b/misc/dbusnotification.h index b229e2e..0f1fd17 100644 --- a/misc/dbusnotification.h +++ b/misc/dbusnotification.h @@ -39,12 +39,18 @@ public: const QString &icon() const; void setIcon(const QString &icon); void setIcon(NotificationIcon icon); + const QImage image() const; + void setImage(const QImage &image); + const QString imagePath() const; + void setImagePath(const QString &imagePath); int timeout() const; void setTimeout(int timeout); const QStringList &actions() const; void setActions(const QStringList &actions); const QVariantMap &hints() const; QVariantMap &hints(); + QVariant hint(const QString &name) const; + QVariant hint(const QString &name, const QString &fallbackNames...) const; bool isVisible() const; void deleteOnCloseOrError(); @@ -123,6 +129,42 @@ inline void DBusNotification::setIcon(const QString &icon) m_icon = icon; } +/*! + * \brief Returns the hint with the specified \a name. + */ +inline QVariant DBusNotification::hint(const QString &name) const +{ + return m_hints[name]; +} + +/*! + * \brief Returns the hint with the specified \a name. If no hint is present, the \a fallbackNames are tried in the specified order. + */ +inline QVariant DBusNotification::hint(const QString &name, const QString &fallbackNames...) const +{ + const auto variant(m_hints[name]); + return variant.isNull() ? this->hint(fallbackNames) : variant; +} + +/*! + * \brief Returns the image path. + * \sa setImagePath() for more details + */ +inline const QString DBusNotification::imagePath() const +{ + return hint(QStringLiteral("image-data"), QStringLiteral("image_path")).toString(); +} + +/*! + * \brief Sets the image path. + * \remarks + * Alternative way to define the notification image; setImage() precedes. + */ +inline void DBusNotification::setImagePath(const QString &imagePath) +{ + m_hints[QStringLiteral("image-path")] = imagePath; +} + inline int DBusNotification::timeout() const { return m_timeout;