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
55QT_BEGIN_NAMESPACE
56
57const 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
65const 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
73const 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
81const 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
89uint 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(akey: QLatin1String("children-display"), avalue: QLatin1String("submenu"));
95 if (topLevelMenu)
96 populate(menu: 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
114void 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: depth - 1, propertyNames);
120 m_children << child;
121 }
122}
123
124void 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
135const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuLayoutItem &item)
136{
137 arg.beginStructure();
138 arg << item.m_id << item.m_properties;
139 arg.beginArray(elementMetaTypeId: qMetaTypeId<QDBusVariant>());
140 for (const QDBusMenuLayoutItem &child : item.m_children)
141 arg << QDBusVariant(QVariant::fromValue<QDBusMenuLayoutItem>(value: child));
142 arg.endArray();
143 arg.endStructure();
144 return arg;
145}
146
147const 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>(v: dbusVariant.variant());
156
157 QDBusMenuLayoutItem child;
158 childArgument >> child;
159 item.m_children.append(t: child);
160 }
161 arg.endArray();
162 arg.endStructure();
163 return arg;
164}
165
166void 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
179QDBusMenuItem::QDBusMenuItem(const QDBusPlatformMenuItem *item)
180 : m_id(item->dbusID())
181{
182 if (item->isSeparator()) {
183 m_properties.insert(akey: QLatin1String("type"), avalue: QLatin1String("separator"));
184 } else {
185 m_properties.insert(akey: QLatin1String("label"), avalue: convertMnemonic(label: item->text()));
186 if (item->menu())
187 m_properties.insert(akey: QLatin1String("children-display"), avalue: QLatin1String("submenu"));
188 m_properties.insert(akey: QLatin1String("enabled"), avalue: item->isEnabled());
189 if (item->isCheckable()) {
190 QString toggleType = item->hasExclusiveGroup() ? QLatin1String("radio") : QLatin1String("checkmark");
191 m_properties.insert(akey: QLatin1String("toggle-type"), avalue: toggleType);
192 m_properties.insert(akey: QLatin1String("toggle-state"), avalue: item->isChecked() ? 1 : 0);
193 }
194#ifndef QT_NO_SHORTCUT
195 const QKeySequence &scut = item->shortcut();
196 if (!scut.isEmpty()) {
197 QDBusMenuShortcut shortcut = convertKeySequence(sequence: scut);
198 m_properties.insert(akey: QLatin1String("shortcut"), avalue: QVariant::fromValue(value: shortcut));
199 }
200#endif
201 const QIcon &icon = item->icon();
202 if (!icon.name().isEmpty()) {
203 m_properties.insert(akey: QLatin1String("icon-name"), avalue: icon.name());
204 } else if (!icon.isNull()) {
205 QBuffer buf;
206 icon.pixmap(extent: 16).save(device: &buf, format: "PNG");
207 m_properties.insert(akey: QLatin1String("icon-data"), avalue: buf.data());
208 }
209 }
210 m_properties.insert(akey: QLatin1String("visible"), avalue: item->isVisible());
211}
212
213QDBusMenuItemList 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(asize: items.size());
219 for (const QDBusPlatformMenuItem *item : items)
220 ret << QDBusMenuItem(item);
221 return ret;
222}
223
224QString 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(c: 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
237QDBusMenuShortcut 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, format: 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
267const 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
275const 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
284QDebug 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
292QDebug 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
301QT_END_NAMESPACE
302

source code of qtbase/src/platformsupport/themes/genericunix/dbusmenu/qdbusmenutypes.cpp