From 4fd3a9ca5beca327e69452757b6e5fe797082597 Mon Sep 17 00:00:00 2001
From: Dennis Schridde <devurandom@gna.org>
Date: Mon, 19 May 2008 17:24:59 +0200
Subject: [PATCH] Improved support for org.freedesktop.Notifications.

Add a DesktopNotifications class derived from QDBusAbstractInterface, to be able to call the interface more conveniently.
Replace notifications if possible, instead of spamming new ones.
React on NotificationClosed signals to properly decide which replace_id to use.
Add a dummy button for demonstration to the notification.
---
 src/client/client.pri                        |    6 +-
 src/client/desktopnotifications.cpp          |   26 ++++++++
 src/client/desktopnotifications.h            |   80 ++++++++++++++++++++++++++
 src/client/org.freedesktop.Notifications.xml |   36 ++++++++++++
 src/qtui/mainwin.cpp                         |   57 ++++++++++++++----
 src/qtui/mainwin.h                           |    7 ++
 6 files changed, 198 insertions(+), 14 deletions(-)
 create mode 100644 src/client/desktopnotifications.cpp
 create mode 100644 src/client/desktopnotifications.h
 create mode 100644 src/client/org.freedesktop.Notifications.xml

diff --git a/src/client/client.pri b/src/client/client.pri
index 0945919..88461ce 100644
--- a/src/client/client.pri
+++ b/src/client/client.pri
@@ -2,9 +2,11 @@ DEPMOD = common
 QT_MOD = core network gui
 
 SRCS += buffer.cpp buffersettings.cpp clientbacklogmanager.cpp treemodel.cpp networkmodel.cpp buffermodel.cpp \
-        client.cpp clientsettings.cpp clientsyncer.cpp mappedselectionmodel.cpp selectionmodelsynchronizer.cpp
+        client.cpp clientsettings.cpp clientsyncer.cpp mappedselectionmodel.cpp selectionmodelsynchronizer.cpp \
+		desktopnotifications.cpp
 HDRS += buffer.h buffersettings.h clientbacklogmanager.h treemodel.h networkmodel.h buffermodel.h \
