File indexing completed on 2024-05-12 05:37:25
0001 /* 0002 SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "actions.h" 0008 0009 #include "debug.h" 0010 0011 #include <QDBusConnection> 0012 #include <QDBusPendingCallWatcher> 0013 #include <QDBusPendingReply> 0014 #include <QDebug> 0015 0016 Actions::Actions(const QString &serviceName, const QString &objectPath, QObject *parent) 0017 : QObject(parent) 0018 , m_interface(new OrgGtkActionsInterface(serviceName, objectPath, QDBusConnection::sessionBus(), this)) 0019 { 0020 connect(m_interface.get(), &OrgGtkActionsInterface::Changed, this, &Actions::onActionsChanged); 0021 } 0022 0023 Actions::~Actions() = default; 0024 0025 void Actions::load() 0026 { 0027 QDBusPendingReply<GMenuActionMap> reply = m_interface->DescribeAll(); 0028 auto *watcher = new QDBusPendingCallWatcher(reply, this); 0029 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0030 QDBusPendingReply<GMenuActionMap> reply = *watcher; 0031 if (reply.isError()) { 0032 qCWarning(DBUSMENUPROXY) << "Failed to get actions from" << m_interface->service() << "at" << m_interface->path() << reply.error(); 0033 Q_EMIT failedToLoad(); 0034 } else { 0035 m_actions = reply.value(); 0036 Q_EMIT loaded(); 0037 } 0038 watcher->deleteLater(); 0039 }); 0040 } 0041 0042 bool Actions::get(const QString &name, GMenuAction &action) const 0043 { 0044 auto it = m_actions.find(name); 0045 if (it == m_actions.constEnd()) { 0046 return false; 0047 } 0048 0049 action = *it; 0050 return true; 0051 } 0052 0053 GMenuActionMap Actions::getAll() const 0054 { 0055 return m_actions; 0056 } 0057 0058 void Actions::trigger(const QString &name, const QVariant &target, uint timestamp) 0059 { 0060 if (!m_actions.contains(name)) { 0061 qCWarning(DBUSMENUPROXY) << "Cannot invoke action" << name << "which doesn't exist"; 0062 return; 0063 } 0064 0065 QVariantList parameter; 0066 if (target.isValid()) { 0067 parameter << target; 0068 } 0069 0070 QVariantMap platformData; 0071 if (timestamp) { 0072 // From documentation: 0073 // If the startup notification id is not available, this can be just "_TIMEtime", where 0074 // time is the time stamp from the event triggering the call. 0075 // see also gtkwindow.c extract_time_from_startup_id and startup_id_is_fake 0076 platformData.insert(QStringLiteral("desktop-startup-id"), QStringLiteral("_TIME") + QString::number(timestamp)); 0077 } 0078 0079 QDBusPendingReply<void> reply = m_interface->Activate(name, parameter, platformData); 0080 auto *watcher = new QDBusPendingCallWatcher(reply, this); 0081 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, name](QDBusPendingCallWatcher *watcher) { 0082 QDBusPendingReply<void> reply = *watcher; 0083 if (reply.isError()) { 0084 qCWarning(DBUSMENUPROXY) << "Failed to invoke action" << name << "on" << m_interface->service() << "at" << m_interface->path() << reply.error(); 0085 } 0086 watcher->deleteLater(); 0087 }); 0088 } 0089 0090 bool Actions::isValid() const 0091 { 0092 return !m_actions.isEmpty(); 0093 } 0094 0095 void Actions::onActionsChanged(const QStringList &removed, const StringBoolMap &enabledChanges, const QVariantMap &stateChanges, const GMenuActionMap &added) 0096 { 0097 // Collect the actions that we removed, altered, or added, so we can eventually signal changes for all menus that contain one of those actions 0098 QStringList dirtyActions; 0099 0100 // TODO I bet for most of the loops below we could use a nice short std algorithm 0101 0102 for (const QString &removedAction : removed) { 0103 if (m_actions.remove(removedAction)) { 0104 dirtyActions.append(removedAction); 0105 } 0106 } 0107 0108 for (auto it = enabledChanges.constBegin(), end = enabledChanges.constEnd(); it != end; ++it) { 0109 const QString &actionName = it.key(); 0110 const bool enabled = it.value(); 0111 0112 auto actionIt = m_actions.find(actionName); 0113 if (actionIt == m_actions.end()) { 0114 qCInfo(DBUSMENUPROXY) << "Got enabled changed for action" << actionName << "which we don't know"; 0115 continue; 0116 } 0117 0118 GMenuAction &action = *actionIt; 0119 if (action.enabled != enabled) { 0120 action.enabled = enabled; 0121 dirtyActions.append(actionName); 0122 } else { 0123 qCInfo(DBUSMENUPROXY) << "Got enabled change for action" << actionName << "which didn't change it"; 0124 } 0125 } 0126 0127 for (auto it = stateChanges.constBegin(), end = stateChanges.constEnd(); it != end; ++it) { 0128 const QString &actionName = it.key(); 0129 const QVariant &state = it.value(); 0130 0131 auto actionIt = m_actions.find(actionName); 0132 if (actionIt == m_actions.end()) { 0133 qCInfo(DBUSMENUPROXY) << "Got state changed for action" << actionName << "which we don't know"; 0134 continue; 0135 } 0136 0137 GMenuAction &action = *actionIt; 0138 0139 if (action.state.isEmpty()) { 0140 qCDebug(DBUSMENUPROXY) << "Got new state for action" << actionName << "that didn't have any state before"; 0141 action.state.append(state); 0142 dirtyActions.append(actionName); 0143 } else { 0144 // Action state is a list but the state change only sends us a single variant, so just overwrite the first one 0145 QVariant &firstState = action.state.first(); 0146 if (firstState != state) { 0147 firstState = state; 0148 dirtyActions.append(actionName); 0149 } else { 0150 qCInfo(DBUSMENUPROXY) << "Got state change for action" << actionName << "which didn't change it"; 0151 } 0152 } 0153 } 0154 0155 // unite() will result in keys being present multiple times, do it manually and overwrite existing ones 0156 for (auto it = added.constBegin(), end = added.constEnd(); it != end; ++it) { 0157 const QString &actionName = it.key(); 0158 0159 if (DBUSMENUPROXY().isInfoEnabled()) { 0160 if (m_actions.contains(actionName)) { 0161 qCInfo(DBUSMENUPROXY) << "Got new action" << actionName << "that we already have, overwriting existing one"; 0162 } 0163 } 0164 0165 m_actions.insert(actionName, it.value()); 0166 0167 dirtyActions.append(actionName); 0168 } 0169 0170 if (!dirtyActions.isEmpty()) { 0171 Q_EMIT actionsChanged(dirtyActions); 0172 } 0173 }