File indexing completed on 2024-11-24 05:00:55

0001 /*  This file is part of the KDE libraries
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "kdeplatformsystemtrayicon.h"
0007 #include <QAction>
0008 #include <QActionGroup>
0009 #include <QApplication>
0010 #include <QIcon>
0011 #include <QMenu>
0012 #include <QRect>
0013 #include <kstatusnotifieritem.h>
0014 
0015 #include "statusnotifierwatcher_interface.h"
0016 
0017 SystemTrayMenu::SystemTrayMenu()
0018     : QPlatformMenu()
0019     , m_enabled(QVariant::Bool)
0020     , m_visible(QVariant::Bool)
0021     , m_separatorsCollapsible(QVariant::Bool)
0022     , m_tag(0)
0023 {
0024 }
0025 
0026 SystemTrayMenu::~SystemTrayMenu()
0027 {
0028     if (m_menu) {
0029         m_menu->deleteLater();
0030     }
0031 }
0032 
0033 QPlatformMenuItem *SystemTrayMenu::createMenuItem() const
0034 {
0035     return new SystemTrayMenuItem();
0036 }
0037 
0038 QPlatformMenu *SystemTrayMenu::createSubMenu() const
0039 {
0040     return new SystemTrayMenu();
0041 }
0042 
0043 void SystemTrayMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
0044 {
0045     if (SystemTrayMenuItem *ours = qobject_cast<SystemTrayMenuItem *>(menuItem)) {
0046         bool inserted = false;
0047         if (SystemTrayMenuItem *oursBefore = qobject_cast<SystemTrayMenuItem *>(before)) {
0048             for (auto it = m_items.begin(); it != m_items.end(); ++it) {
0049                 if (*it == oursBefore) {
0050                     m_items.insert(it, ours);
0051                     if (m_menu) {
0052                         m_menu->insertAction(oursBefore->action(), ours->action());
0053                     }
0054                     inserted = true;
0055                     break;
0056                 }
0057             }
0058         }
0059         if (!inserted) {
0060             m_items.append(ours);
0061             if (m_menu) {
0062                 m_menu->addAction(ours->action());
0063             }
0064         }
0065     }
0066 }
0067 
0068 QPlatformMenuItem *SystemTrayMenu::menuItemAt(int position) const
0069 {
0070     if (position < m_items.size()) {
0071         return m_items.at(position);
0072     }
0073     return nullptr;
0074 }
0075 
0076 QPlatformMenuItem *SystemTrayMenu::menuItemForTag(quintptr tag) const
0077 {
0078     auto it = std::find_if(m_items.constBegin(), m_items.constEnd(), [tag](SystemTrayMenuItem *item) {
0079         return item->tag() == tag;
0080     });
0081     if (it != m_items.constEnd()) {
0082         return *it;
0083     }
0084     return nullptr;
0085 }
0086 
0087 void SystemTrayMenu::removeMenuItem(QPlatformMenuItem *menuItem)
0088 {
0089     if (SystemTrayMenuItem *ours = qobject_cast<SystemTrayMenuItem *>(menuItem)) {
0090         m_items.removeOne(ours);
0091         if (ours->action() && m_menu) {
0092             m_menu->removeAction(ours->action());
0093         }
0094     }
0095 }
0096 
0097 void SystemTrayMenu::setEnabled(bool enabled)
0098 {
0099     m_enabled = enabled;
0100     if (m_menu) {
0101         m_menu->setEnabled(enabled);
0102     }
0103 }
0104 
0105 void SystemTrayMenu::setIcon(const QIcon &icon)
0106 {
0107     m_icon = icon;
0108     if (m_menu) {
0109         m_menu->setIcon(icon);
0110     }
0111 }
0112 
0113 void SystemTrayMenu::setTag(quintptr tag)
0114 {
0115     m_tag = tag;
0116 }
0117 
0118 void SystemTrayMenu::setText(const QString &text)
0119 {
0120     m_text = text;
0121     if (m_menu) {
0122         m_menu->setTitle(text);
0123     }
0124 }
0125 
0126 void SystemTrayMenu::setVisible(bool visible)
0127 {
0128     m_visible = visible;
0129     if (m_menu) {
0130         m_menu->setVisible(visible);
0131     }
0132 }
0133 
0134 void SystemTrayMenu::syncMenuItem(QPlatformMenuItem *menuItem)
0135 {
0136     Q_UNUSED(menuItem)
0137     // nothing to do
0138 }
0139 
0140 void SystemTrayMenu::syncSeparatorsCollapsible(bool enable)
0141 {
0142     m_separatorsCollapsible = enable;
0143     if (m_menu) {
0144         m_menu->setSeparatorsCollapsible(enable);
0145     }
0146 }
0147 
0148 quintptr SystemTrayMenu::tag() const
0149 {
0150     return m_tag;
0151 }
0152 
0153 QMenu *SystemTrayMenu::menu()
0154 {
0155     if (!m_menu) {
0156         createMenu();
0157     }
0158     return m_menu;
0159 }
0160 
0161 void SystemTrayMenu::createMenu()
0162 {
0163     m_menu = new QMenu();
0164     connect(m_menu, &QMenu::aboutToShow, this, &QPlatformMenu::aboutToShow);
0165     connect(m_menu, &QMenu::aboutToHide, this, &QPlatformMenu::aboutToHide);
0166 
0167     if (!m_icon.isNull()) {
0168         m_menu->setIcon(m_icon);
0169     }
0170     if (m_menu->title() != m_text) {
0171         m_menu->setTitle(m_text);
0172     }
0173     if (!m_enabled.isNull()) {
0174         m_menu->setEnabled(m_enabled.toBool());
0175     }
0176     if (!m_visible.isNull()) {
0177         m_menu->setVisible(m_visible.toBool());
0178     }
0179     if (!m_separatorsCollapsible.isNull()) {
0180         m_menu->setSeparatorsCollapsible(m_separatorsCollapsible.toBool());
0181     }
0182     for (auto item : std::as_const(m_items)) {
0183         m_menu->addAction(item->action());
0184     }
0185 }
0186 
0187 SystemTrayMenuItem::SystemTrayMenuItem()
0188     : QPlatformMenuItem()
0189     , m_tag(0)
0190     , m_action(new QAction(this))
0191 {
0192     connect(m_action, &QAction::triggered, this, &QPlatformMenuItem::activated);
0193     connect(m_action, &QAction::hovered, this, &QPlatformMenuItem::hovered);
0194 }
0195 
0196 SystemTrayMenuItem::~SystemTrayMenuItem()
0197 {
0198 }
0199 
0200 void SystemTrayMenuItem::setCheckable(bool checkable)
0201 {
0202     m_action->setCheckable(checkable);
0203 }
0204 
0205 void SystemTrayMenuItem::setChecked(bool isChecked)
0206 {
0207     m_action->setChecked(isChecked);
0208 }
0209 
0210 void SystemTrayMenuItem::setEnabled(bool enabled)
0211 {
0212     m_action->setEnabled(enabled);
0213 }
0214 
0215 void SystemTrayMenuItem::setFont(const QFont &font)
0216 {
0217     m_action->setFont(font);
0218 }
0219 
0220 void SystemTrayMenuItem::setIcon(const QIcon &icon)
0221 {
0222     m_action->setIcon(icon);
0223 }
0224 
0225 void SystemTrayMenuItem::setIsSeparator(bool isSeparator)
0226 {
0227     m_action->setSeparator(isSeparator);
0228 }
0229 
0230 void SystemTrayMenuItem::setMenu(QPlatformMenu *menu)
0231 {
0232     if (SystemTrayMenu *ourMenu = qobject_cast<SystemTrayMenu *>(menu)) {
0233         m_action->setMenu(ourMenu->menu());
0234     }
0235 }
0236 
0237 void SystemTrayMenuItem::setRole(QPlatformMenuItem::MenuRole role)
0238 {
0239     Q_UNUSED(role)
0240 }
0241 
0242 void SystemTrayMenuItem::setShortcut(const QKeySequence &shortcut)
0243 {
0244     m_action->setShortcut(shortcut);
0245 }
0246 
0247 void SystemTrayMenuItem::setTag(quintptr tag)
0248 {
0249     m_tag = tag;
0250 }
0251 
0252 void SystemTrayMenuItem::setText(const QString &text)
0253 {
0254     m_action->setText(text);
0255 }
0256 
0257 void SystemTrayMenuItem::setVisible(bool isVisible)
0258 {
0259     m_action->setVisible(isVisible);
0260 }
0261 
0262 void SystemTrayMenuItem::setIconSize(int size)
0263 {
0264     Q_UNUSED(size)
0265 }
0266 
0267 void SystemTrayMenuItem::setHasExclusiveGroup(bool hasExclusiveGroup)
0268 {
0269     if (hasExclusiveGroup) {
0270         if (!m_action->actionGroup()) {
0271             m_action->setActionGroup(new QActionGroup(m_action));
0272         }
0273     } else {
0274         QActionGroup *actionGroup = m_action->actionGroup();
0275         if (actionGroup) {
0276             m_action->setActionGroup(nullptr);
0277             delete actionGroup;
0278         }
0279     }
0280 }
0281 
0282 quintptr SystemTrayMenuItem::tag() const
0283 {
0284     return m_tag;
0285 }
0286 
0287 QAction *SystemTrayMenuItem::action() const
0288 {
0289     return m_action;
0290 }
0291 
0292 KDEPlatformSystemTrayIcon::KDEPlatformSystemTrayIcon()
0293     : QPlatformSystemTrayIcon()
0294 {
0295 }
0296 
0297 KDEPlatformSystemTrayIcon::~KDEPlatformSystemTrayIcon()
0298 {
0299 }
0300 
0301 void KDEPlatformSystemTrayIcon::init()
0302 {
0303     if (!m_sni) {
0304         m_sni = new KStatusNotifierItem();
0305         m_sni->setStandardActionsEnabled(false);
0306         m_sni->setTitle(QApplication::applicationDisplayName());
0307         m_sni->setStatus(KStatusNotifierItem::Active);
0308         connect(m_sni, &KStatusNotifierItem::activateRequested, [this](bool active, const QPoint &pos) {
0309             Q_UNUSED(active)
0310             Q_UNUSED(pos)
0311             Q_EMIT activated(QPlatformSystemTrayIcon::Trigger);
0312         });
0313         connect(m_sni, &KStatusNotifierItem::secondaryActivateRequested, [this](const QPoint &pos) {
0314             Q_UNUSED(pos)
0315             Q_EMIT activated(QPlatformSystemTrayIcon::MiddleClick);
0316         });
0317     }
0318 }
0319 
0320 void KDEPlatformSystemTrayIcon::cleanup()
0321 {
0322     delete m_sni;
0323     m_sni = nullptr;
0324 }
0325 
0326 void KDEPlatformSystemTrayIcon::updateIcon(const QIcon &icon)
0327 {
0328     if (!m_sni) {
0329         return;
0330     }
0331     if (icon.name().isEmpty()) {
0332         m_sni->setIconByPixmap(icon);
0333         m_sni->setToolTipIconByPixmap(icon);
0334     } else {
0335         m_sni->setIconByName(icon.name());
0336         m_sni->setToolTipIconByName(icon.name());
0337     }
0338 }
0339 
0340 void KDEPlatformSystemTrayIcon::updateToolTip(const QString &tooltip)
0341 {
0342     if (!m_sni) {
0343         return;
0344     }
0345     m_sni->setToolTipTitle(tooltip);
0346 }
0347 
0348 void KDEPlatformSystemTrayIcon::updateMenu(QPlatformMenu *menu)
0349 {
0350     if (!m_sni) {
0351         return;
0352     }
0353     if (SystemTrayMenu *ourMenu = qobject_cast<SystemTrayMenu *>(menu)) {
0354         m_sni->setContextMenu(ourMenu->menu());
0355     }
0356 }
0357 
0358 QPlatformMenu *KDEPlatformSystemTrayIcon::createMenu() const
0359 {
0360     return new SystemTrayMenu();
0361 }
0362 
0363 QRect KDEPlatformSystemTrayIcon::geometry() const
0364 {
0365     // StatusNotifierItem doesn't provide the geometry
0366     return QRect();
0367 }
0368 
0369 void KDEPlatformSystemTrayIcon::showMessage(const QString &title, const QString &msg, const QIcon &icon, MessageIcon iconType, int secs)
0370 {
0371     Q_UNUSED(iconType)
0372     if (!m_sni) {
0373         return;
0374     }
0375     m_sni->showMessage(title, msg, icon.name(), secs);
0376 }
0377 
0378 bool KDEPlatformSystemTrayIcon::isSystemTrayAvailable() const
0379 {
0380     org::kde::StatusNotifierWatcher systrayHost(QStringLiteral("org.kde.StatusNotifierWatcher"),
0381                                                 QStringLiteral("/StatusNotifierWatcher"),
0382                                                 QDBusConnection::sessionBus());
0383     if (systrayHost.isValid()) {
0384         return systrayHost.isStatusNotifierHostRegistered();
0385     }
0386 
0387     return false;
0388 }
0389 
0390 bool KDEPlatformSystemTrayIcon::supportsMessages() const
0391 {
0392     return true;
0393 }