-        client.h clientsettings.h clientsyncer.h quasselui.h mappedselectionmodel.h selectionmodelsynchronizer.h
+        client.h clientsettings.h clientsyncer.h quasselui.h mappedselectionmodel.h selectionmodelsynchronizer.h \
+		desktopnotifications.h
 
 sputdev {
   SRCS += messagemodel.cpp
diff --git a/src/client/desktopnotifications.cpp b/src/client/desktopnotifications.cpp
new file mode 100644
index 0000000..ddbc752
--- /dev/null
+++ b/src/client/desktopnotifications.cpp
@@ -0,0 +1,26 @@
+/*
+ * This file was generated by dbusxml2cpp version 0.6
+ * Command line was: dbusxml2cpp -p desktopnotifications org.freedesktop.Notifications.xml
+ *
+ * dbusxml2cpp is Copyright (C) 2006 Trolltech ASA. All rights reserved.
+ *
+ * This is an auto-generated file.
+ * This file may have been hand-edited. Look for HAND-EDIT comments
+ * before re-generating it.
+ */
+
+#include "desktopnotifications.h"
+
+/*
+ * Implementation of interface class OrgFreedesktopNotificationsInterface
+ */
+
+OrgFreedesktopNotificationsInterface::OrgFreedesktopNotificationsInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
+    : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
+{
+}
+
+OrgFreedesktopNotificationsInterface::~OrgFreedesktopNotificationsInterface()
+{
+}
+
diff --git a/src/client/desktopnotifications.h b/src/client/desktopnotifications.h
new file mode 100644
index 0000000..ec26b5f
--- /dev/null
+++ b/src/client/desktopnotifications.h
@@ -0,0 +1,80 @@
+/*
+ * This file was generated by dbusxml2cpp version 0.6
+ * Command line was: dbusxml2cpp -p desktopnotifications org.freedesktop.Notifications.xml
+ *
+ * dbusxml2cpp is Copyright (C) 2006 Trolltech ASA. All rights reserved.
+ *
+ * This is an auto-generated file.
+ * Do not edit! All changes made to it will be lost.
+ */
+
+#ifndef DESKTOPNOTIFICATIONS_H_1211193208
+#define DESKTOPNOTIFICATIONS_H_1211193208
+
+#include <QtCore/QObject>
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QVariant>
+#include <QtDBus/QtDBus>
+
+/*
+ * Proxy class for interface org.freedesktop.Notifications
+ */
+class OrgFreedesktopNotificationsInterface: public QDBusAbstractInterface
+{
+    Q_OBJECT
+public:
+    static inline const char *staticInterfaceName()
+    { return "org.freedesktop.Notifications"; }
+
+public:
+    OrgFreedesktopNotificationsInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);
+
+    ~OrgFreedesktopNotificationsInterface();
+
+public Q_SLOTS: // METHODS
+    inline QDBusReply<void> CloseNotification(uint id)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(id);
+        return callWithArgumentList(QDBus::Block, QLatin1String("CloseNotification"), argumentList);
+    }
+
+    inline QDBusReply<QStringList> GetCapabilities()
+    {
+        QList<QVariant> argumentList;
+        return callWithArgumentList(QDBus::Block, QLatin1String("GetCapabilities"), argumentList);
+    }
+
+    inline QDBusReply<QString> GetServerInformation(QString &vendor, QString &version)
+    {
+        QList<QVariant> argumentList;
+        QDBusMessage reply = callWithArgumentList(QDBus::Block, QLatin1String("GetServerInformation"), argumentList);
+        if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 3) {
+            vendor = qdbus_cast<QString>(reply.arguments().at(1));
+            version = qdbus_cast<QString>(reply.arguments().at(2));
+        }
+        return reply;
+    }
+
+    inline QDBusReply<uint> Notify(const QString &app_name, uint replaces_id, const QString &app_icon, const QString &summary, const QString &body, const QStringList &actions, const QVariantMap &hints, int expire_timeout)
+    {
+        QList<QVariant> argumentList;
+        argumentList << qVariantFromValue(app_name) << qVariantFromValue(replaces_id) << qVariantFromValue(app_icon) << qVariantFromValue(summary) << qVariantFromValue(body) << qVariantFromValue(actions) << qVariantFromValue(hints) << qVariantFromValue(expire_timeout);
+        return callWithArgumentList(QDBus::Block, QLatin1String("Notify"), argumentList);
+    }
+
+Q_SIGNALS: // SIGNALS
+    void ActionInvoked(uint id, const QString &action);
+    void NotificationClosed(uint id, uint reason);
+};
+
+namespace org {
+  namespace freedesktop {
+    typedef ::OrgFreedesktopNotificationsInterface Notifications;
+  }
+}
+#endif
diff --git a/src/client/org.freedesktop.Notifications.xml b/src/client/org.freedesktop.Notifications.xml
new file mode 100644
index 0000000..0827cba
--- /dev/null
+++ b/src/client/org.freedesktop.Notifications.xml
@@ -0,0 +1,36 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.freedesktop.Notifications">
+    <signal name="NotificationClosed">
+      <arg name="id" type="u" direction="out"/>
+      <arg name="reason" type="u" direction="out"/>
+    </signal>
+    <signal name="ActionInvoked">
+      <arg name="id" type="u" direction="out"/>
+      <arg name="action" type="s" direction="out"/>
+    </signal>
+    <method name="GetCapabilities">
+      <arg type="as" direction="out"/>
+    </method>
+    <method name="GetServerInformation">
+      <arg name="name" type="s" direction="out"/>
+      <arg name="vendor" type="s" direction="out"/>
+      <arg name="version" type="s" direction="out"/>
+    </method>
+    <method name="Notify">
+      <arg type="u" direction="out"/>
+      <arg name="app_name" type="s" direction="in"/>
+      <arg name="replaces_id" type="u" direction="in"/>
+      <arg name="app_icon" type="s" direction="in"/>
+      <arg name="summary" type="s" direction="in"/>
+      <arg name="body" type="s" direction="in"/>
+      <arg name="actions" type="as" direction="in"/>
+      <arg name="hints" type="a{sv}" direction="in"/>
+      <annotation name="com.trolltech.QtDBus.QtTypeName.In6" value="QVariantMap"/>
+      <arg name="expire_timeout" type="i" direction="in"/>
+    </method>
+    <method name="CloseNotification">
+      <arg name="id" type="u" direction="in"/>
+    </method>
+  </interface>
+</node>
diff --git a/src/qtui/mainwin.cpp b/src/qtui/mainwin.cpp
index 78c408a..7d893df 100644
--- a/src/qtui/mainwin.cpp
+++ b/src/qtui/mainwin.cpp
@@ -60,7 +60,7 @@
 #include "global.h"
 #include "qtuistyle.h"
 
-#include <Qt/QtDBus>
+#include "desktopnotifications.h"
 
 
 MainWin::MainWin(QtUi *_gui, QWidget *parent)
