Integrate DesktopNotification into system tray/status notifier
authorManuel Nickschas <sputnick@quassel-irc.org>
Tue, 2 Mar 2010 23:08:21 +0000 (00:08 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Wed, 3 Mar 2010 01:36:37 +0000 (02:36 +0100)
Since the DBus notification spec is quite widespread by now, and in fact KDE's
StatusNotifierItem uses that same spec for displaying popup messages, we now
integrate the former DesktopNotification into StatusNotifierItem.

If DBus or a NotificationsClient are not present, we fall back to traditional systray
popups. This could be fugly if we have StatusNotifier, but not NotificationsClient, but
all platforms supporting the former should also support the latter by now.

Having this in SystemTray also makes our notification handling code much nicer, and we have
one less backend to care about. Additionally, users using current versions of KDE or GNOME
will get pretty popups automatically.

13 files changed:
src/qtui/CMakeLists.txt
src/qtui/desktopnotificationbackend.cpp [deleted file]
src/qtui/desktopnotificationbackend.h [deleted file]
src/qtui/legacysystemtray.cpp
src/qtui/legacysystemtray.h
src/qtui/mainwin.cpp
src/qtui/statusnotifieritem.cpp
src/qtui/statusnotifieritem.h
src/qtui/systemtray.cpp
src/qtui/systemtray.h
src/qtui/systraynotificationbackend.cpp
src/qtui/systraynotificationbackend.h
src/qtui/ui/desktopnotificationconfigwidget.ui [deleted file]

index 4b76e97..b806d66 100644 (file)
@@ -139,13 +139,6 @@ if(HAVE_KDE)
   set(SOURCES ${SOURCES} knotificationbackend.cpp)
   set(MOC_HDRS ${MOC_HDRS} knotificationbackend.h)
 else(HAVE_KDE)
-  if(HAVE_DBUS)
-    set(SOURCES ${SOURCES} desktopnotificationbackend.cpp)
-    set(MOC_HDRS ${MOC_HDRS} desktopnotificationbackend.h)
-    set(FORMS ${FORMS} desktopnotificationconfigwidget.ui)
-    qt4_add_dbus_interface(DBUS ../../interfaces/org.freedesktop.Notifications.xml desktopnotificationinterface)
-  endif(HAVE_DBUS)
-
   if(HAVE_PHONON)
     set(SOURCES ${SOURCES} phononnotificationbackend.cpp)
     set(MOC_HDRS ${MOC_HDRS} phononnotificationbackend.h)
@@ -159,6 +152,7 @@ if(HAVE_DBUS)
   set(MOC_HDRS ${MOC_HDRS} statusnotifieritem.h statusnotifieritemdbus.h)
   set(FORMS ${FORMS})
   qt4_add_dbus_interface(DBUS ../../interfaces/org.kde.StatusNotifierWatcher.xml statusnotifierwatcher)
+  qt4_add_dbus_interface(DBUS ../../interfaces/org.freedesktop.Notifications.xml notificationsclient)
   qt4_add_dbus_adaptor(DBUS ../../interfaces/org.kde.StatusNotifierItem.xml statusnotifieritemdbus.h StatusNotifierItemDBus)  
 endif(HAVE_DBUS)
 
diff --git a/src/qtui/desktopnotificationbackend.cpp b/src/qtui/desktopnotificationbackend.cpp
deleted file mode 100644 (file)
index dedade7..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/***************************************************************************
-*   Copyright (C) 2005-09 by the Quassel Project                          *
-*   devel@quassel-irc.org                                                 *
-*                                                                         *
-*   This program is free software; you can redistribute it and/or modify  *
-*   it under the terms of the GNU General Public License as published by  *
-*   the Free Software Foundation; either version 2 of the License, or     *
-*   (at your option) version 3.                                           *
-*                                                                         *
-*   This program 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 General Public License for more details.                          *
-*                                                                         *
-*   You should have received a copy of the GNU General Public License     *
-*   along with this program; if not, write to the                         *
-*   Free Software Foundation, Inc.,                                       *
-*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
-***************************************************************************/
-
-#include "desktopnotificationbackend.h"
-
-#include <QTextDocument>
-
-#include "client.h"
-#include "clientsettings.h"
-#include "networkmodel.h"
-
-DesktopNotificationBackend::DesktopNotificationBackend(QObject *parent)
-  : AbstractNotificationBackend(parent),
-  _lastDbusId(0)
-{
-  _dbusInterface = new org::freedesktop::Notifications(
-    "org.freedesktop.Notifications",
-    "/org/freedesktop/Notifications",
-    QDBusConnection::sessionBus(), this);
-
-  if(!_dbusInterface->isValid()) {
-    qWarning() << "DBus notification service not available!";
-    return;
-  }
-
-  QStringList desktopCapabilities = _dbusInterface->GetCapabilities();
-  _daemonSupportsMarkup = desktopCapabilities.contains("body-markup");
-
-  connect(_dbusInterface, SIGNAL(NotificationClosed(uint, uint)), SLOT(desktopNotificationClosed(uint, uint)));
-  connect(_dbusInterface, SIGNAL(ActionInvoked(uint, const QString &)), SLOT(desktopNotificationInvoked(uint, const QString&)));
-
-  NotificationSettings notificationSettings;
-  _enabled = notificationSettings.value("DesktopNotification/Enabled", false).toBool();
-  _useHints = notificationSettings.value("DesktopNotification/UseHints", false).toBool();
-  _xHint = notificationSettings.value("DesktopNotification/XHint", 0).toInt();
-  _yHint = notificationSettings.value("DesktopNotification/YHint", 0).toInt();
-  _queueNotifications = notificationSettings.value("DesktopNotification/QueueNotifications", true).toBool();
-  _timeout = notificationSettings.value("DesktopNotification/Timeout", 10000).toInt();
-  _useTimeout = notificationSettings.value("DesktopNotification/UseTimeout", true).toBool();
-
-  notificationSettings.notify("DesktopNotification/Enabled", this, SLOT(enabledChanged(const QVariant &)));
-  notificationSettings.notify("DesktopNotification/UseHints", this, SLOT(useHintsChanged(const QVariant &)));
-  notificationSettings.notify("DesktopNotification/XHint", this, SLOT(xHintChanged(const QVariant &)));
-  notificationSettings.notify("DesktopNotification/YHint", this, SLOT(yHintChanged(const QVariant &)));
-  notificationSettings.notify("DesktopNotification/Timeout", this, SLOT(timeoutChanged(const QVariant &)));
-  notificationSettings.notify("DesktopNotification/UseTimeout", this, SLOT(useTimeoutChanged(const QVariant &)));
-  notificationSettings.notify("DesktopNotification/QueueNotifications", this, SLOT(queueNotificationsChanged(const QVariant &)));
-}
-
-void DesktopNotificationBackend::enabledChanged(const QVariant &v) {
-  _enabled = v.toBool();
-}
-
-void DesktopNotificationBackend::useHintsChanged(const QVariant &v) {
-  _useHints = v.toBool();
-}
-
-void DesktopNotificationBackend::xHintChanged(const QVariant &v) {
-  _xHint = v.toInt();
-}
-
-void DesktopNotificationBackend::yHintChanged(const QVariant &v) {
-  _yHint = v.toInt();
-}
-
-void DesktopNotificationBackend::queueNotificationsChanged(const QVariant &v) {
-  _queueNotifications = v.toBool();
-}
-
-void DesktopNotificationBackend::timeoutChanged(const QVariant &v) {
-  _timeout = v.toInt();
-}
-
-void DesktopNotificationBackend::useTimeoutChanged(const QVariant &v) {
-  _useTimeout = v.toBool();
-}
-
-void DesktopNotificationBackend::notify(const Notification &n) {
-  if(_enabled && _dbusInterface->isValid() && (n.type == Highlight || n.type == PrivMsg)) {
-    QStringList actions;
-    QMap<QString, QVariant> hints;
-
-    if(_useHints) {
-      hints["x"] = _xHint; // Standard hint: x location for the popup to show up
-      hints["y"] = _yHint; // Standard hint: y location for the popup to show up
-    }
-
-    uint oldId = _queueNotifications ? 0 : _lastDbusId;
-
-    actions << "activate" << "View";
-
-    QString title = Client::networkModel()->networkName(n.bufferId) + " - " + Client::networkModel()->bufferName(n.bufferId);
-    QString message = QString("<%1> %2").arg(n.sender, n.message);
-
-    if(_daemonSupportsMarkup)
-      message = Qt::escape(message);
-
-    QDBusReply<uint> reply = _dbusInterface->Notify(
-      "Quassel IRC", // Application name
-      oldId, // ID of previous notification to replace
-      "quassel", // Icon to display
-      title, // Summary / Header of the message to display
-      message, // Body of the message to display
-      actions, // Actions from which the user may choose
-      hints, // Hints to the server displaying the message
-      _useTimeout? _timeout : 0 // Timeout in milliseconds
-    );
-
-    if(!reply.isValid()) {
-      /* ERROR */
-      // could also happen if no notification service runs, so... whatever :)
-      // qDebug() << "Error on sending notification..." << reply.error();
-      return;
-    }
-    uint dbusid = reply.value();
-    _idMap.insert(n.notificationId, dbusid);
-    _lastDbusId = dbusid;
-  }
-}
-
-void DesktopNotificationBackend::close(uint notificationId) {
-  uint dbusId = _idMap.value(notificationId, 0);
-  if(dbusId) {
-    _idMap.remove(notificationId);
-    _dbusInterface->CloseNotification(dbusId);
-  }
-  _lastDbusId = 0;
-}
-
-void DesktopNotificationBackend::desktopNotificationClosed(uint id, uint reason) {
-  Q_UNUSED(reason);
-  _idMap.remove(_idMap.key(id));
-  _lastDbusId = 0;
-}
-
-
-void DesktopNotificationBackend::desktopNotificationInvoked(uint id, const QString & action) {
-  Q_UNUSED(action);
-  foreach(uint ourid, _idMap.keys()) {
-    if(_idMap.value(ourid) != id)
-      continue;
-    emit activated(ourid);
-    return;
-  }
-
-  emit activated();
-}
-
-SettingsPage *DesktopNotificationBackend::createConfigWidget() const {
-  return new ConfigWidget();
-}
-
-/***************************************************************************/
-
-DesktopNotificationBackend::ConfigWidget::ConfigWidget(QWidget *parent) : SettingsPage("Internal", "DesktopNotification", parent) {
-  ui.setupUi(this);
-
-  connect(ui.enabled, SIGNAL(toggled(bool)), SLOT(widgetChanged()));
-  connect(ui.useHints, SIGNAL(toggled(bool)), SLOT(widgetChanged()));
-  connect(ui.xHint, SIGNAL(valueChanged(int)), SLOT(widgetChanged()));
-  connect(ui.yHint, SIGNAL(valueChanged(int)), SLOT(widgetChanged()));
-  connect(ui.queueNotifications, SIGNAL(toggled(bool)), SLOT(widgetChanged()));
-  connect(ui.useTimeout, SIGNAL(toggled(bool)), SLOT(widgetChanged()));
-  connect(ui.timeout, SIGNAL(valueChanged(int)), SLOT(widgetChanged()));
-}
-
-void DesktopNotificationBackend::ConfigWidget::widgetChanged() {
-  bool changed =
-       enabled != ui.enabled->isChecked()
-    || useHints != ui.useHints->isChecked()
-    || xHint != ui.xHint->value()
-    || yHint != ui.yHint->value()
-    || queueNotifications != ui.queueNotifications->isChecked()
-    || timeout/1000 != ui.timeout->value()
-    || useTimeout != ui.useTimeout->isChecked();
-  if(changed != hasChanged()) setChangedState(changed);
-}
-
-bool DesktopNotificationBackend::ConfigWidget::hasDefaults() const {
-  return true;
-}
-
-void DesktopNotificationBackend::ConfigWidget::defaults() {
-  ui.enabled->setChecked(false);
-  ui.useTimeout->setChecked(true);
-  ui.timeout->setValue(10);
-  ui.useHints->setChecked(false);
-  ui.xHint->setValue(0);
-  ui.yHint->setValue(0);
-  ui.queueNotifications->setChecked(true);
-  widgetChanged();
-}
-
-void DesktopNotificationBackend::ConfigWidget::load() {
-  NotificationSettings s;
-  enabled = s.value("DesktopNotification/Enabled", false).toBool();
-  useTimeout = s.value("DesktopNotification/UseTimeout", true).toBool();
-  timeout = s.value("DesktopNotification/Timeout", 10000).toInt();
-  useHints = s.value("DesktopNotification/UseHints", false).toBool();
-  xHint = s.value("DesktopNotification/XHint", 0).toInt();
-  yHint = s.value("DesktopNotification/YHint", 0).toInt();
-  queueNotifications = s.value("DesktopNotification/QueueNotifications", true).toBool();
-
-  ui.enabled->setChecked(enabled);
-  ui.useTimeout->setChecked(useTimeout);
-  ui.timeout->setValue(timeout/1000);
-  ui.useHints->setChecked(useHints);
-  ui.xHint->setValue(xHint);
-  ui.yHint->setValue(yHint);
-  ui.queueNotifications->setChecked(queueNotifications);
-
-  setChangedState(false);
-}
-
-void DesktopNotificationBackend::ConfigWidget::save() {
-  NotificationSettings s;
-  s.setValue("DesktopNotification/Enabled", ui.enabled->isChecked());
-  s.setValue("DesktopNotification/UseTimeout", ui.useTimeout->isChecked());
-  s.setValue("DesktopNotification/Timeout", ui.timeout->value() * 1000);
-  s.setValue("DesktopNotification/UseHints", ui.useHints->isChecked());
-  s.setValue("DesktopNotification/XHint", ui.xHint->value());
-  s.setValue("DesktopNotification/YHint", ui.yHint->value());
-  s.setValue("DesktopNotification/QueueNotifications", ui.queueNotifications->isChecked());
-
-  load();
-}
diff --git a/src/qtui/desktopnotificationbackend.h b/src/qtui/desktopnotificationbackend.h
deleted file mode 100644 (file)
index c83a3a1..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/***************************************************************************
-*   Copyright (C) 2005-09 by the Quassel Project                          *
-*   devel@quassel-irc.org                                                 *
-*                                                                         *
-*   This program is free software; you can redistribute it and/or modify  *
-*   it under the terms of the GNU General Public License as published by  *
-*   the Free Software Foundation; either version 2 of the License, or     *
-*   (at your option) version 3.                                           *
-*                                                                         *
-*   This program 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 General Public License for more details.                          *
-*                                                                         *
-*   You should have received a copy of the GNU General Public License     *
-*   along with this program; if not, write to the                         *
-*   Free Software Foundation, Inc.,                                       *
-*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
-***************************************************************************/
-
-#ifndef DESKTOPNOTIFICATIONBACKEND_H_
-#define DESKTOPNOTIFICATIONBACKEND_H_
-
-#include <QHash>
-
-#include "abstractnotificationbackend.h"
-
-#include "settingspage.h"
-
-#include "desktopnotificationinterface.h"
-#include "ui_desktopnotificationconfigwidget.h"
-
-//! Implements the freedesktop.org notifications specification (via D-Bus)
-/**
- *  cf. http://www.galago-project.org/specs/notification/
- *
- *  This class will only be available if -DHAVE_DBUS is enabled
- */
-class DesktopNotificationBackend : public AbstractNotificationBackend {
-  Q_OBJECT
-
-public:
-  DesktopNotificationBackend(QObject *parent = 0);
-
-  void notify(const Notification &);
-  void close(uint notificationId);
-  virtual SettingsPage *createConfigWidget() const;
-
-private slots:
-  void desktopNotificationClosed(uint id, uint reason);
-  void desktopNotificationInvoked(uint id, const QString &action);
-
-  void enabledChanged(const QVariant &);
-  void useHintsChanged(const QVariant &);
-  void xHintChanged(const QVariant &);
-  void yHintChanged(const QVariant &);
-  void queueNotificationsChanged(const QVariant &);
-  void timeoutChanged(const QVariant &);
-  void useTimeoutChanged(const QVariant &);
-
-private:
-  class ConfigWidget;
-
-  org::freedesktop::Notifications *_dbusInterface;
-  bool _daemonSupportsMarkup;
-  quint32 _lastDbusId;
-  QHash<uint, uint> _idMap; ///< Maps our own notification Id to the D-Bus one
-
-  bool _enabled, _queueNotifications, _useHints;
-  int _xHint, _yHint;
-  int _timeout;
-  bool _useTimeout;
-
-};
-
-class DesktopNotificationBackend::ConfigWidget : public SettingsPage {
-  Q_OBJECT
-
-public:
-  ConfigWidget(QWidget *parent = 0);
-  void save();
-  void load();
-  bool hasDefaults() const;
-  void defaults();
-
-private slots:
-  void widgetChanged();
-
-private:
-  Ui::DesktopNotificationConfigWidget ui;
-  int xHint, yHint;
-  bool useHints, queueNotifications;
-  int timeout;
-  bool useTimeout;
-  bool enabled;
-};
-
-#endif
index 18186f2..0c6a1e3 100644 (file)
@@ -27,7 +27,8 @@
 LegacySystemTray::LegacySystemTray(QWidget *parent)
   : SystemTray(parent),
   _blinkState(false),
-  _isVisible(true)
+  _isVisible(true),
+  _lastMessageId(0)
 {
 #ifndef HAVE_KDE
   _trayIcon = new QSystemTrayIcon(associatedWidget());
@@ -42,7 +43,7 @@ LegacySystemTray::LegacySystemTray(QWidget *parent)
                      SLOT(on_activated(QSystemTrayIcon::ActivationReason)));
 #endif
   connect(_trayIcon, SIGNAL(messageClicked()),
-                     SIGNAL(messageClicked()));
+                     SLOT(on_messageClicked()));
 
   _blinkTimer.setInterval(500);
   _blinkTimer.setSingleShot(false);
