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