1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qdbusmenutypes_p.h"
41 
42 #include <QDBusConnection>
43 #include <QDBusMetaType>
44 #include <QImage>
45 #include <QIcon>
46 #include <QImage>
47 #include <QPixmap>
48 #include <QDebug>
49 #include <QtEndian>
50 #include <QBuffer>
51 #include <private/qkeysequence_p.h>
52 #include <qpa/qplatformmenu.h>
53 #include "qdbusplatformmenu_p.h"
54 
55 QT_BEGIN_NAMESPACE
56 
operator <<(QDBusArgument & arg,const QDBusMenuItem & item)57 const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuItem &item)
58 {
59     arg.beginStructure();
60     arg << item.m_id << item.m_properties;
61     arg.endStructure();
62     return arg;
63 }
64 
operator >>(const QDBusArgument & arg,QDBusMenuItem & item)65 const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuItem &item)
66 {
67     arg.beginStructure();
68     arg >> item.m_id >> item.m_properties;
69     arg.endStructure();
70     return arg;
71 }
72 
operator <<(QDBusArgument & arg,const QDBusMenuItemKeys & keys)73 const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuItemKeys &keys)
74 {
75     arg.beginStructure();
76     arg << keys.id << keys.properties;
77     arg.endStructure();
78     return arg;
79 }
80 
operator >>(const QDBusArgument & arg,QDBusMenuItemKeys & keys)81 const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuItemKeys &keys)
82 {
83     arg.beginStructure();
84     arg >> keys.id >> keys.properties;
85     arg.endStructure();
86     return arg;
87 }
88 
populate(int id,int depth,const QStringList & propertyNames,const QDBusPlatformMenu * topLevelMenu)89 uint QDBusMenuLayoutItem::populate(int id, int depth, const QStringList &propertyNames, const QDBusPlatformMenu *topLevelMenu)
90 {
91     qCDebug(qLcMenu) << id << "depth" << depth << propertyNames;
92     m_id = id;
93     if (id == 0) {
94         m_properties.insert(QLatin1String("children-display"), QLatin1String("submenu"));
95         if (topLevelMenu)
96             populate(topLevelMenu, depth, propertyNames);
97         return 1; // revision
98     }
99 
100     QDBusPlatformMenuItem *item = QDBusPlatformMenuItem::byId(id);
101     if (item) {
102         const QDBusPlatformMenu *menu = static_cast<const QDBusPlatformMenu *>(item->menu());
103 
104         if (menu) {
105             if (depth != 0)
106                 populate(menu, depth, propertyNames);
107             return menu->revision();
108         }
109     }
110 
111     return 1; // revision
112 }
113 
populate(const QDBusPlatformMenu * menu,int depth,const QStringList & propertyNames)114 void QDBusMenuLayoutItem::populate(const QDBusPlatformMenu *menu, int depth, const QStringList &propertyNames)
115 {
116     const auto items = menu->items();
117     for (QDBusPlatformMenuItem *item : items) {
118         QDBusMenuLayoutItem child;
119         child.populate(item, depth - 1, propertyNames);
120         m_children << child;
121     }
122 }
123 
populate(const QDBusPlatformMenuItem * item,int depth,const QStringList & propertyNames)124 void QDBusMenuLayoutItem::populate(const QDBusPlatformMenuItem *item, int depth, const QStringList &propertyNames)
125 {
126     m_id = item->dbusID();
127     QDBusMenuItem proxy(item);
128     m_properties = proxy.m_properties;
129 
130     const QDBusPlatformMenu *menu = static_cast<const QDBusPlatformMenu *>(item->menu());
131     if (depth != 0 && menu)
132         populate(menu, depth, propertyNames);
133 }
134 
operator <<(QDBusArgument & arg,const QDBusMenuLayoutItem & item)135 const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuLayoutItem &item)
136 {
137     arg.beginStructure();
138     arg << item.m_id << item.m_properties;
139     arg.beginArray(qMetaTypeId<QDBusVariant>());
140     for (const QDBusMenuLayoutItem &child : item.m_children)
141         arg << QDBusVariant(QVariant::fromValue<QDBusMenuLayoutItem>(child));
142     arg.endArray();
143     arg.endStructure();
144     return arg;
145 }
146 
operator >>(const QDBusArgument & arg,QDBusMenuLayoutItem & item)147 const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuLayoutItem &item)
148 {
149     arg.beginStructure();
150     arg >> item.m_id >> item.m_properties;
151     arg.beginArray();
152     while (!arg.atEnd()) {
153         QDBusVariant dbusVariant;
154         arg >> dbusVariant;
155         QDBusArgument childArgument = qvariant_cast<QDBusArgument>(dbusVariant.variant());
156 
157         QDBusMenuLayoutItem child;
158         childArgument >> child;
159         item.m_children.append(child);
160     }
161     arg.endArray();
162     arg.endStructure();
163     return arg;
164 }
165 
registerDBusTypes()166 void QDBusMenuItem::registerDBusTypes()
167 {
168     qDBusRegisterMetaType<QDBusMenuItem>();
169     qDBusRegisterMetaType<QDBusMenuItemList>();
170     qDBusRegisterMetaType<QDBusMenuItemKeys>();
171     qDBusRegisterMetaType<QDBusMenuItemKeysList>();
172     qDBusRegisterMetaType<QDBusMenuLayoutItem>();
173     qDBusRegisterMetaType<QDBusMenuLayoutItemList>();
174     qDBusRegisterMetaType<QDBusMenuEvent>();
175     qDBusRegisterMetaType<QDBusMenuEventList>();
176     qDBusRegisterMetaType<QDBusMenuShortcut>();
177 }
178 
QDBusMenuItem(const QDBusPlatformMenuItem * item)179 QDBusMenuItem::QDBusMenuItem(const QDBusPlatformMenuItem *item)
180     : m_id(item->dbusID())
181 {
182     if (item->isSeparator()) {
183         m_properties.insert(QLatin1String("type"), QLatin1String("separator"));
184     } else {
185         m_properties.insert(QLatin1String("label"), convertMnemonic(item->text()));
186         if (item->menu())
187             m_properties.insert(QLatin1String("children-display"), QLatin1String("submenu"));
188         m_properties.insert(QLatin1String("enabled"), item->isEnabled());
189         if (item->isCheckable()) {
190             QString toggleType = item->hasExclusiveGroup() ? QLatin1String("radio") : QLatin1String("checkmark");
191             m_properties.insert(QLatin1String("toggle-type"), toggleType);
192             m_properties.insert(QLatin1String("toggle-state"), item->isChecked() ? 1 : 0);
193         }
194 #ifndef QT_NO_SHORTCUT
195         const QKeySequence &scut = item->shortcut();
196         if (!scut.isEmpty()) {
197             QDBusMenuShortcut shortcut = convertKeySequence(scut);
198             m_properties.insert(QLatin1String("shortcut"), QVariant::fromValue(shortcut));
199         }
200 #endif
201         const QIcon &icon = item->icon();
202         if (!icon.name().isEmpty()) {
203             m_properties.insert(QLatin1String("icon-name"), icon.name());
204         } else if (!icon.isNull()) {
205             QBuffer buf;
206             icon.pixmap(16).save(&buf, "PNG");
207             m_properties.insert(QLatin1String("icon-data"), buf.data());
208         }
209     }
210     m_properties.insert(QLatin1String("visible"), item->isVisible());
211 }
212 
items(const QList<int> & ids,const QStringList & propertyNames)213 QDBusMenuItemList QDBusMenuItem::items(const QList<int> &ids, const QStringList &propertyNames)
214 {
215     Q_UNUSED(propertyNames)
216     QDBusMenuItemList ret;
217     const QList<const QDBusPlatformMenuItem *> items = QDBusPlatformMenuItem::byIds(ids);
218     ret.reserve(items.size());
219     for (const QDBusPlatformMenuItem *item : items)
220         ret << QDBusMenuItem(item);
221     return ret;
222 }
223 
convertMnemonic(const QString & label)224 QString QDBusMenuItem::convertMnemonic(const QString &label)
225 {
226     // convert only the first occurrence of ampersand which is not at the end
227     // dbusmenu uses underscore instead of ampersand
228     int idx = label.indexOf(QLatin1Char('&'));
229     if (idx < 0 || idx == label.length() - 1)
230         return label;
231     QString ret(label);
232     ret[idx] = QLatin1Char('_');
233     return ret;
234 }
235 
236 #ifndef QT_NO_SHORTCUT
convertKeySequence(const QKeySequence & sequence)237 QDBusMenuShortcut QDBusMenuItem::convertKeySequence(const QKeySequence &sequence)
238 {
239     QDBusMenuShortcut shortcut;
240     for (int i = 0; i < sequence.count(); ++i) {
241         QStringList tokens;
242         int key = sequence[i];
243         if (key & Qt::MetaModifier)
244             tokens << QStringLiteral("Super");
245         if (key & Qt::ControlModifier)
246             tokens << QStringLiteral("Control");
247         if (key & Qt::AltModifier)
248             tokens << QStringLiteral("Alt");
249         if (key & Qt::ShiftModifier)
250             tokens << QStringLiteral("Shift");
251         if (key & Qt::KeypadModifier)
252             tokens << QStringLiteral("Num");
253 
254         QString keyName = QKeySequencePrivate::keyName(key, QKeySequence::PortableText);
255         if (keyName == QLatin1String("+"))
256             tokens << QStringLiteral("plus");
257         else if (keyName == QLatin1String("-"))
258             tokens << QStringLiteral("minus");
259         else
260             tokens << keyName;
261         shortcut << tokens;
262     }
263     return shortcut;
264 }
265 #endif
266 
operator <<(QDBusArgument & arg,const QDBusMenuEvent & ev)267 const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuEvent &ev)
268 {
269     arg.beginStructure();
270     arg << ev.m_id << ev.m_eventId << ev.m_data << ev.m_timestamp;
271     arg.endStructure();
272     return arg;
273 }
274 
operator >>(const QDBusArgument & arg,QDBusMenuEvent & ev)275 const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuEvent &ev)
276 {
277     arg.beginStructure();
278     arg >> ev.m_id >> ev.m_eventId >> ev.m_data >> ev.m_timestamp;
279     arg.endStructure();
280     return arg;
281 }
282 
283 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug d,const QDBusMenuItem & item)284 QDebug operator<<(QDebug d, const QDBusMenuItem &item)
285 {
286     QDebugStateSaver saver(d);
287     d.nospace();
288     d << "QDBusMenuItem(id=" << item.m_id << ", properties=" << item.m_properties << ')';
289     return d;
290 }
291 
operator <<(QDebug d,const QDBusMenuLayoutItem & item)292 QDebug operator<<(QDebug d, const QDBusMenuLayoutItem &item)
293 {
294     QDebugStateSaver saver(d);
295     d.nospace();
296     d << "QDBusMenuLayoutItem(id=" << item.m_id << ", properties=" << item.m_properties << ", " << item.m_children.count() << " children)";
297     return d;
298 }
299 #endif
300 
301 QT_END_NAMESPACE
302