icons: Add hicolor fallback for Qt < 5.5
[quassel.git] / src / qtui / statusnotifieritem.cpp
index 4eb73b3..dc33b97 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2014 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 <QFile>
 #include <QMenu>
 #include <QMouseEvent>
 #include <QTextDocument>
 
+#include "qtui.h"
 #include "quassel.h"
 #include "statusnotifieritem.h"
 #include "statusnotifieritemdbus.h"
@@ -51,29 +54,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 +94,8 @@ void StatusNotifierItem::init()
     qDBusRegisterMetaType<DBusImageVector>();
     qDBusRegisterMetaType<DBusToolTipStruct>();
 
+    refreshIcons();
+
     _statusNotifierItemDBus = new StatusNotifierItemDBus(this);
 
     connect(this, SIGNAL(toolTipChanged(QString, QString)), _statusNotifierItemDBus, SIGNAL(NewToolTip()));
@@ -117,9 +124,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 +159,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 +178,44 @@ 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 }) {
+            auto iconName = SystemTray::iconName(trayState);
+            QIcon icon = QIcon::fromTheme(iconName);
+            if (!icon.isNull()) {
+                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 + "/" + iconName + ".png")) {
+                        qWarning() << "Could not save tray icon" << iconName << "for size" << size;
+                    }
+                }
+            }
+            else {
+                // No theme icon found; use fallback from resources
+                auto iconDir = QString{"%1/24x24/status"}.arg(baseDir.absolutePath());
+                QDir{}.mkpath(iconDir);
+                if (!QFile::copy(QString{":/icons/hicolor/24x24/status/%1.svg"}.arg(iconName),
+                                 QString{"%1/%2.svg"}.arg(iconDir, iconName))) {
+                    qWarning() << "Could not access fallback tray icon" << iconName;
+                    continue;
+                }
+            }
+        }
+    }
+#endif
+    if (_statusNotifierItemDBus) {
+        emit _statusNotifierItemDBus->NewIcon();
+        emit _statusNotifierItemDBus->NewAttentionIcon();
+    }
+}
+
+
 bool StatusNotifierItem::isSystemTrayAvailable() const
 {
     if (mode() == StatusNotifier)
@@ -248,25 +290,29 @@ QString StatusNotifierItem::title() const
 
 QString StatusNotifierItem::iconName() const
 {
-    if (state() == Passive)
-        return QString("quassel-inactive");
-    else
-        return QString("quassel");
+    if (state() == Passive) {
+        return SystemTray::iconName(State::Passive);
+    }
+    else {
+        return SystemTray::iconName(State::Active);
+    }
 }
 
 
 QString StatusNotifierItem::attentionIconName() const
 {
-    if (animationEnabled())
-        return QString("quassel-message");
-    else
-        return QString("quassel");
+    if (animationEnabled()) {
+        return SystemTray::iconName(State::NeedsAttention);
+    }
+    else {
+        return SystemTray::iconName(State::Active);
+    }
 }
 
 
 QString StatusNotifierItem::toolTipIconName() const
 {
-    return QString("quassel");
+    return "quassel";
 }
 
 
@@ -293,7 +339,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 +360,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)