icons: Fix icon theme support for StatusNotifierItem
[quassel.git] / src / qtui / statusnotifieritem.cpp
index e96cd4e..e23b7d8 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2013 by the Quassel Project                        *
+ *   Copyright (C) 2005-2018 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This contains code from KStatusNotifierItem, part of the KDE libs     *
 #ifdef HAVE_DBUS
 
 #include <QApplication>
+#include <QDir>
 #include <QMenu>
 #include <QMouseEvent>
 #include <QTextDocument>
 
+#include "qtui.h"
 #include "quassel.h"
 #include "statusnotifieritem.h"
 #include "statusnotifieritemdbus.h"
@@ -51,29 +53,31 @@ public:
 protected:
     virtual QString iconNameForAction(QAction *action) // TODO Qt 4.7: fixme when we have converted our iconloader
     {
-        Icon icon(action->icon());
-#if QT_VERSION >= 0x040701
-        // QIcon::name() is in the 4.7 git branch, but it is not in 4.7 TP.
-        // If you get a build error here, you need to update your pre-release
-        // of Qt 4.7.
+        QIcon icon(action->icon());
         return icon.isNull() ? QString() : icon.name();
-#else
-        return QString();
-#endif
     }
 };
 
-
 #endif /* HAVE_DBUSMENU */
 
 StatusNotifierItem::StatusNotifierItem(QWidget *parent)
-    : StatusNotifierItemParent(parent),
-    _statusNotifierItemDBus(0),
-    _statusNotifierWatcher(0),
-    _notificationsClient(0),
-    _notificationsClientSupportsMarkup(true),
-    _lastNotificationsDBusId(0)
+    : StatusNotifierItemParent(parent)
+#if QT_VERSION >= 0x050000
+    , _iconThemeDir{QDir::tempPath() + QLatin1String{"/quassel-sni-XXXXXX"}}
+#endif
 {
+    // Create a temporary directory that holds copies of the tray icons. That way, visualizers can find our icons.
+    // For Qt4 the relevant icons are installed in hicolor already, so nothing to be done.
+#if QT_VERSION >= 0x050000
+    if (_iconThemeDir.isValid()) {
+        _iconThemePath = _iconThemeDir.path();
+    }
+    else {
+        qWarning() << StatusNotifierItem::tr("Could not create temporary directory for themed tray icons: %1").arg(_iconThemeDir.errorString());
+    }
+#endif
+
+    connect(QtUi::instance(), SIGNAL(iconThemeRefreshed()), this, SLOT(refreshIcons()));
 }
 
 
@@ -89,6 +93,8 @@ void StatusNotifierItem::init()
     qDBusRegisterMetaType<DBusImageVector>();
     qDBusRegisterMetaType<DBusToolTipStruct>();
 
+    refreshIcons();
+
     _statusNotifierItemDBus = new StatusNotifierItemDBus(this);
 
     connect(this, SIGNAL(toolTipChanged(QString, QString)), _statusNotifierItemDBus, SIGNAL(NewToolTip()));
@@ -117,9 +123,6 @@ void StatusNotifierItem::init()
     StatusNotifierItemParent::init();
     trayMenu()->installEventFilter(this);
 
-    // use the appdata icon folder for now
-    _iconThemePath = Quassel::findDataFilePath("icons");
-
 #ifdef HAVE_DBUSMENU
     _menuObjectPath = "/MenuBar";
     new QuasselDBusMenuExporter(menuObjectPath(), trayMenu(), _statusNotifierItemDBus->dbusConnection()); // will be added as menu child
@@ -155,7 +158,7 @@ void StatusNotifierItem::serviceChange(const QString &name, const QString &oldOw
         //unregistered
         //qDebug() << "Connection to the StatusNotifierWatcher lost";
         delete _statusNotifierWatcher;
-        _statusNotifierWatcher = 0;
+        _statusNotifierWatcher = nullptr;
         setMode(Legacy);
     }
     else if (oldOwner.isEmpty()) {
@@ -174,6 +177,33 @@ void StatusNotifierItem::checkForRegisteredHosts()
 }
 
 
+void StatusNotifierItem::refreshIcons()
+{
+#if QT_VERSION >= 0x050000
+    if (!_iconThemePath.isEmpty()) {
+        QDir baseDir{_iconThemePath + "/hicolor"};
+        baseDir.removeRecursively();
+        for (auto &&trayState : { State::Active, State::Passive, State::NeedsAttention }) {
+            const QIcon &icon = SystemTray::stateIcon(trayState);
+            if (!icon.name().isEmpty()) {
+                for (auto &&size : icon.availableSizes()) {
+                    auto pixDir = QString{"%1/%2x%3/status"}.arg(baseDir.absolutePath()).arg(size.width()).arg(size.height());
+                    QDir{}.mkpath(pixDir);
+                    if (!icon.pixmap(size).save(pixDir + "/" + icon.name() + ".png")) {
+                        qWarning() << "Could not save tray icon" << icon.name() << "for size" << size;
+                    }
+                }
+            }
+        }
+    }
+#endif
+    if (_statusNotifierItemDBus) {
+        emit _statusNotifierItemDBus->NewIcon();
+        emit _statusNotifierItemDBus->NewAttentionIcon();
+    }
+}
+
+
 bool StatusNotifierItem::isSystemTrayAvailable() const
 {
     if (mode() == StatusNotifier)
@@ -249,7 +279,7 @@ QString StatusNotifierItem::title() const
 QString StatusNotifierItem::iconName() const
 {
     if (state() == Passive)
-        return QString("quassel-inactive");
+        return QString("inactive-quassel");
     else
         return QString("quassel");
 }
@@ -258,7 +288,7 @@ QString StatusNotifierItem::iconName() const
 QString StatusNotifierItem::attentionIconName() const
 {
     if (animationEnabled())
-        return QString("quassel-message");
+        return QString("message-quassel");
     else
         return QString("quassel");
 }
@@ -293,7 +323,7 @@ bool StatusNotifierItem::eventFilter(QObject *watched, QEvent *event)
 {
     if (mode() == StatusNotifier) {
         //FIXME: ugly ugly workaround to weird QMenu's focus problems
-#ifdef HAVE_KDE
+#ifdef HAVE_KDE4
         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
@@ -314,7 +344,11 @@ void StatusNotifierItem::showMessage(const QString &title, const QString &messag
     QString message = message_;
     if (_notificationsClient->isValid()) {
         if (_notificationsClientSupportsMarkup)
+#if QT_VERSION < 0x050000
             message = Qt::escape(message);
+#else
+            message = message.toHtmlEscaped();
+#endif
 
         QStringList actions;
         if (_notificationsClientSupportsActions)