@@ -75,7 +75,12 @@ MainWin::MainWin(QtUi *_gui, QWidget *parent)
     trayIconActive(false),
     timer(new QTimer(this)),
     settingsDlg(new SettingsDlg(this)),
-    debugConsole(new DebugConsole(this))
+    debugConsole(new DebugConsole(this)),
+	desktopNotifications(new org::freedesktop::Notifications(
+		"org.freedesktop.Notifications",
+		"/org/freedesktop/Notifications",
+		QDBusConnection::sessionBus(), this)),
+	notificationId(0)
 {
   ui.setupUi(this);
   setWindowTitle("Quassel IRC");
@@ -93,6 +98,9 @@ MainWin::MainWin(QtUi *_gui, QWidget *parent)
   if(style != "") {
     QApplication::setStyle(style);
   }
+
+  connect(desktopNotifications, SIGNAL(NotificationClosed(uint, uint)), this, SLOT(desktopNotificationClosed(uint, uint)));
+  connect(desktopNotifications, SIGNAL(ActionInvoked(uint, const QString&)), this, SLOT(desktopNotificationInvoked(uint, const QString&)));
 }
 
 void MainWin::init() {
@@ -599,21 +607,46 @@ void MainWin::sendDesktopNotification(const QString &title, const QString &messa
 {
 	QStringList actions;
 	QMap<QString, QVariant> hints;
-	QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "", "Notify");
 
 	hints["x"] = 100; // Standard hint: x location for the popup to show up
 	hints["y"] = 100; // Standard hint: y location for the popup to show up
 
-	msg << "Quassel"; // Application name
-	msg << quint32(0); // ID of previous notification to replace
-	msg << ""; // Icon to display
-	msg << "Quassel: " + title; // Summary / Header of the message to display
-	msg << message; // Body of the message to display
-	msg << actions; // Actions from which the user may choose
-	msg << hints; // Hints to the server displaying the message
-	msg << qint32(10000); // Timeout in milliseconds
+	actions << "click" << "Click Me!";
+
+	QDBusReply<uint> reply = desktopNotifications->Notify(
+		"Quassel", // Application name
+		notificationId, // ID of previous notification to replace
+		"", // Icon to display
+		title, // Summary / Header of the message to display
+		QString("%1: %2:\n%2").arg(QTime::currentTime().toString()).arg(title).arg(message), // Body of the message to display
+		actions, // Actions from which the user may choose
+		hints, // Hints to the server displaying the message
+		5000 // Timeout in milliseconds
+	);
+
+	if (!reply.isValid())
+	{
+		/* ERROR */
+		qDebug() << "Error on sending notification...";
+		return;
+	}
+
+	notificationId = reply.value();
+
+	qDebug() << "ID: " << notificationId << " Time: " << QTime::currentTime().toString();
+}
+
+
+void MainWin::desktopNotificationClosed(uint id, uint reason)
+{
+	qDebug() << "OID: " << notificationId << " ID: " << id << " Reason: " << reason << " Time: " << QTime::currentTime().toString();
+	notificationId = 0;
+}
+
 
-	(void)QDBusConnection::sessionBus().call(msg); // Would return a message containing the id of this notification
+void MainWin::desktopNotificationInvoked(uint id, const QString & action)
+{
+	qDebug() << "OID: " << notificationId << " ID: " << id << " Action: " << action << " Time: " << QTime::currentTime().toString();
 }
 
 
diff --git a/src/qtui/mainwin.h b/src/qtui/mainwin.h
index 6d1752f..a80b371 100644
--- a/src/qtui/mainwin.h
+++ b/src/qtui/mainwin.h
@@ -40,6 +40,7 @@ class Message;
 class NickListWidget;
 class DebugConsole;
 
+#include "desktopnotifications.h"
 
 //!\brief The main window of Quassel's QtUi.
 class MainWin : public QMainWindow {
@@ -97,6 +98,9 @@ class MainWin : public QMainWindow {
     void loadLayout();
     void saveLayout();
 
+	void desktopNotificationClosed(uint id, uint reason);
+	void desktopNotificationInvoked(uint id, const QString & action);
+
   signals:
     void connectToCore(const QVariantMap &connInfo);
     void disconnectFromCore();
@@ -146,6 +150,9 @@ class MainWin : public QMainWindow {
     QAction *actionEditNetworks;
     QList<QAction *> networkActions;
 
+	org::freedesktop::Notifications *desktopNotifications;
+	quint32 notificationId;
+
     friend class QtUi;
 };
 
-- 
1.5.5.1

