File indexing completed on 2025-01-12 03:40:51
0001 /* This file is part of the dbusmenu-qt library 0002 SPDX-FileCopyrightText: 2010 Canonical 0003 Author: Aurelien Gateau <aurelien.gateau@canonical.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 #include "dbusmenuexporterdbus_p.h" 0008 0009 // Qt 0010 #include <QDBusMessage> 0011 #include <QMenu> 0012 #include <QVariant> 0013 0014 // Local 0015 #include "dbusmenuadaptor.h" 0016 #include "dbusmenuexporterprivate_p.h" 0017 #include "dbusmenushortcut_p.h" 0018 #include "debug_p.h" 0019 0020 static const char *DBUSMENU_INTERFACE = "com.canonical.dbusmenu"; 0021 static const char *FDO_PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties"; 0022 0023 DBusMenuExporterDBus::DBusMenuExporterDBus(DBusMenuExporter *exporter) 0024 : QObject(exporter) 0025 , m_exporter(exporter) 0026 , m_status(QStringLiteral("normal")) 0027 { 0028 DBusMenuTypes_register(); 0029 new DbusmenuAdaptor(this); 0030 } 0031 0032 uint DBusMenuExporterDBus::GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &item) 0033 { 0034 QMenu *menu = m_exporter->d->menuForId(parentId); 0035 DMRETURN_VALUE_IF_FAIL(menu, 0); 0036 0037 // Process pending actions, we need them *now* 0038 QMetaObject::invokeMethod(m_exporter, "doUpdateActions"); 0039 m_exporter->d->fillLayoutItem(&item, menu, parentId, recursionDepth, propertyNames); 0040 0041 return m_exporter->d->m_revision; 0042 } 0043 0044 void DBusMenuExporterDBus::Event(int id, const QString &eventType, const QDBusVariant & /*data*/, uint /*timestamp*/) 0045 { 0046 if (eventType == QStringLiteral("clicked")) { 0047 QAction *action = m_exporter->d->m_actionForId.value(id); 0048 if (!action) { 0049 return; 0050 } 0051 // dbusmenu-glib seems to ignore the Q_NOREPLY and blocks when calling 0052 // Event(), so trigger the action asynchronously 0053 QMetaObject::invokeMethod(action, "trigger", Qt::QueuedConnection); 0054 } else if (eventType == QStringLiteral("hovered")) { 0055 QMenu *menu = m_exporter->d->menuForId(id); 0056 if (menu) { 0057 QMetaObject::invokeMethod(menu, "aboutToShow"); 0058 } 0059 } 0060 } 0061 0062 QDBusVariant DBusMenuExporterDBus::GetProperty(int id, const QString &name) 0063 { 0064 QAction *action = m_exporter->d->m_actionForId.value(id); 0065 DMRETURN_VALUE_IF_FAIL(action, QDBusVariant()); 0066 return QDBusVariant(m_exporter->d->m_actionProperties.value(action).value(name)); 0067 } 0068 0069 QVariantMap DBusMenuExporterDBus::getProperties(int id, const QStringList &names) const 0070 { 0071 if (id == 0) { 0072 QVariantMap map; 0073 map.insert(QStringLiteral("children-display"), QStringLiteral("submenu")); 0074 return map; 0075 } 0076 QAction *action = m_exporter->d->m_actionForId.value(id); 0077 DMRETURN_VALUE_IF_FAIL(action, QVariantMap()); 0078 const QVariantMap all = m_exporter->d->m_actionProperties.value(action); 0079 if (names.isEmpty()) { 0080 return all; 0081 } else { 0082 QVariantMap map; 0083 for (const QString &name : names) { 0084 QVariant value = all.value(name); 0085 if (value.isValid()) { 0086 map.insert(name, value); 0087 } 0088 } 0089 return map; 0090 } 0091 } 0092 0093 DBusMenuItemList DBusMenuExporterDBus::GetGroupProperties(const QList<int> &ids, const QStringList &names) 0094 { 0095 DBusMenuItemList list; 0096 for (int id : ids) { 0097 DBusMenuItem item; 0098 item.id = id; 0099 item.properties = getProperties(item.id, names); 0100 list << item; 0101 } 0102 return list; 0103 } 0104 0105 /** 0106 * An helper class for ::AboutToShow, which sets mChanged to true if a menu 0107 * changes after its aboutToShow() signal has been emitted. 0108 */ 0109 class ActionEventFilter : public QObject 0110 { 0111 public: 0112 ActionEventFilter() 0113 { 0114 } 0115 0116 bool mChanged = false; 0117 0118 protected: 0119 bool eventFilter(QObject *object, QEvent *event) override 0120 { 0121 switch (event->type()) { 0122 case QEvent::ActionAdded: 0123 case QEvent::ActionChanged: 0124 case QEvent::ActionRemoved: 0125 mChanged = true; 0126 // We noticed a change, no need to filter anymore 0127 object->removeEventFilter(this); 0128 break; 0129 default: 0130 break; 0131 } 0132 return false; 0133 } 0134 }; 0135 0136 bool DBusMenuExporterDBus::AboutToShow(int id) 0137 { 0138 QMenu *menu = m_exporter->d->menuForId(id); 0139 DMRETURN_VALUE_IF_FAIL(menu, false); 0140 0141 ActionEventFilter filter; 0142 menu->installEventFilter(&filter); 0143 QMetaObject::invokeMethod(menu, "aboutToShow"); 0144 return filter.mChanged; 0145 } 0146 0147 void DBusMenuExporterDBus::setStatus(const QString &status) 0148 { 0149 if (m_status == status) { 0150 return; 0151 } 0152 m_status = status; 0153 0154 QVariantMap map; 0155 map.insert(QStringLiteral("Status"), QVariant(status)); 0156 0157 QDBusMessage msg = 0158 QDBusMessage::createSignal(m_exporter->d->m_objectPath, QString::fromLatin1(FDO_PROPERTIES_INTERFACE), QStringLiteral("PropertiesChanged")); 0159 QVariantList args = QVariantList() << QString::fromLatin1(DBUSMENU_INTERFACE) << map << QStringList() // New properties: none 0160 ; 0161 msg.setArguments(args); 0162 QDBusConnection::sessionBus().send(msg); 0163 } 0164 0165 QString DBusMenuExporterDBus::status() const 0166 { 0167 return m_status; 0168 } 0169 0170 #include "moc_dbusmenuexporterdbus_p.cpp"