@@ -131,8 +132,24 @@ void LegacySystemTray::on_activated(QSystemTrayIcon::ActivationReason reason) {
   activate((SystemTray::ActivationReason)reason);
 }
 
-void LegacySystemTray::showMessage(const QString &title, const QString &message, SystemTray::MessageIcon icon, int millisecondsTimeoutHint) {
-  _trayIcon->showMessage(title, message, (QSystemTrayIcon::MessageIcon)icon, millisecondsTimeoutHint);
+void LegacySystemTray::on_messageClicked() {
+  emit messageClicked(_lastMessageId);
+}
+
+void LegacySystemTray::showMessage(const QString &title, const QString &message, SystemTray::MessageIcon icon, int msTimeout, uint id) {
+  // fancy stuff later: show messages in order
+  // for now, we just show the last message
+  _lastMessageId = id;
+  _trayIcon->showMessage(title, message, (QSystemTrayIcon::MessageIcon)icon, msTimeout);
+}
+
+void LegacySystemTray::closeMessage(uint notificationId) {
+  Q_UNUSED(notificationId)
+
+  // there really seems to be no sane way to close the bubble... :(
+#ifdef Q_WS_X11
+  showMessage("", "", NoIcon, 1);
+#endif
 }
 
 #endif /* QT_NO_SYSTEMTRAYICON */
index 07c1b1d..c6cb91b 100644 (file)
@@ -48,7 +48,8 @@ public:
 public slots:
   virtual void setState(State state);
   virtual void setVisible(bool visible = true);
-  virtual void showMessage(const QString &title, const QString &message, MessageIcon icon = Information, int millisecondsTimeoutHint = 10000);
+  virtual void showMessage(const QString &title, const QString &message, MessageIcon icon = Information, int msTimeout = 10000, uint notificationId = 0);
+  virtual void closeMessage(uint notificationId);
 
 protected slots:
 
@@ -58,6 +59,7 @@ protected:
 private slots:
   void on_blinkTimeout();
   void on_activated(QSystemTrayIcon::ActivationReason);
+  void on_messageClicked();
 
   void syncLegacyIcon();
 
@@ -65,6 +67,7 @@ private:
   QTimer _blinkTimer;
   bool _blinkState;
   bool _isVisible;
+  uint _lastMessageId;
 
 #ifdef HAVE_KDE
   KSystemTrayIcon *_trayIcon;
index c6d4051..fe050fb 100644 (file)
@@ -85,9 +85,6 @@
 #include "verticaldock.h"
 
 #ifndef HAVE_KDE
-#  ifdef HAVE_DBUS
-#    include "desktopnotificationbackend.h"
-#  endif
 #  ifdef HAVE_PHONON
 #    include "phononnotificationbackend.h"
 #  endif
@@ -199,9 +196,6 @@ void MainWin::init() {
 #  ifdef HAVE_PHONON
   QtUi::registerNotificationBackend(new PhononNotificationBackend(this));
 #  endif
-#  ifdef HAVE_DBUS
-  QtUi::registerNotificationBackend(new DesktopNotificationBackend(this));
-#  endif
 
 #else /* HAVE_KDE */
   QtUi::registerNotificationBackend(new KNotificationBackend(this));
index 690ed9e..9d61fa7 100644 (file)
 #include "statusnotifieritem.h"
 #include "statusnotifieritemdbus.h"
 
+#include <QApplication>
 #include <QMenu>
 #include <QMouseEvent>
+#include <QTextDocument>
 
 const int StatusNotifierItem::_protocolVersion = 0;
 
 StatusNotifierItem::StatusNotifierItem(QWidget *parent)
   : StatusNotifierItemParent(parent),
   _statusNotifierItemDBus(0),
-  _statusNotifierWatcher(0)
+  _statusNotifierWatcher(0),
+  _notificationsClient(0),
+  _notificationsClientSupportsMarkup(true),
+  _lastNotificationsDBusId(0)
 {
 
 }
 
 StatusNotifierItem::~StatusNotifierItem() {
   delete _statusNotifierWatcher;
-
 }
 
 void StatusNotifierItem::init() {
@@ -57,6 +61,17 @@ void StatusNotifierItem::init() {
 
   setMode(StatusNotifier);
 
+  _notificationsClient = new org::freedesktop::Notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications",
+                                                             QDBusConnection::sessionBus(), this);
+
+  connect(_notificationsClient, SIGNAL(NotificationClosed(uint,uint)), SLOT(notificationClosed(uint,uint)));
+  connect(_notificationsClient, SIGNAL(ActionInvoked(uint,QString)), SLOT(notificationInvoked(uint,QString)));
+
+  if(_notificationsClient->isValid()) {
+    QStringList desktopCapabilities = _notificationsClient->GetCapabilities();
+    _notificationsClientSupportsMarkup = desktopCapabilities.contains("body-markup");
+  }
+
   StatusNotifierItemParent::init();
   trayMenu()->installEventFilter(this);
 }
@@ -73,7 +88,7 @@ void StatusNotifierItem::registerToDaemon() {
     _statusNotifierWatcher->RegisterStatusNotifierItem(_statusNotifierItemDBus->service());
 
   } else {
-    qDebug() << "StatusNotifierWatcher not reachable!";
+    //qDebug() << "StatusNotifierWatcher not reachable!";
     setMode(Legacy);
   }
 }
@@ -175,4 +190,44 @@ bool StatusNotifierItem::eventFilter(QObject *watched, QEvent *event) {
   return StatusNotifierItemParent::eventFilter(watched, event);
 }
 
+void StatusNotifierItem::showMessage(const QString &title, const QString &message_, SystemTray::MessageIcon icon, int timeout, uint notificationId) {
+  QString message = message_;
+  if(_notificationsClient->isValid()) {
+    if(_notificationsClientSupportsMarkup)
+      message = Qt::escape(message);
+
+    QStringList actions = QStringList() << "activate" << "View";
+
+    // we always queue notifications right now
+    QDBusReply<uint> reply = _notificationsClient->Notify(title, 0, "quassel", title, message, actions, QVariantMap(), timeout);
+    if(reply.isValid()) {
+      uint dbusid = reply.value();
+      _notificationsIdMap.insert(dbusid, notificationId);
+      _lastNotificationsDBusId = dbusid;
+    }
+  } else
+    StatusNotifierItemParent::showMessage(title, message, icon, timeout, notificationId);
+}
+
+void StatusNotifierItem::closeMessage(uint notificationId) {
+  foreach(uint dbusid, _notificationsIdMap.keys()) {
+    if(_notificationsIdMap.value(dbusid) == notificationId) {
+      _notificationsIdMap.remove(dbusid);
+      _notificationsClient->CloseNotification(dbusid);
+    }
+  }
+  _lastNotificationsDBusId = 0;
+}
+
+void StatusNotifierItem::notificationClosed(uint dbusid, uint reason) {
+  Q_UNUSED(reason)
+  _lastNotificationsDBusId = 0;
+  emit messageClosed(_notificationsIdMap.take(dbusid));
+}
+
+void StatusNotifierItem::notificationInvoked(uint dbusid, const QString &action) {
+  Q_UNUSED(action)
+  emit messageClicked(_notificationsIdMap.value(dbusid, 0));
+}
+
 #endif
index 2efbefd..3bed80f 100644 (file)
@@ -26,6 +26,7 @@
 
 #ifdef HAVE_DBUS
 
+#include "notificationsclient.h"
 #include "systemtray.h"
 #include "statusnotifierwatcher.h"
 
@@ -47,6 +48,8 @@ public:
 
 public slots:
   virtual void setState(State state);
+  virtual void showMessage(const QString &title, const QString &message, MessageIcon icon = Information, int msTimeout = 10000, uint notificationId = 0);
+  virtual void closeMessage(uint notificationId);
 
 protected:
   virtual void init();
@@ -63,6 +66,9 @@ private slots:
   void activated(const QPoint &pos);
   void serviceChange(const QString& name, const QString& oldOwner, const QString& newOwner);
 
+  void notificationClosed(uint id, uint reason);
+  void notificationInvoked(uint id, const QString &action);
+
 private:
   void registerToDaemon();
 
@@ -70,7 +76,10 @@ private:
   StatusNotifierItemDBus *_statusNotifierItemDBus;
 
   org::kde::StatusNotifierWatcher *_statusNotifierWatcher;
-  //org::freedesktop::Notifications *_notificationsClient;
+  org::freedesktop::Notifications *_notificationsClient;
+  bool _notificationsClientSupportsMarkup;
+  quint32 _lastNotificationsDBusId;
+  QHash<uint, uint> _notificationsIdMap; ///< Maps our own notification ID to the D-Bus one
 
   friend class StatusNotifierItemDBus;
 };
