1 /* This file is part of the dbusmenu-qt library
2 Copyright 2010 Canonical
3 Author: Aurelien Gateau <aurelien.gateau@canonical.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License (LGPL) as published by the Free Software Foundation;
8 either version 2 of the License, or (at your option) any later
9 version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20 */
21 #include "dbusmenuexporterdbus_p.h"
22
23 // Qt
24 #include <QDBusMessage>
25 #include <QMenu>
26 #include <QVariant>
27
28 // Local
29 #include "dbusmenuadaptor.h"
30 #include "dbusmenuexporterprivate_p.h"
31 #include "dbusmenushortcut_p.h"
32 #include "debug_p.h"
33
34 static const char *DBUSMENU_INTERFACE = "com.canonical.dbusmenu";
35 static const char *FDO_PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties";
36
DBusMenuExporterDBus(DBusMenuExporter * exporter)37 DBusMenuExporterDBus::DBusMenuExporterDBus(DBusMenuExporter *exporter)
38 : QObject(exporter)
39 , m_exporter(exporter)
40 , m_status("normal")
41 {
42 DBusMenuTypes_register();
43 new DbusmenuAdaptor(this);
44 }
45
GetLayout(int parentId,int recursionDepth,const QStringList & propertyNames,DBusMenuLayoutItem & item)46 uint DBusMenuExporterDBus::GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &item)
47 {
48 QMenu *menu = m_exporter->d->menuForId(parentId);
49 DMRETURN_VALUE_IF_FAIL(menu, 0);
50
51 // Process pending actions, we need them *now*
52 QMetaObject::invokeMethod(m_exporter, "doUpdateActions");
53 m_exporter->d->fillLayoutItem(&item, menu, parentId, recursionDepth, propertyNames);
54
55 return m_exporter->d->m_revision;
56 }
57
Event(int id,const QString & eventType,const QDBusVariant &,uint)58 void DBusMenuExporterDBus::Event(int id, const QString &eventType, const QDBusVariant &/*data*/, uint /*timestamp*/)
59 {
60 if (eventType == "clicked") {
61 QAction *action = m_exporter->d->m_actionForId.value(id);
62 if (!action) {
63 return;
64 }
65 // dbusmenu-glib seems to ignore the Q_NOREPLY and blocks when calling
66 // Event(), so trigger the action asynchronously
67 QMetaObject::invokeMethod(action, "trigger", Qt::QueuedConnection);
68 } else if (eventType == "hovered") {
69 QMenu *menu = m_exporter->d->menuForId(id);
70 if (menu) {
71 QMetaObject::invokeMethod(menu, "aboutToShow");
72 }
73 }
74 }
75
GetProperty(int id,const QString & name)76 QDBusVariant DBusMenuExporterDBus::GetProperty(int id, const QString &name)
77 {
78 QAction *action = m_exporter->d->m_actionForId.value(id);
79 DMRETURN_VALUE_IF_FAIL(action, QDBusVariant());
80 return QDBusVariant(m_exporter->d->m_actionProperties.value(action).value(name));
81 }
82
getProperties(int id,const QStringList & names) const83 QVariantMap DBusMenuExporterDBus::getProperties(int id, const QStringList &names) const
84 {
85 if (id == 0) {
86 QVariantMap map;
87 map.insert("children-display", "submenu");
88 return map;
89 }
90 QAction *action = m_exporter->d->m_actionForId.value(id);
91 DMRETURN_VALUE_IF_FAIL(action, QVariantMap());
92 QVariantMap all = m_exporter->d->m_actionProperties.value(action);
93 if (names.isEmpty()) {
94 return all;
95 } else {
96 QVariantMap map;
97 Q_FOREACH(const QString &name, names) {
98 QVariant value = all.value(name);
99 if (value.isValid()) {
100 map.insert(name, value);
101 }
102 }
103 return map;
104 }
105 }
106
GetGroupProperties(const QList<int> & ids,const QStringList & names)107 DBusMenuItemList DBusMenuExporterDBus::GetGroupProperties(const QList<int> &ids, const QStringList &names)
108 {
109 DBusMenuItemList list;
110 Q_FOREACH(int id, ids) {
111 DBusMenuItem item;
112 item.id = id;
113 item.properties = getProperties(item.id, names);
114 list << item;
115 }
116 return list;
117 }
118
119 /**
120 * An helper class for ::AboutToShow, which sets mChanged to true if a menu
121 * changes after its aboutToShow() signal has been emitted.
122 */
123 class ActionEventFilter: public QObject
124 {
125 public:
ActionEventFilter()126 ActionEventFilter()
127 : mChanged(false)
128 {}
129
130 bool mChanged;
131 protected:
eventFilter(QObject * object,QEvent * event)132 bool eventFilter(QObject *object, QEvent *event)
133 {
134 switch (event->type()) {
135 case QEvent::ActionAdded:
136 case QEvent::ActionChanged:
137 case QEvent::ActionRemoved:
138 mChanged = true;
139 // We noticed a change, no need to filter anymore
140 object->removeEventFilter(this);
141 break;
142 default:
143 break;
144 }
145 return false;
146 }
147 };
148
AboutToShow(int id)149 bool DBusMenuExporterDBus::AboutToShow(int id)
150 {
151 QMenu *menu = m_exporter->d->menuForId(id);
152 DMRETURN_VALUE_IF_FAIL(menu, false);
153
154 ActionEventFilter filter;
155 menu->installEventFilter(&filter);
156 QMetaObject::invokeMethod(menu, "aboutToShow");
157 return filter.mChanged;
158 }
159
setStatus(const QString & status)160 void DBusMenuExporterDBus::setStatus(const QString& status)
161 {
162 if (m_status == status) {
163 return;
164 }
165 m_status = status;
166
167 QVariantMap map;
168 map.insert("Status", QVariant(status));
169
170 QDBusMessage msg = QDBusMessage::createSignal(m_exporter->d->m_objectPath, FDO_PROPERTIES_INTERFACE, "PropertiesChanged");
171 QVariantList args = QVariantList()
172 << DBUSMENU_INTERFACE
173 << map
174 << QStringList() // New properties: none
175 ;
176 msg.setArguments(args);
177 QDBusConnection::sessionBus().send(msg);
178 }
179
status() const180 QString DBusMenuExporterDBus::status() const
181 {
182 return m_status;
183 }
184
185
186 #include "moc_dbusmenuexporterdbus_p.cpp"
187