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