From 5c78a50fa720e5f82fcaa03c0176feab71d74c8e Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Wed, 8 Oct 2008 00:37:57 +0200 Subject: [PATCH] Add SystrayNotificationBackend (untested yet and still somewhat rudimentary), evolve API in QtUi --- src/client/clientsettings.h | 6 +- src/qtui/CMakeLists.txt | 2 + src/qtui/mainwin.cpp | 28 ++-- src/qtui/mainwin.h | 10 +- src/qtui/qtui.cpp | 59 +++++-- src/qtui/qtui.h | 16 +- src/qtui/systraynotificationbackend.cpp | 167 ++++++++++++++++++++ src/qtui/systraynotificationbackend.h | 80 ++++++++++ src/uisupport/abstractnotificationbackend.h | 17 +- 9 files changed, 353 insertions(+), 32 deletions(-) create mode 100644 src/qtui/systraynotificationbackend.cpp create mode 100644 src/qtui/systraynotificationbackend.h diff --git a/src/client/clientsettings.h b/src/client/clientsettings.h index da1fc880..6c7c6db1 100644 --- a/src/client/clientsettings.h +++ b/src/client/clientsettings.h @@ -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(); diff --git a/src/qtui/CMakeLists.txt b/src/qtui/CMakeLists.txt index c02fd2e7..d51224bc 100644 --- a/src/qtui/CMakeLists.txt +++ b/src/qtui/CMakeLists.txt @@ -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 diff --git a/src/qtui/mainwin.cpp b/src/qtui/mainwin.cpp index fc183148..ceedb9b1 100644 --- a/src/qtui/mainwin.cpp +++ b/src/qtui/mainwin.cpp @@ -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; } } diff --git a/src/qtui/mainwin.h b/src/qtui/mainwin.h index 6a41a107..fab52f38 100644 --- a/src/qtui/mainwin.h +++ b/src/qtui/mainwin.h @@ -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 diff --git a/src/qtui/qtui.cpp b/src/qtui/qtui.cpp index 14597604..30727676 100644 --- a/src/qtui/qtui.cpp +++ b/src/qtui/qtui.cpp @@ -31,7 +31,9 @@ #include "util.h" ActionCollection *QtUi::_actionCollection = 0; +MainWin *QtUi::_mainWin = 0; QSet QtUi::_notificationBackends; +QList 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()); - 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 &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::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::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 &QtUi::activeNotifications() { + return _notifications; +} + diff --git a/src/qtui/qtui.h b/src/qtui/qtui.h index 33eb1b87..d170ae3d 100644 --- a/src/qtui/qtui.h +++ b/src/qtui/qtui.h @@ -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 ¬ificationBackends(); + static uint invokeNotification(BufferId bufId, const QString &sender, const QString &text); + static void closeNotification(uint notificationId); + static void closeNotifications(BufferId bufferId); + static const QList &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 _notificationBackends; + static QList _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 index 00000000..1c8b5725 --- /dev/null +++ b/src/qtui/systraynotificationbackend.cpp @@ -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 + +#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 ¬ification) { + /* 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 index 00000000..dc5fbb5e --- /dev/null +++ b/src/qtui/systraynotificationbackend.h @@ -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 _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 diff --git a/src/uisupport/abstractnotificationbackend.h b/src/uisupport/abstractnotificationbackend.h index 22c76003..c7112afa 100644 --- a/src/uisupport/abstractnotificationbackend.h +++ b/src/uisupport/abstractnotificationbackend.h @@ -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; }; -- 2.20.1