index 139c968..afa7964 100644 (file)
@@ -144,11 +144,12 @@ void SystemTray::setToolTip(const QString &title, const QString &subtitle) {
   emit toolTipChanged(title, subtitle);
 }
 
-void SystemTray::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint) {
+void SystemTray::showMessage(const QString &title, const QString &message, MessageIcon icon, int millisecondsTimeoutHint, uint id) {
   Q_UNUSED(title)
   Q_UNUSED(message)
   Q_UNUSED(icon)
   Q_UNUSED(millisecondsTimeoutHint)
+  Q_UNUSED(id)
 }
 
 void SystemTray::activate(SystemTray::ActivationReason reason) {
index df79ddf..35a9c17 100644 (file)
@@ -77,13 +77,15 @@ public slots:
   virtual void setState(State);
   virtual void setVisible(bool visible = true);
   virtual void setToolTip(const QString &title, const QString &subtitle);
-  virtual void showMessage(const QString &title, const QString &message, MessageIcon icon = Information, int millisecondsTimeoutHint = 10000);
+  virtual void showMessage(const QString &title, const QString &message, MessageIcon icon = Information, int msTimeout = 10000, uint notificationId = 0);
+  virtual void closeMessage(uint notificationId) { Q_UNUSED(notificationId) }
 
 signals:
   void activated(SystemTray::ActivationReason);
   void iconChanged(const Icon &);
   void toolTipChanged(const QString &title, const QString &subtitle);
-  void messageClicked();
+  void messageClicked(uint notificationId);
+  void messageClosed(uint notificationId);
 
 protected slots:
   virtual void activate(SystemTray::ActivationReason = Trigger);
@@ -98,6 +100,7 @@ protected:
   inline QString toolTipSubTitle() const;
   inline QMenu *trayMenu() const;
 
+
 private slots:
   void minimizeRestore();
   void trayMenuAboutToShow();
index 721ee07..f3bd5a0 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
-#include "systraynotificationbackend.h"
+#include <QApplication>
+#include <QCheckBox>
+#include <QGroupBox>
+#include <QVBoxLayout>
 
-#include <QtGui>
+#include "systraynotificationbackend.h"
 
 #include "client.h"
 #include "clientsettings.h"
@@ -41,7 +44,7 @@ SystrayNotificationBackend::SystrayNotificationBackend(QObject *parent)
   notificationSettings.notify("Systray/ShowBubble", this, SLOT(showBubbleChanged(const QVariant &)));
   notificationSettings.notify("Systray/Animate", this, SLOT(animateChanged(const QVariant &)));
 
-  connect(QtUi::mainWindow()->systemTray(), SIGNAL(messageClicked()), SLOT(notificationActivated()));
+  connect(QtUi::mainWindow()->systemTray(), SIGNAL(messageClicked(uint)), SLOT(notificationActivated(uint)));
   connect(QtUi::mainWindow()->systemTray(), SIGNAL(activated(SystemTray::ActivationReason)),
                                             SLOT(notificationActivated(SystemTray::ActivationReason)));
 
@@ -50,13 +53,16 @@ SystrayNotificationBackend::SystrayNotificationBackend(QObject *parent)
   updateToolTip();
 }
 
-void SystrayNotificationBackend::notify(const Notification &notification) {
-  if(notification.type != Highlight && notification.type != PrivMsg)
+void SystrayNotificationBackend::notify(const Notification &n) {
+  if(n.type != Highlight && n.type != PrivMsg)
     return;
 
-  _notifications.append(notification);
-  if(_showBubble)
-    showBubble();
+  _notifications.append(n);
+  if(_showBubble) {
+    QString title = Client::networkModel()->networkName(n.bufferId) + " - " + Client::networkModel()->bufferName(n.bufferId);
+    QString message = QString("<%1> %2").arg(n.sender, n.message);
+    QtUi::mainWindow()->systemTray()->showMessage(title, message, SystemTray::Information, 10000, n.notificationId);
+  }
 
   if(_animate)
     QtUi::mainWindow()->systemTray()->setAlert(true);
@@ -73,7 +79,7 @@ void SystrayNotificationBackend::close(uint notificationId) {
       ++i;
   }
 
-  closeBubble();
+  QtUi::mainWindow()->systemTray()->closeMessage(notificationId);
 
   if(!_notifications.count())
     QtUi::mainWindow()->systemTray()->setAlert(false);
@@ -81,30 +87,13 @@ void SystrayNotificationBackend::close(uint notificationId) {
   updateToolTip();
 }
 
-void SystrayNotificationBackend::showBubble() {
-  // fancy stuff later: show messages in order
-  // for now, we just show the last message
-  if(_notifications.isEmpty())
-    return;
-  Notification n = _notifications.last();
-  QString title = Client::networkModel()->networkName(n.bufferId) + " - " + Client::networkModel()->bufferName(n.bufferId);
-  QString message = QString("<%1> %2").arg(n.sender, n.message);
-  QtUi::mainWindow()->systemTray()->showMessage(title, message);
-}
-
-void SystrayNotificationBackend::closeBubble() {
-  // there really seems to be no sane way to close the bubble... :(
-#ifdef Q_WS_X11
-  QtUi::mainWindow()->systemTray()->showMessage("", "", SystemTray::NoIcon, 1);
-#endif
-}
-
-void SystrayNotificationBackend::notificationActivated() {
+void SystrayNotificationBackend::notificationActivated(uint notificationId) {
   if(!_blockActivation) {
     if(QtUi::mainWindow()->systemTray()->isAlerted()) {
       _blockActivation = true; // prevent double activation because both tray icon and bubble might send a signal
-      uint id = _notifications.count()? _notifications.last().notificationId : 0;
-      emit activated(id);
+      if(!notificationId)
+        notificationId = _notifications.count()? _notifications.last().notificationId : 0;
+      emit activated(notificationId);
     } else
       GraphicalUi::toggleMainWidget();
   }
@@ -112,7 +101,7 @@ void SystrayNotificationBackend::notificationActivated() {
 
 void SystrayNotificationBackend::notificationActivated(SystemTray::ActivationReason reason) {
   if(reason == SystemTray::Trigger) {
-    notificationActivated();
+    notificationActivated(0);
   }
 }
 
index 4c8ff79..3a68533 100644 (file)
@@ -41,9 +41,7 @@ protected:
   virtual bool eventFilter(QObject *obj, QEvent *event);
 
 private slots:
-  void showBubble();
-  void closeBubble();
-  void notificationActivated();
+  void notificationActivated(uint notificationId);
   void notificationActivated(SystemTray::ActivationReason);
 
   void animateChanged(const QVariant &);
diff --git a/src/qtui/ui/desktopnotificationconfigwidget.ui b/src/qtui/ui/desktopnotificationconfigwidget.ui
deleted file mode 100644 (file)
index a26b69f..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>DesktopNotificationConfigWidget</class>
- <widget class="QWidget" name="DesktopNotificationConfigWidget">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>435</width>
-    <height>144</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout_2">
-   <item>
-    <widget class="QGroupBox" name="enabled">
-     <property name="title">
-      <string>Desktop Notification (via D-Bus)</string>
-     </property>
-     <property name="checkable">
-      <bool>true</bool>
-     </property>
-     <property name="checked">
-      <bool>false</bool>
-     </property>
-     <layout class="QVBoxLayout" name="verticalLayout">
-      <item>
-       <layout class="QHBoxLayout" name="horizontalLayout_2">
-        <item>
-         <widget class="QCheckBox" name="useTimeout">
-          <property name="text">
-           <string>Timeout:</string>
-          </property>
-          <property name="checked">
-           <bool>true</bool>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QSpinBox" name="timeout">
-          <property name="specialValueText">
-           <string/>
-          </property>
-          <property name="suffix">
-           <string> s</string>
-          </property>
-          <property name="minimum">
-           <number>1</number>
-          </property>
-          <property name="value">
-           <number>10</number>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <spacer name="horizontalSpacer">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>40</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-       </layout>
-      </item>
-      <item>
-       <layout class="QHBoxLayout" name="horizontalLayout">
-        <item>
-         <widget class="QCheckBox" name="useHints">
-          <property name="text">
-           <string>Position hint:</string>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QSpinBox" name="xHint">
-          <property name="enabled">
-           <bool>false</bool>
-          </property>
-          <property name="suffix">
-           <string> px</string>
-          </property>
-          <property name="prefix">
-           <string>X: </string>
-          </property>
-          <property name="maximum">
-           <number>9999</number>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QSpinBox" name="yHint">
-          <property name="enabled">
-           <bool>false</bool>
-          </property>
-          <property name="suffix">
-           <string> px</string>
-          </property>
-          <property name="prefix">
-           <string>Y: </string>
-          </property>
-          <property name="maximum">
-           <number>9999</number>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <spacer name="horizontalSpacer_2">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>40</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-       </layout>
-      </item>
-      <item>
-       <widget class="QCheckBox" name="queueNotifications">
-        <property name="text">
-         <string>Queue unread notifications</string>
-        </property>
-        <property name="checked">
-         <bool>true</bool>
-        </property>
-       </widget>
-      </item>
-     </layout>
-    </widget>
-   </item>
-   <item>
-    <spacer name="verticalSpacer">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>40</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-  </layout>
- </widget>
- <tabstops>
-  <tabstop>enabled</tabstop>
-  <tabstop>useTimeout</tabstop>
-  <tabstop>timeout</tabstop>
-  <tabstop>useHints</tabstop>
-  <tabstop>xHint</tabstop>
-  <tabstop>yHint</tabstop>
-  <tabstop>queueNotifications</tabstop>
- </tabstops>
- <resources/>
- <connections>
-  <connection>
-   <sender>useHints</sender>
-   <signal>toggled(bool)</signal>
-   <receiver>xHint</receiver>
-   <slot>setEnabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>92</x>
-     <y>82</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>156</x>
-     <y>82</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>useHints</sender>
-   <signal>toggled(bool)</signal>
-   <receiver>yHint</receiver>
-   <slot>setEnabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>51</x>
-     <y>78</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>262</x>
-     <y>83</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>useTimeout</sender>
-   <signal>toggled(bool)</signal>
-   <receiver>timeout</receiver>
-   <slot>setEnabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>58</x>
-     <y>50</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>128</x>
-     <y>51</y>
-    </hint>
-   </hints>
-  </connection>
- </connections>
-</ui>