diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cb62ad61..fa5b21216 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Widgets Core Quick Gui QuickControls2 Multimedia) -find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS Kirigami2 ItemModels I18n) +find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS Kirigami2 ItemModels I18n Notifications) find_package(Quotient 0.7 REQUIRED) find_package(Qt5Keychain REQUIRED) @@ -29,6 +29,7 @@ find_package(cmark REQUIRED) install(PROGRAMS org.kde.neochat.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES org.kde.neochat.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) #install(FILES neochat.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps) +install(FILES neochat.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) add_subdirectory(src) diff --git a/neochat.notifyrc b/neochat.notifyrc new file mode 100644 index 000000000..b5811ba1b --- /dev/null +++ b/neochat.notifyrc @@ -0,0 +1,10 @@ +[Global] +IconName=neochat +Name=Neochat +DesktopEntry=org.kde.neochat +Comment=IM client for the Matrix protocol + +[Event/message] +Name=New message +Comment=There is a new message +Action=Popup diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 337c0a0fa..b4be5199f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,10 +14,10 @@ add_executable(neochat userdirectorylistmodel.cpp utils.cpp main.cpp - manager.cpp + notificationsmanager.cpp ../res.qrc ) -target_link_libraries(neochat Qt5::Widgets Qt5::Quick Qt5::Qml Qt5::Gui Qt5::Network Qt5::QuickControls2 KF5::Kirigami2 Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES}) +target_link_libraries(neochat Qt5::Widgets Qt5::Quick Qt5::Qml Qt5::Gui Qt5::Network Qt5::QuickControls2 KF5::Kirigami2 KF5::Notifications Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES}) install(TARGETS neochat ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/main.cpp b/src/main.cpp index 7890b61c1..da9fe3864 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,7 @@ #include "imageclipboard.h" #include "matriximageprovider.h" #include "messageeventmodel.h" -#include "manager.h" +#include "notificationsmanager.h" #include "publicroomlistmodel.h" #include "room.h" #include "roomlistmodel.h" @@ -40,7 +40,7 @@ int main(int argc, char *argv[]) app.setOrganizationName("KDE"); app.setOrganizationDomain("kde.org"); - app.setApplicationName("NeoChat"); + app.setApplicationName("neochat"); app.setWindowIcon(QIcon(":/assets/img/icon.png")); qmlRegisterType("Spectral", 0, 1, "Controller"); diff --git a/src/manager.cpp b/src/manager.cpp deleted file mode 100644 index db2d8ef69..000000000 --- a/src/manager.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "manager.h" - -#include -#include -#include -#include -#include -#include - -NotificationsManager::NotificationsManager(QObject *parent) - : QObject(parent) - , dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus(), this) -{ - qDBusRegisterMetaType(); - - const QDBusReply capabilitiesReply = dbus.call("GetCapabilities"); - - if (capabilitiesReply.isValid()) { - const QStringList capabilities = capabilitiesReply.value(); - serverSupportsHtml = capabilities.contains("body-markup"); - } else { - qWarning() << "Could not get notification server capabilities" << capabilitiesReply.error(); - } - - QDBusConnection::sessionBus().connect("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "ActionInvoked", this, SLOT(actionInvoked(uint, QString))); - QDBusConnection::sessionBus().connect("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "NotificationClosed", this, SLOT(notificationClosed(uint, uint))); -} - -void NotificationsManager::postNotification(const QString &roomid, const QString &eventid, const QString &roomname, const QString &sender, const QString &text, const QImage &icon) -{ - uint id = showNotification(sender + " (" + roomname + ")", text, icon); - notificationIds[id] = roomEventId {roomid, eventid}; -} -/** - * This function is based on code from - * https://github.com/rohieb/StratumsphereTrayIcon - * Copyright (C) 2012 Roland Hieber - * Licensed under the GNU General Public License, version 3 - */ -uint NotificationsManager::showNotification(const QString summary, const QString text, const QImage image) -{ - QImage croppedImage; - QRect rect = image.rect(); - if (rect.width() != rect.height()) { - if (rect.width() > rect.height()) { - QRect crop((rect.width() - rect.height()) / 2, 0, rect.height(), rect.height()); - croppedImage = image.copy(crop); - } else { - QRect crop(0, (rect.height() - rect.width()) / 2, rect.width(), rect.width()); - croppedImage = image.copy(crop); - } - } else { - croppedImage = image; - } - - const QString body = serverSupportsHtml ? text.toHtmlEscaped() : text; - - QVariantMap hints; - hints["image-data"] = croppedImage; - hints["desktop-entry"] = "org.eu.encom.spectral"; - QList argumentList; - argumentList << "Spectral"; // app_name - argumentList << uint(0); // replace_id - argumentList << ""; // app_icon - argumentList << summary; // summary - argumentList << body; // body - argumentList << (QStringList("default") << "reply"); // actions - argumentList << hints; // hints - argumentList << int(-1); // timeout in ms - - static QDBusInterface notifyApp("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications"); - QDBusMessage reply = notifyApp.callWithArgumentList(QDBus::AutoDetect, "Notify", argumentList); - if (reply.type() == QDBusMessage::ErrorMessage) { - qDebug() << "D-Bus Error:" << reply.errorMessage(); - return 0; - } else { - return reply.arguments().first().toUInt(); - } -} - -void NotificationsManager::actionInvoked(uint id, QString action) -{ - if (action == "default" && notificationIds.contains(id)) { - roomEventId idEntry = notificationIds[id]; - emit notificationClicked(idEntry.roomId, idEntry.eventId); - } -} - -void NotificationsManager::notificationClosed(uint id, uint reason) -{ - Q_UNUSED(reason); - notificationIds.remove(id); -} - -/** - * Automatic marshaling of a QImage for org.freedesktop.Notifications.Notify - * - * This function is from the Clementine project (see - * http://www.clementine-player.org) and licensed under the GNU General Public - * License, version 3 or later. - * - * Copyright 2010, David Sansome - */ -QDBusArgument &operator<<(QDBusArgument &arg, const QImage &image) -{ - if (image.isNull()) { - arg.beginStructure(); - arg << 0 << 0 << 0 << false << 0 << 0 << QByteArray(); - arg.endStructure(); - return arg; - } - - QImage scaled = image.scaledToHeight(100, Qt::SmoothTransformation); - scaled = scaled.convertToFormat(QImage::Format_ARGB32); - -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - // ABGR -> ARGB - QImage i = scaled.rgbSwapped(); -#else - // ABGR -> GBAR - QImage i(scaled.size(), scaled.format()); - for (int y = 0; y < i.height(); ++y) { - QRgb *p = (QRgb *)scaled.scanLine(y); - QRgb *q = (QRgb *)i.scanLine(y); - QRgb *end = p + scaled.width(); - while (p < end) { - *q = qRgba(qGreen(*p), qBlue(*p), qAlpha(*p), qRed(*p)); - p++; - q++; - } - } -#endif - - arg.beginStructure(); - arg << i.width(); - arg << i.height(); - arg << i.bytesPerLine(); - arg << i.hasAlphaChannel(); - int channels = i.isGrayscale() ? 1 : (i.hasAlphaChannel() ? 4 : 3); - arg << i.depth() / channels; - arg << channels; - arg << QByteArray(reinterpret_cast(i.bits()), i.sizeInBytes()); - arg.endStructure(); - return arg; -} - -const QDBusArgument &operator>>(const QDBusArgument &arg, QImage &) -{ - // This is needed to link but shouldn't be called. - Q_ASSERT(0); - return arg; -} diff --git a/src/manager.h b/src/manager.h deleted file mode 100644 index fa801d6c7..000000000 --- a/src/manager.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) -#include -#include -#endif - -struct roomEventId { - QString roomId; - QString eventId; -}; - -class NotificationsManager : public QObject -{ - Q_OBJECT -public: - NotificationsManager(QObject *parent = nullptr); - -signals: - void notificationClicked(const QString roomId, const QString eventId); - -private: -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) - QDBusInterface dbus; - bool serverSupportsHtml = false; - uint showNotification(const QString summary, const QString text, const QImage image); -#endif - - // notification ID to (room ID, event ID) - QMap notificationIds; - - // these slots are platform specific (D-Bus only) - // but Qt slot declarations can not be inside an ifdef! -public slots: - void actionInvoked(uint id, QString action); - void notificationClosed(uint id, uint reason); - - void postNotification(const QString &roomId, const QString &eventId, const QString &roomName, const QString &senderName, const QString &text, const QImage &icon); -}; - -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) -QDBusArgument &operator<<(QDBusArgument &arg, const QImage &image); -const QDBusArgument &operator>>(const QDBusArgument &arg, QImage &); -#endif diff --git a/src/notificationsmanager.cpp b/src/notificationsmanager.cpp new file mode 100644 index 000000000..c52c94a99 --- /dev/null +++ b/src/notificationsmanager.cpp @@ -0,0 +1,24 @@ +#include "notificationsmanager.h" + +#include +#include + +#include + +NotificationsManager::NotificationsManager(QObject *parent) + : QObject(parent) +{ +} + +void NotificationsManager::postNotification(const QString &roomid, const QString &eventid, const QString &roomname, const QString &sender, const QString &text, const QImage &icon) +{ + QPixmap img; + img.convertFromImage(icon); + KNotification *notification = new KNotification("message"); + notification->setTitle(i18n("%1 (%2)", sender, roomname)); + notification->setText(text); + notification->setPixmap(img); + notification->sendEvent(); + + m_notifications.insert(roomid, notification); +} diff --git a/src/notificationsmanager.h b/src/notificationsmanager.h new file mode 100644 index 000000000..8f581bb48 --- /dev/null +++ b/src/notificationsmanager.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include + +#include + +class NotificationsManager : public QObject +{ + Q_OBJECT + +public: + NotificationsManager(QObject *parent = nullptr); + + Q_INVOKABLE void postNotification(const QString &roomId, const QString &eventId, const QString &roomName, const QString &senderName, const QString &text, const QImage &icon); + +private: + QMultiMap m_notifications; +};