7#ifndef QT_NO_SYSTEMTRAYICON
12#include <QLoggingCategory>
13#include <QStandardPaths>
18#include <QDBusConnectionInterface>
19#include <QDBusArgument>
20#include <QDBusMetaType>
21#include <QDBusServiceWatcher>
23#include <qpa/qplatformmenu.h>
24#include <qpa/qplatformintegration.h>
25#include <qpa/qplatformservices.h>
27#include <private/qdbusmenuconnection_p.h>
28#include <private/qstatusnotifieritemadaptor_p.h>
29#include <private/qdbusmenuadaptor_p.h>
30#include <private/qdbusplatformmenu_p.h>
31#include <private/qxdgnotificationproxy_p.h>
32#include <private/qlockfile_p.h>
33#include <private/qguiapplication_p.h>
40using namespace Qt::StringLiterals;
46 QString tempPath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
47 if (!tempPath.isEmpty()) {
48 QString flatpakId = qEnvironmentVariable(
"FLATPAK_ID");
49 if (!flatpakId.isEmpty() && QFileInfo::exists(
"/.flatpak-info"_L1))
50 tempPath +=
"/app/"_L1 + flatpakId;
54 tempPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
56 if (!tempPath.isEmpty()) {
57 QDir tempDir(tempPath);
61 if (tempDir.mkpath(QStringLiteral(
"."))) {
62 const QFile::Permissions permissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner;
63 if (QFile(tempPath).setPermissions(permissions))
68 return QDir::tempPath();
80 static const QString TempFileTemplate = iconTempPath() +
"/qt-trayicon-XXXXXX.png"_L1;
81 return TempFileTemplate;
85
86
87
90 : m_dbusConnection(
nullptr)
91 , m_adaptor(
new QStatusNotifierItemAdaptor(
this))
92 , m_menuAdaptor(
nullptr)
95 , m_instanceId(KDEItemFormat.arg(QCoreApplication::applicationPid()).arg(++instanceCount))
96 , m_category(QStringLiteral(
"ApplicationStatus"))
97 , m_defaultStatus(QStringLiteral(
"Active"))
98 , m_status(m_defaultStatus)
100 , m_tempAttentionIcon(
nullptr)
101 , m_registered(
false)
105 QDBusMenuItem::registerDBusTypes();
106 qDBusRegisterMetaType<QXdgDBusImageStruct>();
107 qDBusRegisterMetaType<QXdgDBusImageVector>();
108 qDBusRegisterMetaType<QXdgDBusToolTipStruct>();
110 connect(
this, SIGNAL(statusChanged(QString)), m_adaptor, SIGNAL(NewStatus(QString)));
112 connect(
this, SIGNAL(
iconChanged()), m_adaptor, SIGNAL(NewIcon()));
113 connect(
this, SIGNAL(
attention()), m_adaptor, SIGNAL(NewAttentionIcon()));
114 connect(
this, SIGNAL(
menuChanged()), m_adaptor, SIGNAL(NewMenu()));
115 connect(
this, SIGNAL(
attention()), m_adaptor, SIGNAL(NewTitle()));
116 connect(&m_attentionTimer, SIGNAL(timeout()),
this, SLOT(attentionTimerExpired()));
117 m_attentionTimer.setSingleShot(
true);
126 qCDebug(qLcTray) <<
"registering" << m_instanceId;
127 m_registered = dBusConnection()->registerTrayIcon(
this);
128 QObject::connect(dBusConnection()->dbusWatcher(), &QDBusServiceWatcher::serviceRegistered,
129 this, &QDBusTrayIcon::watcherServiceRegistered);
134 qCDebug(qLcTray) <<
"unregistering" << m_instanceId;
136 dBusConnection()->unregisterTrayIcon(
this);
137 delete m_dbusConnection;
138 m_dbusConnection =
nullptr;
140 m_notifier =
nullptr;
141 m_registered =
false;
144void QDBusTrayIcon::watcherServiceRegistered(
const QString &serviceName)
146 Q_UNUSED(serviceName);
150 dBusConnection()->registerTrayIconWithWatcher(
this);
155 m_messageTitle = QString();
156 m_message = QString();
157 m_attentionIcon = QIcon();
159 emit tooltipChanged();
160 setStatus(m_defaultStatus);
165 qCDebug(qLcTray) << status;
166 if (m_status == status)
169 emit statusChanged(m_status);
176 static bool necessity_checked =
false;
177 static bool necessary =
false;
178 if (!necessity_checked) {
179 QDBusConnection session = QDBusConnection::sessionBus();
180 uint pid = session.interface()->servicePid(KDEWatcherService).value();
181 QString processName = QLockFilePrivate::processNameByPid(pid);
182 necessary = processName.endsWith(
"indicator-application-service"_L1);
184 necessary = session.interface()->isServiceRegistered(
185 QStringLiteral(
"com.canonical.indicator.application"));
188 necessary = session.interface()->isServiceRegistered(
189 QStringLiteral(
"org.ayatana.indicator.application"));
191 if (!necessary && QGuiApplication::desktopSettingsAware()) {
194 const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services();
196 necessary = services->desktopEnvironment().split(
':').contains(
"UNITY");
198 necessity_checked =
true;
202 QTemporaryFile *ret =
new QTemporaryFile(tempFileTemplate(),
this);
207 icon.pixmap(QSize(22, 22)).save(ret);
214 if (!m_dbusConnection) {
215 m_dbusConnection =
new QDBusMenuConnection(
this, m_instanceId);
216 m_notifier =
new QXdgNotificationInterface(XdgNotificationService,
217 XdgNotificationPath, m_dbusConnection->connection(),
this);
218 connect(m_notifier, SIGNAL(NotificationClosed(uint,uint)),
this, SLOT(notificationClosed(uint,uint)));
219 connect(m_notifier, SIGNAL(ActionInvoked(uint,QString)),
this, SLOT(actionInvoked(uint,QString)));
221 return m_dbusConnection;
226 m_iconName = icon.name();
228 if (m_iconName.isEmpty()) {
231 m_tempIcon = tempIcon(icon);
233 m_iconName = m_tempIcon->fileName();
235 qCDebug(qLcTray) << m_iconName << icon.availableSizes();
241 qCDebug(qLcTray) << tooltip;
243 emit tooltipChanged();
248 return new QDBusPlatformMenu();
253 qCDebug(qLcTray) << menu;
255 if (m_menu != newMenu) {
257 dBusConnection()->unregisterTrayIconMenu(
this);
258 delete m_menuAdaptor;
261 m_menuAdaptor =
new QDBusMenuAdaptor(m_menu);
263 connect(m_menu, SIGNAL(propertiesUpdated(QDBusMenuItemList,QDBusMenuItemKeysList)),
264 m_menuAdaptor, SIGNAL(ItemsPropertiesUpdated(QDBusMenuItemList,QDBusMenuItemKeysList)));
265 connect(m_menu, SIGNAL(updated(uint,
int)),
266 m_menuAdaptor, SIGNAL(LayoutUpdated(uint,
int)));
267 dBusConnection()->registerTrayIconMenu(
this);
273 QPlatformSystemTrayIcon::MessageIcon iconType,
int msecs)
275 m_messageTitle = title;
277 m_attentionIcon = icon;
278 QStringList notificationActions;
281 m_attentionIconName = QStringLiteral(
"dialog-information");
284 m_attentionIconName = QStringLiteral(
"dialog-warning");
287 m_attentionIconName = QStringLiteral(
"dialog-error");
291 notificationActions << DefaultAction << tr(
"OK");
294 m_attentionIconName.clear();
297 if (m_attentionIconName.isEmpty()) {
298 if (m_tempAttentionIcon)
299 delete m_tempAttentionIcon;
300 m_tempAttentionIcon = tempIcon(icon);
301 if (m_tempAttentionIcon)
302 m_attentionIconName = m_tempAttentionIcon->fileName();
304 qCDebug(qLcTray) << title << msg <<
305 QPlatformSystemTrayIcon::metaObject()->enumerator(
306 QPlatformSystemTrayIcon::staticMetaObject.indexOfEnumerator(
"MessageIcon")).valueToKey(iconType)
307 << m_attentionIconName << msecs;
308 setStatus(QStringLiteral(
"NeedsAttention"));
309 m_attentionTimer.start(msecs);
310 emit tooltipChanged();
317 int urgency =
static_cast<
int>(iconType) - 1;
320 hints.insert(
"urgency"_L1, QVariant(urgency));
321 m_notifier->notify(QCoreApplication::applicationName(), 0,
322 m_attentionIconName, title, msg, notificationActions, hints, msecs);
325void QDBusTrayIcon::actionInvoked(uint id,
const QString &action)
327 qCDebug(qLcTray) << id << action;
328 emit messageClicked();
333 qCDebug(qLcTray) << id << reason;
338 QDBusMenuConnection * conn =
const_cast<
QDBusTrayIcon *>(
this)->dBusConnection();
342 qCDebug(qLcTray) << conn->isWatcherRegistered();
343 return conn->isWatcherRegistered();
348#include "moc_qdbustrayicon_p.cpp"
void init() override
This method is called to initialize the platform dependent implementation.
void updateMenu(QPlatformMenu *menu) override
This method is called when the system tray menu did change.
void showMessage(const QString &title, const QString &msg, const QIcon &icon, MessageIcon iconType, int msecs) override
Shows a balloon message for the entry with the given title, message msg and icon for the time specifi...
QDBusMenuConnection * dBusConnection()
void cleanup() override
This method is called to cleanup the platform dependent implementation.
void updateIcon(const QIcon &icon) override
This method is called when the icon did change.
void updateToolTip(const QString &tooltip) override
This method is called when the tooltip text did change.
bool isSystemTrayAvailable() const override
Returns true if the system tray is available on the platform.
QPlatformMenu * createMenu() const override
This method allows platforms to use a different QPlatformMenu for system tray menus than what would n...
static const QString KDEWatcherService
static const QString DefaultAction
static const QString KDEItemFormat
static QString tempFileTemplate()
static const QString XdgNotificationPath
static QString iconTempPath()
static const QString XdgNotificationService
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")