Add SystrayNotificationBackend (untested yet and still somewhat rudimentary), evolve...
authorManuel Nickschas <sputnick@quassel-irc.org>
Tue, 7 Oct 2008 22:37:57 +0000 (00:37 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sun, 12 Oct 2008 18:38:43 +0000 (20:38 +0200)
src/client/clientsettings.h
src/qtui/CMakeLists.txt
src/qtui/mainwin.cpp
src/qtui/mainwin.h
src/qtui/qtui.cpp
src/qtui/qtui.h
src/qtui/systraynotificationbackend.cpp [new file with mode: 0644]
src/qtui/systraynotificationbackend.h [new file with mode: 0644]
src/uisupport/abstractnotificationbackend.h

index da1fc88..6c7c6db 100644 (file)
@@ -78,7 +78,11 @@ class NotificationSettings : public ClientSettings {
     };
 
     NotificationSettings();
-
+    
+    inline void setValue(const QString &key, const QVariant &data) { setLocalValue(key, data); }
+    inline QVariant value(const QString &key, const QVariant &def = QVariant()) { return localValue(key, def); }
+    inline void remove(const QString &key) { removeLocalKey(key); }
+      
     void setHighlightList(const QVariantList &highlightList);
     QVariantList highlightList();
 
index c02fd2e..d51224b 100644 (file)
@@ -38,6 +38,7 @@ set(SOURCES
     sessionsettings.cpp
     settingsdlg.cpp
     settingspagedlg.cpp
+    systraynotificationbackend.cpp
     titlesetter.cpp
     topiclabel.cpp
     topicwidget.cpp
@@ -70,6 +71,7 @@ set(MOC_HDRS
     qtuimessageprocessor.h
     settingsdlg.h
     settingspagedlg.h
+    systraynotificationbackend.h
     titlesetter.h
     topiclabel.h
     topicwidget.h
index fc18314..ceedb9b 100644 (file)
@@ -78,7 +78,7 @@ MainWin::MainWin(QWidget *parent)
     msgProcessorStatusWidget(new MsgProcessorStatusWidget()),
 
     _titleSetter(this),
-    systray(new QSystemTrayIcon(this)),
+    _trayIcon(new QSystemTrayIcon(this)),
 
     activeTrayIcon(DesktopIcon("quassel_newmessage", IconLoader::SizeEnormous)),
     onlineTrayIcon(DesktopIcon("quassel", IconLoader::SizeEnormous)),
@@ -97,7 +97,7 @@ MainWin::MainWin(QWidget *parent)
   setWindowTitle("Quassel IRC");
   setWindowIcon(offlineTrayIcon);
   qApp->setWindowIcon(offlineTrayIcon);
-  systray->setIcon(offlineTrayIcon);
+  systemTrayIcon()->setIcon(offlineTrayIcon);
   setWindowIconText("Quassel IRC");
 
   QtUi::actionCollection()->addAssociatedWidget(this);
@@ -382,15 +382,15 @@ void MainWin::setupSystray() {
   systrayMenu->addSeparator();
   systrayMenu->addAction(ui.actionQuit);
 
-  systray->setContextMenu(systrayMenu);
+  systemTrayIcon()->setContextMenu(systrayMenu);
 
   UiSettings s;
   if(s.value("UseSystemTrayIcon", QVariant(true)).toBool()) {
-    systray->show();
+    systemTrayIcon()->show();
   }
 
 #ifndef Q_WS_MAC
-  connect(systray, SIGNAL(activated( QSystemTrayIcon::ActivationReason )),
+  connect(systemTrayIcon(), SIGNAL(activated( QSystemTrayIcon::ActivationReason )),
           this, SLOT(systrayActivated( QSystemTrayIcon::ActivationReason )));
 #endif
 
@@ -428,7 +428,7 @@ void MainWin::setConnectedState() {
   statusBar()->showMessage(tr("Connected to core."));
   setWindowIcon(onlineTrayIcon);
   qApp->setWindowIcon(onlineTrayIcon);
-  systray->setIcon(onlineTrayIcon);
+  systemTrayIcon()->setIcon(onlineTrayIcon);
   if(sslLabel->width() == 0)
     sslLabel->setPixmap(SmallIcon("security-low"));
 }
@@ -486,7 +486,7 @@ void MainWin::setDisconnectedState() {
   statusBar()->showMessage(tr("Not connected to core."));
   setWindowIcon(offlineTrayIcon);
   qApp->setWindowIcon(offlineTrayIcon);
-  systray->setIcon(offlineTrayIcon);
+  systemTrayIcon()->setIcon(offlineTrayIcon);
   sslLabel->setPixmap(QPixmap());
 }
 
@@ -567,11 +567,11 @@ void MainWin::toggleVisibility() {
     // setFocus(); //Qt::ActiveWindowFocusReason
 
   } else {
-    if(systray->isSystemTrayAvailable ()) {
+    if(systemTrayIcon()->isSystemTrayAvailable ()) {
       clearFocus();
       hide();
-      if(!systray->isVisible()) {
-        systray->show();
+      if(!systemTrayIcon()->isVisible()) {
+        systemTrayIcon()->show();
       }
     } else {
       lower();
@@ -685,7 +685,7 @@ void MainWin::desktopNotificationInvoked(uint id, const QString & action) {
 #endif /* HAVE_DBUS */
 
 void MainWin::displayTrayIconMessage(const QString &title, const QString &message) {
-  systray->showMessage(title, message);
+  systemTrayIcon()->showMessage(title, message);
 }
 
 void MainWin::setTrayIconActivity(bool active) {
@@ -694,16 +694,16 @@ void MainWin::setTrayIconActivity(bool active) {
       timer->start(500);
   } else {
     timer->stop();
-    systray->setIcon(onlineTrayIcon);
+    systemTrayIcon()->setIcon(onlineTrayIcon);
   }
 }
 
 void MainWin::makeTrayIconBlink() {
   if(trayIconActive) {
-    systray->setIcon(onlineTrayIcon);
+    systemTrayIcon()->setIcon(onlineTrayIcon);
     trayIconActive = false;
   } else {
-    systray->setIcon(activeTrayIcon);
+    systemTrayIcon()->setIcon(activeTrayIcon);
     trayIconActive = true;
   }
 }
index 6a41a10..fab52f3 100644 (file)
@@ -37,6 +37,7 @@ class BufferViewConfig;
 class MsgProcessorStatusWidget;
 class Message;
 class NickListWidget;
+class SystemTrayIcon;
 
 #ifdef HAVE_DBUS
 #  include "desktopnotifications.h"
@@ -55,6 +56,7 @@ class MainWin : public QMainWindow {
     void addBufferView(BufferViewConfig *config = 0);
 
     void displayTrayIconMessage(const QString &title, const QString &message);
+    inline QSystemTrayIcon *systemTrayIcon() const;
 
 #ifdef HAVE_DBUS
     void sendDesktopNotification(const QString &title, const QString &message);
@@ -77,7 +79,7 @@ class MainWin : public QMainWindow {
     void securedConnection();
     void disconnectedFromCore();
     void setDisconnectedState();
-    void systrayActivated( QSystemTrayIcon::ActivationReason );
+    void systrayActivated(QSystemTrayIcon::ActivationReason);
 
   private slots:
     void addBufferView(int bufferViewConfigId);
@@ -137,7 +139,7 @@ class MainWin : public QMainWindow {
     void toggleVisibility();
     void enableMenus();
 
-    QSystemTrayIcon *systray;
+    QSystemTrayIcon *_trayIcon;
     QPixmap activeTrayIcon;
     QPixmap onlineTrayIcon;
     QPixmap offlineTrayIcon;
@@ -160,4 +162,8 @@ class MainWin : public QMainWindow {
     friend class QtUi;
 };
 
+QSystemTrayIcon *MainWin::systemTrayIcon() const {
+  return _trayIcon;
+}
+
 #endif
index 1459760..3072767 100644 (file)
@@ -31,7 +31,9 @@
 #include "util.h"
 
 ActionCollection *QtUi::_actionCollection = 0;
+MainWin *QtUi::_mainWin = 0;
 QSet<AbstractNotificationBackend *> QtUi::_notificationBackends;
+QList<AbstractNotificationBackend::Notification> QtUi::_notifications;
 QtUiStyle *QtUi::_style = 0;
 
 QtUi::QtUi() : AbstractUi() {
@@ -44,21 +46,21 @@ QtUi::QtUi() : AbstractUi() {
   UiSettings uiSettings;
   loadTranslation(uiSettings.value("Locale", QLocale::system()).value<QLocale>());
 
-  mainWin = new MainWin();
+  _mainWin = new MainWin();
   _style = new QtUiStyle;
 
-  connect(mainWin, SIGNAL(connectToCore(const QVariantMap &)), this, SIGNAL(connectToCore(const QVariantMap &)));
-  connect(mainWin, SIGNAL(disconnectFromCore()), this, SIGNAL(disconnectFromCore()));
+  connect(_mainWin, SIGNAL(connectToCore(const QVariantMap &)), this, SIGNAL(connectToCore(const QVariantMap &)));
+  connect(_mainWin, SIGNAL(disconnectFromCore()), this, SIGNAL(disconnectFromCore()));
 }
 
 QtUi::~QtUi() {
   unregisterAllNotificationBackends();
   delete _style;
-  delete mainWin;
+  delete _mainWin;
 }
 
 void QtUi::init() {
-  mainWin->init();
+  _mainWin->init();
 }
 
 MessageModel *QtUi::createMessageModel(QObject *parent) {
@@ -70,11 +72,11 @@ AbstractMessageProcessor *QtUi::createMessageProcessor(QObject *parent) {
 }
 
 void QtUi::connectedToCore() {
-  mainWin->connectedToCore();
+  _mainWin->connectedToCore();
 }
 
 void QtUi::disconnectedFromCore() {
-  mainWin->disconnectedFromCore();
+  _mainWin->disconnectedFromCore();
 }
 
 void QtUi::registerNotificationBackend(AbstractNotificationBackend *backend) {
@@ -91,7 +93,46 @@ void QtUi::unregisterAllNotificationBackends() {
   _notificationBackends.clear();
 }
 
-void QtUi::notify(BufferId id, const QString &sender, const QString &text) {
+const QSet<AbstractNotificationBackend *> &QtUi::notificationBackends() {
+  return _notificationBackends;
+}
+
+uint QtUi::invokeNotification(BufferId bufId, const QString &sender, const QString &text) {
+  static int notificationId = 0;
+  //notificationId++;
+  AbstractNotificationBackend::Notification notification(++notificationId, bufId, sender, text);
+  _notifications.append(notification);
   foreach(AbstractNotificationBackend *backend, _notificationBackends)
-    backend->notify(id, sender, text);
+    backend->notify(notification);
+  return notificationId;
+}
+
+void QtUi::closeNotification(uint notificationId) {
+  QList<AbstractNotificationBackend::Notification>::iterator i = _notifications.begin();
+  while(i != _notifications.end()) {
+    if((*i).notificationId == notificationId) {
+      foreach(AbstractNotificationBackend *backend, _notificationBackends)
+        backend->close(notificationId);
+      _notifications.erase(i);
+      break;
+    }
+    ++i;
+  }
 }
+
+void QtUi::closeNotifications(BufferId bufferId) {
+  QList<AbstractNotificationBackend::Notification>::iterator i = _notifications.begin();
+  while(i != _notifications.end()) {
+    if((*i).bufferId == bufferId) {
+      foreach(AbstractNotificationBackend *backend, _notificationBackends)
+        backend->close((*i).notificationId);
+      _notifications.erase(i);
+    }
+    ++i;
+  }
+}
+
+const QList<AbstractNotificationBackend::Notification> &QtUi::activeNotifications() {
+  return _notifications;
+}
+
index 33eb1b8..d170ae3 100644 (file)
@@ -23,7 +23,8 @@
 
 #include "quasselui.h"
 
-class AbstractNotificationBackend;
+#include "abstractnotificationbackend.h"
+
 class ActionCollection;
 class MainWin;
 class MessageModel;
@@ -45,6 +46,7 @@ public:
   AbstractMessageProcessor *createMessageProcessor(QObject *parent);
 
   inline static QtUiStyle *style();
+  inline static MainWin *mainWindow();
 
   //! Access the global ActionCollection.
   /** This ActionCollection is associated with the main window, i.e. it contains global
@@ -53,10 +55,16 @@ public:
    */
   inline static ActionCollection *actionCollection();
 
+  /* Notifications */
+
   static void registerNotificationBackend(AbstractNotificationBackend *);
   static void unregisterNotificationBackend(AbstractNotificationBackend *);
   static void unregisterAllNotificationBackends();
-  static void notify(BufferId bufId, const QString &sender, const QString &text);
+  static const QSet<AbstractNotificationBackend *> &notificationBackends();
+  static uint invokeNotification(BufferId bufId, const QString &sender, const QString &text);
+  static void closeNotification(uint notificationId);
+  static void closeNotifications(BufferId bufferId);
+  static const QList<AbstractNotificationBackend::Notification> &activeNotifications();
 
 public slots:
   void init();
@@ -66,13 +74,15 @@ protected slots:
   void disconnectedFromCore();
 
 private:
-  MainWin *mainWin;
+  static MainWin *_mainWin;
   static ActionCollection *_actionCollection;
   static QtUiStyle *_style;
   static QSet<AbstractNotificationBackend *> _notificationBackends;
+  static QList<AbstractNotificationBackend::Notification> _notifications;
 };
 
 ActionCollection *QtUi::actionCollection() { return _actionCollection; }
 QtUiStyle *QtUi::style() { return _style; }
+MainWin *QtUi::mainWindow() { return _mainWin; }
 
 #endif
diff --git a/src/qtui/systraynotificationbackend.cpp b/src/qtui/systraynotificationbackend.cpp
new file mode 100644 (file)
index 0000000..1c8b572
--- /dev/null
@@ -0,0 +1,167 @@
+/***************************************************************************
+*   Copyright (C) 2005-08 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 "systraynotificationbackend.h"
+
+#include <QtGui>
+
+#include "client.h"
+#include "clientsettings.h"
+#include "icon.h"
+#include "mainwin.h"
+#include "networkmodel.h"
+#include "qtui.h"
+
+SystrayNotificationBackend::SystrayNotificationBackend(QObject *parent) : AbstractNotificationBackend(parent) {
+  NotificationSettings notificationSettings;
+  _showBubble = notificationSettings.value("Systray/ShowBubble", true).toBool();
+  _animate = notificationSettings.value("Systray/Animate", true).toBool();
+
+  notificationSettings.notify("Systray/ShowBubble", this, SLOT(showBubbleChanged(const QVariant &)));
+  notificationSettings.notify("Systray/Animate", this, SLOT(animateChanged(const QVariant &)));
+
+  _configWidget = new ConfigWidget();
+  _iconActive = false;
+  connect(&_animationTimer, SIGNAL(timeout()), SLOT(blink()));
+}
+
+SystrayNotificationBackend::~SystrayNotificationBackend() {
+  delete _configWidget;
+}
+
+void SystrayNotificationBackend::notify(const Notification &notification) {
+  /* fancy stuff to be implemented later: show notifications in order
+  _notifications.append(notification);
+  if(_showBubble && _notifications.count() == 1) {
+    showBubble();
+  }
+  */
+  _notifications.clear();
+  _notifications.append(notification);
+  if(_showBubble) {
+    showBubble();
+  }
+  if(_animate && _notifications.count() == 1) {
+    startAnimation();
+  }
+}
+
+void SystrayNotificationBackend::close(uint notificationId) {
+  Q_UNUSED(notificationId);
+  /* fancy stuff to be implemented later
+  int idx = _notifications.indexOf(notificationId);
+
+  if(_notifications.isEmpty()) {
+  */
+  _notifications.clear();
+  closeBubble();
+  stopAnimation();
+}
+
+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.takeLast();
+  QString title = Client::networkModel()->networkName(n.bufferId) + " - " + Client::networkModel()->bufferName(n.bufferId);
+  QString message = QString("<%1> %2").arg(n.sender, n.message);
+  QtUi::mainWindow()->systemTrayIcon()->showMessage(title, message);
+}
+
+void SystrayNotificationBackend::closeBubble() {
+  // there really seems to be no decent way to close the bubble...
+  QtUi::mainWindow()->systemTrayIcon()->showMessage("", "", QSystemTrayIcon::NoIcon, 1);
+}
+
+void SystrayNotificationBackend::showBubbleChanged(const QVariant &v) {
+  _showBubble = v.toBool();
+}
+
+void SystrayNotificationBackend::startAnimation() {
+  if(!_animationTimer.isActive())
+    _animationTimer.start(500);
+}
+
+void SystrayNotificationBackend::stopAnimation() {
+  _animationTimer.stop();
+  QtUi::mainWindow()->systemTrayIcon()->setIcon(Icon("quassel"));
+  _iconActive = false;
+}
+
+void SystrayNotificationBackend::blink() {
+  QtUi::mainWindow()->systemTrayIcon()->setIcon(_iconActive ? Icon("quassel") : Icon("quassel_newmessage"));
+  _iconActive = !_iconActive;
+}
+
+void SystrayNotificationBackend::animateChanged(const QVariant &v) {
+  _animate = v.toBool();
+}
+
+SettingsPage *SystrayNotificationBackend::configWidget() const {
+  return _configWidget;
+}
+
+/***************************************************************************/
+
+SystrayNotificationBackend::ConfigWidget::ConfigWidget(QWidget *parent) : SettingsPage("Internal", "SystrayNotification", parent) {
+  QGroupBox *groupBox = new QGroupBox(tr("System Tray Icon"), this);
+  _animateBox = new QCheckBox(tr("Animate"));
+  connect(_animateBox, SIGNAL(toggled(bool)), this, SLOT(widgetChanged()));
+  _showBubbleBox = new QCheckBox(tr("Show bubble"));
+  connect(_showBubbleBox, SIGNAL(toggled(bool)), this, SLOT(widgetChanged()));
+  QVBoxLayout *layout = new QVBoxLayout(groupBox);
+  layout->addWidget(_animateBox);
+  layout->addWidget(_showBubbleBox);
+  layout->addStretch(1);
+  QVBoxLayout *globalLayout = new QVBoxLayout(this);
+  globalLayout->addWidget(groupBox);
+
+}
+
+void SystrayNotificationBackend::ConfigWidget::widgetChanged() {
+  bool changed = (_showBubble != _showBubbleBox->isChecked() || _animate != _animateBox->isChecked());
+  if(changed != hasChanged()) setChangedState(changed);
+}
+
+bool SystrayNotificationBackend::ConfigWidget::hasDefaults() const {
+  return true;
+}
+
+void SystrayNotificationBackend::ConfigWidget::defaults() {
+  _animateBox->setChecked(true);
+  _showBubbleBox->setChecked(false);
+  widgetChanged();
+}
+
+void SystrayNotificationBackend::ConfigWidget::load() {
+  NotificationSettings s;
+  _animate = s.value("Systray/Animate", true).toBool();
+  _showBubble = s.value("Systray/ShowBubble", false).toBool();
+  _animateBox->setChecked(_animate);
+  _showBubbleBox->setChecked(_showBubble);
+  setChangedState(false);
+}
+
+void SystrayNotificationBackend::ConfigWidget::save() {
+  NotificationSettings s;
+  s.setValue("Systray/Animate", _animateBox->isChecked());
+  s.setValue("Systray/ShowBubble", _showBubbleBox->isChecked());
+  load();
+}
diff --git a/src/qtui/systraynotificationbackend.h b/src/qtui/systraynotificationbackend.h
new file mode 100644 (file)
index 0000000..dc5fbb5
--- /dev/null
@@ -0,0 +1,80 @@
+/***************************************************************************
+*   Copyright (C) 2005-08 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 SYSTRAYNOTIFICATIONBACKEND_H_
+#define SYSTRAYNOTIFICATIONBACKEND_H_
+
+#include "abstractnotificationbackend.h"
+
+#include "settingspage.h"
+
+class QCheckBox;
+
+class SystrayNotificationBackend : public AbstractNotificationBackend {
+  Q_OBJECT
+
+public:
+  SystrayNotificationBackend(QObject *parent = 0);
+  ~SystrayNotificationBackend();
+
+  void notify(const Notification &);
+  void close(uint notificationId);
+  SettingsPage *configWidget() const;
+
+private slots:
+  void showBubble();
+  void closeBubble();
+  void startAnimation();
+  void stopAnimation();
+  void blink();
+
+  void animateChanged(const QVariant &);
+  void showBubbleChanged(const QVariant &);
+
+private:
+  class ConfigWidget;
+
+  SettingsPage *_configWidget;
+  bool _showBubble;
+  bool _animate;
+  bool _iconActive;
+  QTimer _animationTimer;
+  QList<Notification> _notifications;
+};
+
+class SystrayNotificationBackend::ConfigWidget : public SettingsPage {
+  Q_OBJECT
+
+public:
+  ConfigWidget(QWidget *parent = 0);
+  void save();
+  void load();
+  bool hasDefaults() const;
+  void defaults();
+
+private slots:
+  void widgetChanged();
+
+private:
+  QCheckBox *_showBubbleBox, *_animateBox;
+  bool _showBubble, _animate;
+};
+
+#endif
index 22c7600..c7112af 100644 (file)
@@ -32,10 +32,21 @@ class AbstractNotificationBackend : public QObject {
   Q_OBJECT
 
   public:
-    //AbstractNotificationBackend(QObject *parent);
-    //virtual ~AbstractNotificationBackend();
+    struct Notification {
+      uint notificationId;
+      BufferId bufferId;
+      QString sender;
+      QString message;
 
-    virtual void notify(BufferId bufId, const QString &sender, const QString &text) = 0;
+      Notification(uint id_, BufferId buf_, const QString &sender_, const QString &msg_)
+      : notificationId(id_), bufferId(buf_), sender(sender_), message(msg_) {};
+    };
+
+    inline AbstractNotificationBackend(QObject *parent) : QObject(parent) {};
+    virtual ~AbstractNotificationBackend() {};
+
+    virtual void notify(const Notification &) = 0;
+    virtual void close(uint notificationId) { Q_UNUSED(notificationId); }
     virtual SettingsPage *configWidget() const = 0;
 
 };