Refactor the system tray's context menu
authorManuel Nickschas <sputnick@quassel-irc.org>
Tue, 16 Feb 2010 20:33:28 +0000 (21:33 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Tue, 16 Feb 2010 20:33:28 +0000 (21:33 +0100)
Should now work with and without QSystemTrayIcon support in Qt.
There's still some fugly workaround for QMenu's focus issues, which will
probably require some X11 hackery at some point to do it a bit less fugly,
but maybe the DBusMenu is specced earlier...

src/qtui/legacysystemtray.cpp
src/qtui/statusnotifieritem.cpp
src/qtui/statusnotifieritemdbus.cpp
src/qtui/systemtray.cpp
src/qtui/systemtray.h
src/uisupport/graphicalui.cpp
src/uisupport/graphicalui.h

index 18bf02c..9969800 100644 (file)
@@ -43,9 +43,6 @@ LegacySystemTray::LegacySystemTray(QWidget *parent)
   connect(_trayIcon, SIGNAL(messageClicked()),
                      SIGNAL(messageClicked()));
 
-  setTrayMenu(_trayIcon->contextMenu());
-  _trayIcon->setContextMenu(trayMenu());
-
   _blinkTimer.setInterval(500);
   _blinkTimer.setSingleShot(false);
   connect(&_blinkTimer, SIGNAL(timeout()), SLOT(on_blinkTimeout()));
@@ -56,6 +53,8 @@ void LegacySystemTray::init() {
     setMode(Legacy);
 
   SystemTray::init();
+
+  _trayIcon->setContextMenu(trayMenu());
 }
 
 void LegacySystemTray::syncLegacyIcon() {
index da15475..4df13dc 100644 (file)
@@ -45,13 +45,6 @@ StatusNotifierItem::~StatusNotifierItem() {
 }
 
 void StatusNotifierItem::init() {
-// workaround until we handle the tray menu more sanely
-#ifdef QT_NO_SYSTEMTRAYICON
-  setTrayMenu(new QMenu(associatedWidget()));
-#endif
-
-  trayMenu()->installEventFilter(this);
-
   qDBusRegisterMetaType<DBusImageStruct>();
   qDBusRegisterMetaType<DBusImageVector>();
   qDBusRegisterMetaType<DBusToolTipStruct>();
@@ -64,6 +57,7 @@ void StatusNotifierItem::init() {
   setMode(StatusNotifier);
 
   StatusNotifierItemParent::init();
+  trayMenu()->installEventFilter(this);
 }
 
 void StatusNotifierItem::registerToDaemon() {
@@ -165,11 +159,17 @@ void StatusNotifierItem::activated(const QPoint &pos) {
 bool StatusNotifierItem::eventFilter(QObject *watched, QEvent *event) {
   if(mode() == StatusNotifier) {
     //FIXME: ugly ugly workaround to weird QMenu's focus problems
+#ifdef HAVE_KDE
     if(watched == trayMenu() &&
        (event->type() == QEvent::WindowDeactivate || (event->type() == QEvent::MouseButtonRelease && static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton))) {
       // put at the back of event queue to let the action activate anyways
       QTimer::singleShot(0, trayMenu(), SLOT(hide()));
     }
+#else
+    if(watched == trayMenu() && event->type() == QEvent::HoverLeave) {
+      trayMenu()->hide();
+    }
+#endif
   }
   return StatusNotifierItemParent::eventFilter(watched, event);
 }
index db93d5c..9c0487b 100644 (file)
@@ -254,7 +254,9 @@ void StatusNotifierItemDBus::ContextMenu(int x, int y)
 
     //TODO: nicer placement, possible?
     if (!m_statusNotifierItem->trayMenu()->isVisible()) {
+#ifdef HAVE_KDE
         m_statusNotifierItem->trayMenu()->setWindowFlags(Qt::Window|Qt::FramelessWindowHint);
+#endif
         m_statusNotifierItem->trayMenu()->popup(QPoint(x,y));
 #ifdef HAVE_KDE
         KWindowSystem::setState(m_statusNotifierItem->trayMenu()->winId(), NET::SkipTaskbar|NET::SkipPager|NET::KeepAbove);
index 6b2b33c..139c968 100644 (file)
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
+#include <QApplication>
 #include <QMenu>
 
 #include "systemtray.h"
 
+#include "action.h"
 #include "actioncollection.h"
 #include "client.h"
 #include "iconloader.h"
 #include "qtui.h"
 
 #ifdef HAVE_KDE
+#  include <KMenu>
 #  include <KWindowInfo>
 #  include <KWindowSystem>
 #endif
@@ -52,31 +55,53 @@ QWidget *SystemTray::associatedWidget() const {
   return _associatedWidget;
 }
 
-void SystemTray::setTrayMenu(QMenu *menu) {
-  if(menu)
-    _trayMenu = menu;
-  else
-    _trayMenu = new QMenu();
-
+void SystemTray::init() {
   ActionCollection *coll = QtUi::actionCollection("General");
+  _minimizeRestoreAction = new Action(tr("&Minimize"), this, this, SLOT(minimizeRestore()));
+
+#ifdef HAVE_KDE
+  KMenu *kmenu;
+  _trayMenu = kmenu = new KMenu();
+  kmenu->addTitle(qApp->windowIcon(), "Quassel IRC");
+#else
+  _trayMenu = new QMenu(associatedWidget());
+#endif
+
+  _trayMenu->setTitle("Quassel IRC");
+
+#ifndef HAVE_KDE
+  _trayMenu->setAttribute(Qt::WA_Hover);
+#endif
 
   _trayMenu->addAction(coll->action("ConnectCore"));
   _trayMenu->addAction(coll->action("DisconnectCore"));
   _trayMenu->addAction(coll->action("CoreInfo"));
-#ifndef HAVE_KDE
   _trayMenu->addSeparator();
+  _trayMenu->addAction(_minimizeRestoreAction);
   _trayMenu->addAction(coll->action("Quit"));
-#endif /* HAVE_KDE */
+
+  connect(_trayMenu, SIGNAL(aboutToShow()), SLOT(trayMenuAboutToShow()));
+}
+
+void SystemTray::trayMenuAboutToShow() {
+  if(GraphicalUi::isMainWidgetVisible())
+    _minimizeRestoreAction->setText(tr("&Minimize"));
+  else
+    _minimizeRestoreAction->setText(tr("&Restore"));
 }
 
 void SystemTray::setMode(Mode mode_) {
   if(mode_ != _mode) {
     _mode = mode_;
-    if(_mode == Legacy) {
-      _trayMenu->setWindowFlags(Qt::Popup);
-    } else {
-      _trayMenu->setWindowFlags(Qt::Window);
+#ifdef HAVE_KDE
+    if(_trayMenu) {
+      if(_mode == Legacy) {
+        _trayMenu->setWindowFlags(Qt::Popup);
+      } else {
+        _trayMenu->setWindowFlags(Qt::Window);
+      }
     }
+#endif
   }
 }
 
@@ -129,3 +154,7 @@ void SystemTray::showMessage(const QString &title, const QString &message, Messa
 void SystemTray::activate(SystemTray::ActivationReason reason) {
   emit activated(reason);
 }
+
+void SystemTray::minimizeRestore() {
+  GraphicalUi::toggleMainWidget();
+}
index dc31edc..df79ddf 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "icon.h"
 
+class Action;
 class QMenu;
 
 class SystemTray : public QObject {
@@ -61,7 +62,7 @@ public:
 
   explicit SystemTray(QWidget *parent);
   virtual ~SystemTray();
-  virtual void init() {}
+  virtual void init();
 
   inline State state() const;
   inline bool isAlerted() const;
@@ -96,9 +97,10 @@ protected:
   inline QString toolTipTitle() const;
   inline QString toolTipSubTitle() const;
   inline QMenu *trayMenu() const;
-  void setTrayMenu(QMenu *);
 
 private slots:
+  void minimizeRestore();
+  void trayMenuAboutToShow();
 
 private:
   Mode _mode;
@@ -109,6 +111,7 @@ private:
 
   QMenu *_trayMenu;
   QWidget *_associatedWidget;
+  Action *_minimizeRestoreAction;
 };
 
 // inlines
index 2d1943f..8e5fe94 100644 (file)
@@ -101,9 +101,13 @@ bool GraphicalUi::checkMainWidgetVisibility(bool perform) {
   // therefore we watch for activation event and use our stopwatch :)
   if(GetTickCount() - _dwTickCount < 300) {
     // we were active in the last 300ms -> hide it
-    minimizeRestore(false);
+    if(perform)
+      minimizeRestore(false);
+    return false;
   } else {
-    minimizeRestore(true);
+    if(perform)
+      minimizeRestore(true);
+    return true;
   }
 
 #elif defined(HAVE_KDE) && defined(Q_WS_X11)
@@ -182,6 +186,10 @@ bool GraphicalUi::checkMainWidgetVisibility(bool perform) {
   return true;
 }
 
+bool GraphicalUi::isMainWidgetVisible() {
+  return !instance()->checkMainWidgetVisibility(false);
+}
+
 void GraphicalUi::minimizeRestore(bool show) {
   if(show)
     activateMainWidget();
index 88d94fe..6d6306e 100644 (file)
@@ -61,6 +61,9 @@ public:
   //! Toggle main widget
   static void toggleMainWidget();
 
+  //! Check if the main widget if (fully, in KDE) visible
+  static bool isMainWidgetVisible();
+
 protected:
   //! This is the widget we associate global actions with, typically the main window
   void setMainWidget(QWidget *);
@@ -70,7 +73,7 @@ protected:
    *  it should be activated or hidden. Without KDE, we need to resort to checking the current state
    *  as Qt knows it, ignoring windows covering it.
    *  @param  performToggle If true, toggle the window's state in addition to checking visibility
-   *  @return True, if the window is currently visible
+   *  @return True, if the window is currently *not* visible (needs activation)
    */
   bool checkMainWidgetVisibility(bool performToggle);