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 Qt Quick Controls 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 "qquickmenuitem_p.h"
41#include "qquickaction_p.h"
42#include "qquickmenu_p.h"
43#include "qquickmenuitemcontainer_p.h"
44
45#include <private/qguiapplication_p.h>
46#include <QtGui/qpa/qplatformtheme.h>
47#include <QtGui/qpa/qplatformmenu.h>
48#include <qquickitem.h>
49
50QT_BEGIN_NAMESPACE
51
52QQuickMenuBase1::QQuickMenuBase1(QObject *parent, int type)
53 : QObject(parent), m_visible(true), m_type(static_cast<QQuickMenuItemType1::MenuItemType>(type))
54 , m_parentMenu(0), m_container(0), m_platformItem(0), m_visualItem(0)
55{
56 if (type >= 0 && QGuiApplication::platformName() != QStringLiteral("xcb")) { // QTBUG-51372)
57 m_platformItem = QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem();
58 if (m_platformItem)
59 m_platformItem->setRole(QPlatformMenuItem::TextHeuristicRole);
60 }
61}
62
63QQuickMenuBase1::~QQuickMenuBase1()
64{
65 if (parentMenu())
66 parentMenu()->removeItem(this);
67 setParentMenu(0);
68 if (m_platformItem) {
69 delete m_platformItem;
70 m_platformItem = 0;
71 }
72}
73
74void QQuickMenuBase1::setVisible(bool v)
75{
76 if (v != m_visible) {
77 m_visible = v;
78
79 if (m_platformItem) {
80 m_platformItem->setVisible(m_visible);
81 syncWithPlatformMenu();
82 }
83
84 emit visibleChanged();
85 }
86}
87
88QObject *QQuickMenuBase1::parentMenuOrMenuBar() const
89{
90 if (!m_parentMenu)
91 return parent();
92 return m_parentMenu;
93}
94
95QQuickMenu1 *QQuickMenuBase1::parentMenu() const
96{
97 return m_parentMenu;
98}
99
100void QQuickMenuBase1::setParentMenu(QQuickMenu1 *parentMenu)
101{
102 if (m_platformItem && m_parentMenu && m_parentMenu->platformMenu())
103 m_parentMenu->platformMenu()->removeMenuItem(menuItem: m_platformItem);
104
105 m_parentMenu = parentMenu;
106}
107
108QQuickMenuItemContainer1 *QQuickMenuBase1::container() const
109{
110 return m_container;
111}
112
113void QQuickMenuBase1::setContainer(QQuickMenuItemContainer1 *c)
114{
115 m_container = c;
116}
117
118void QQuickMenuBase1::syncWithPlatformMenu()
119{
120 QQuickMenu1 *menu = parentMenu();
121 if (menu && menu->platformMenu() && platformItem()
122 && menu->contains(this)) // If not, it'll be added later and then sync'ed
123 menu->platformMenu()->syncMenuItem(menuItem: platformItem());
124}
125
126QQuickItem *QQuickMenuBase1::visualItem() const
127{
128 return m_visualItem;
129}
130
131void QQuickMenuBase1::setVisualItem(QQuickItem *item)
132{
133 m_visualItem = item;
134}
135
136/*!
137 \qmltype MenuSeparator
138 \instantiates QQuickMenuSeparator1
139 \inqmlmodule QtQuick.Controls
140 \ingroup menus
141 \ingroup controls
142 \brief MenuSeparator provides a separator for items inside a menu.
143
144 \image menu.png
145
146 \code
147 Menu {
148 text: "Edit"
149
150 MenuItem {
151 text: "Cut"
152 shortcut: "Ctrl+X"
153 onTriggered: ...
154 }
155
156 MenuItem {
157 text: "Copy"
158 shortcut: "Ctrl+C"
159 onTriggered: ...
160 }
161
162 MenuItem {
163 text: "Paste"
164 shortcut: "Ctrl+V"
165 onTriggered: ...
166 }
167 MenuSeparator { }
168 MenuItem {
169 text: "Do Nothing"
170 shortcut: "Ctrl+E,Shift+Ctrl+X"
171 enabled: false
172 }
173 }
174 \endcode
175
176 \sa Menu, MenuItem
177*/
178
179/*!
180 \qmlproperty bool MenuSeparator::visible
181
182 Whether the menu separator should be visible.
183*/
184
185/*!
186 \qmlproperty enumeration MenuSeparator::type
187 \readonly
188
189 This property is read-only and constant, and its value is \c MenuItemType.Separator.
190*/
191
192QQuickMenuSeparator1::QQuickMenuSeparator1(QObject *parent)
193 : QQuickMenuBase1(parent, QQuickMenuItemType1::Separator)
194{
195 if (platformItem())
196 platformItem()->setIsSeparator(true);
197}
198
199QQuickMenuText1::QQuickMenuText1(QObject *parent, QQuickMenuItemType1::MenuItemType type)
200 : QQuickMenuBase1(parent, type), m_action(new QQuickAction1(this))
201{
202 connect(sender: m_action, SIGNAL(enabledChanged()), receiver: this, SLOT(updateEnabled()));
203 connect(sender: m_action, SIGNAL(textChanged()), receiver: this, SLOT(updateText()));
204 connect(sender: m_action, SIGNAL(iconNameChanged()), receiver: this, SLOT(updateIcon()));
205 connect(sender: m_action, SIGNAL(iconNameChanged()), receiver: this, SIGNAL(iconNameChanged()));
206 connect(sender: m_action, SIGNAL(iconSourceChanged()), receiver: this, SLOT(updateIcon()));
207 connect(sender: m_action, SIGNAL(iconSourceChanged()), receiver: this, SIGNAL(iconSourceChanged()));
208}
209
210QQuickMenuText1::~QQuickMenuText1()
211{
212 delete m_action;
213}
214
215bool QQuickMenuText1::enabled() const
216{
217 return action()->isEnabled();
218}
219
220void QQuickMenuText1::setEnabled(bool e)
221{
222 action()->setEnabled(e);
223}
224
225QString QQuickMenuText1::text() const
226{
227 return m_action->text();
228}
229
230void QQuickMenuText1::setText(const QString &t)
231{
232 m_action->setText(t);
233}
234
235QUrl QQuickMenuText1::iconSource() const
236{
237 return m_action->iconSource();
238}
239
240void QQuickMenuText1::setIconSource(const QUrl &iconSource)
241{
242 m_action->setIconSource(iconSource);
243}
244
245QString QQuickMenuText1::iconName() const
246{
247 return m_action->iconName();
248}
249
250void QQuickMenuText1::setIconName(const QString &iconName)
251{
252 m_action->setIconName(iconName);
253}
254
255QIcon QQuickMenuText1::icon() const
256{
257 return m_action->icon();
258}
259
260void QQuickMenuText1::updateText()
261{
262 if (platformItem()) {
263 platformItem()->setText(text());
264 syncWithPlatformMenu();
265 }
266 emit __textChanged();
267}
268
269void QQuickMenuText1::updateEnabled()
270{
271 if (platformItem()) {
272 platformItem()->setEnabled(enabled());
273 syncWithPlatformMenu();
274 }
275 emit enabledChanged();
276}
277
278void QQuickMenuText1::updateIcon()
279{
280 if (platformItem()) {
281 platformItem()->setIcon(icon());
282 syncWithPlatformMenu();
283 }
284 emit __iconChanged();
285}
286
287/*!
288 \qmltype MenuItem
289 \instantiates QQuickMenuItem1
290 \ingroup menus
291 \ingroup controls
292 \inqmlmodule QtQuick.Controls
293 \brief MenuItem provides an item to add in a menu or a menu bar.
294
295 \image menu.png
296
297 \code
298 Menu {
299 text: "Edit"
300
301 MenuItem {
302 text: "Cut"
303 shortcut: "Ctrl+X"
304 onTriggered: ...
305 }
306
307 MenuItem {
308 text: "Copy"
309 shortcut: "Ctrl+C"
310 onTriggered: ...
311 }
312
313 MenuItem {
314 text: "Paste"
315 shortcut: "Ctrl+V"
316 onTriggered: ...
317 }
318 }
319 \endcode
320
321 \sa MenuBar, Menu, MenuSeparator, Action
322*/
323
324/*!
325 \qmlproperty bool MenuItem::visible
326
327 Whether the menu item should be visible. Defaults to \c true.
328*/
329
330/*!
331 \qmlproperty enumeration MenuItem::type
332 \readonly
333
334 This property is read-only and constant, and its value is \c MenuItemType.Item.
335*/
336
337/*!
338 \qmlproperty string MenuItem::text
339
340 Text for the menu item. Overrides the item's bound action \c text property.
341
342 Mnemonics are supported by prefixing the shortcut letter with \&.
343 For instance, \c "\&Open" will bind the \c Alt-O shortcut to the
344 \c "Open" menu item. Note that not all platforms support mnemonics.
345
346 Defaults to an empty string.
347
348 \sa Action::text
349*/
350
351/*!
352 \qmlproperty bool MenuItem::enabled
353
354 Whether the menu item is enabled, and responsive to user interaction. Defaults to \c true.
355*/
356
357/*!
358 \qmlproperty url MenuItem::iconSource
359
360 Sets the icon file or resource url for the \l MenuItem icon.
361 Overrides the item's bound action \c iconSource property. Defaults to an empty URL.
362
363 \sa iconName, Action::iconSource
364*/
365
366/*!
367 \qmlproperty string MenuItem::iconName
368
369 Sets the icon name for the \l MenuItem icon. This will pick the icon
370 with the given name from the current theme. Overrides the item's bound
371 action \c iconName property. Defaults to an empty string.
372
373 \include icons.qdocinc iconName
374
375 \sa iconSource, Action::iconName
376*/
377
378/*! \qmlsignal MenuItem::triggered()
379
380 Emitted when either the menu item or its bound action have been activated.
381
382 \sa trigger(), Action::triggered, Action::toggled
383
384 The corresponding handler is \c onTriggered.
385*/
386
387/*! \qmlmethod void MenuItem::trigger()
388
389 Manually trigger a menu item. Will also trigger the item's bound action.
390
391 \sa triggered, Action::trigger()
392*/
393
394/*!
395 \qmlproperty keysequence MenuItem::shortcut
396
397 Shortcut bound to the menu item. The keysequence can be a string
398 or a \l {QKeySequence::StandardKey}{standard key}.
399
400 Defaults to an empty string.
401
402 \qml
403 MenuItem {
404 id: copyItem
405 text: qsTr("&Copy")
406 shortcut: StandardKey.Copy
407 }
408 \endqml
409
410 \sa Action::shortcut
411*/
412
413/*!
414 \qmlproperty bool MenuItem::checkable
415
416 Whether the menu item can be checked, or toggled. Defaults to \c false.
417
418 \sa checked
419*/
420
421/*!
422 \qmlproperty bool MenuItem::checked
423
424 If the menu item is checkable, this property reflects its checked state. Defaults to \c false.
425
426 \sa checkable, Action::toggled
427*/
428
429/*! \qmlproperty ExclusiveGroup MenuItem::exclusiveGroup
430
431 If a menu item is checkable, an \l ExclusiveGroup can be attached to it.
432 All the menu items sharing the same exclusive group, and by extension, any \l Action sharing it,
433 become mutually exclusive selectable, meaning that only the last checked menu item will
434 actually be checked.
435
436 Defaults to \c null, meaning no exclusive behavior is to be expected.
437
438 \sa checked, checkable
439*/
440
441/*! \qmlsignal MenuItem::toggled(checked)
442
443 Emitted whenever a menu item's \a checked property changes.
444 This usually happens at the same time as \l triggered.
445
446 \sa checked, triggered, Action::triggered, Action::toggled
447
448 The corresponding handler is \c onToggled.
449
450 \sa checked
451*/
452
453/*!
454 \qmlproperty Action MenuItem::action
455
456 The action bound to this menu item. It will provide values for all the properties of the menu item.
457 However, it is possible to override the action's \c text, \c iconSource, and \c iconName
458 properties by just assigning these properties, allowing some customization.
459
460 In addition, the menu item \c triggered() and \c toggled() signals will not be emitted.
461 Instead, the action \c triggered() and \c toggled() signals will be.
462
463 Defaults to \c null, meaning no action is bound to the menu item.
464*/
465
466QQuickMenuItem1::QQuickMenuItem1(QObject *parent)
467 : QQuickMenuText1(parent, QQuickMenuItemType1::Item), m_boundAction(0)
468{
469 connect(sender: this, SIGNAL(__textChanged()), receiver: this, SIGNAL(textChanged()));
470
471 connect(sender: action(), SIGNAL(shortcutChanged(QVariant)), receiver: this, SLOT(updateShortcut()));
472 connect(sender: action(), SIGNAL(triggered()), receiver: this, SIGNAL(triggered()));
473 connect(sender: action(), SIGNAL(checkableChanged()), receiver: this, SLOT(updateCheckable()));
474 connect(sender: action(), SIGNAL(toggled(bool)), receiver: this, SLOT(updateChecked()));
475 if (platformItem())
476 connect(sender: platformItem(), SIGNAL(activated()), receiver: this, SLOT(trigger()), Qt::QueuedConnection);
477}
478
479QQuickMenuItem1::~QQuickMenuItem1()
480{
481 unbindFromAction(action: m_boundAction);
482 if (platformItem())
483 disconnect(sender: platformItem(), SIGNAL(activated()), receiver: this, SLOT(trigger()));
484}
485
486void QQuickMenuItem1::setParentMenu(QQuickMenu1 *parentMenu)
487{
488 QQuickMenuText1::setParentMenu(parentMenu);
489 if (parentMenu)
490 connect(sender: this, SIGNAL(triggered()), receiver: parentMenu, SLOT(updateSelectedIndex()));
491}
492
493void QQuickMenuItem1::bindToAction(QQuickAction1 *action)
494{
495 m_boundAction = action;
496
497 connect(sender: m_boundAction, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(unbindFromAction(QObject*)));
498
499 connect(sender: m_boundAction, SIGNAL(triggered()), receiver: this, SIGNAL(triggered()));
500 connect(sender: m_boundAction, SIGNAL(toggled(bool)), receiver: this, SLOT(updateChecked()));
501 connect(sender: m_boundAction, SIGNAL(exclusiveGroupChanged()), receiver: this, SIGNAL(exclusiveGroupChanged()));
502 connect(sender: m_boundAction, SIGNAL(enabledChanged()), receiver: this, SLOT(updateEnabled()));
503 connect(sender: m_boundAction, SIGNAL(textChanged()), receiver: this, SLOT(updateText()));
504 connect(sender: m_boundAction, SIGNAL(shortcutChanged(QVariant)), receiver: this, SLOT(updateShortcut()));
505 connect(sender: m_boundAction, SIGNAL(checkableChanged()), receiver: this, SLOT(updateCheckable()));
506 connect(sender: m_boundAction, SIGNAL(iconNameChanged()), receiver: this, SLOT(updateIcon()));
507 connect(sender: m_boundAction, SIGNAL(iconNameChanged()), receiver: this, SIGNAL(iconNameChanged()));
508 connect(sender: m_boundAction, SIGNAL(iconSourceChanged()), receiver: this, SLOT(updateIcon()));
509 connect(sender: m_boundAction, SIGNAL(iconSourceChanged()), receiver: this, SIGNAL(iconSourceChanged()));
510
511 if (m_boundAction->parent() != this) {
512 updateText();
513 updateShortcut();
514 updateEnabled();
515 updateIcon();
516 if (checkable())
517 updateChecked();
518 }
519}
520
521void QQuickMenuItem1::unbindFromAction(QObject *o)
522{
523 if (!o)
524 return;
525
526 if (o == m_boundAction)
527 m_boundAction = 0;
528
529 QQuickAction1 *action = qobject_cast<QQuickAction1 *>(object: o);
530 if (!action)
531 return;
532
533 disconnect(sender: action, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(unbindFromAction(QObject*)));
534
535 disconnect(sender: action, SIGNAL(triggered()), receiver: this, SIGNAL(triggered()));
536 disconnect(sender: action, SIGNAL(toggled(bool)), receiver: this, SLOT(updateChecked()));
537 disconnect(sender: action, SIGNAL(exclusiveGroupChanged()), receiver: this, SIGNAL(exclusiveGroupChanged()));
538 disconnect(sender: action, SIGNAL(enabledChanged()), receiver: this, SLOT(updateEnabled()));
539 disconnect(sender: action, SIGNAL(textChanged()), receiver: this, SLOT(updateText()));
540 disconnect(sender: action, SIGNAL(shortcutChanged(QVariant)), receiver: this, SLOT(updateShortcut()));
541 disconnect(sender: action, SIGNAL(checkableChanged()), receiver: this, SLOT(updateCheckable()));
542 disconnect(sender: action, SIGNAL(iconNameChanged()), receiver: this, SLOT(updateIcon()));
543 disconnect(sender: action, SIGNAL(iconNameChanged()), receiver: this, SIGNAL(iconNameChanged()));
544 disconnect(sender: action, SIGNAL(iconSourceChanged()), receiver: this, SLOT(updateIcon()));
545 disconnect(sender: action, SIGNAL(iconSourceChanged()), receiver: this, SIGNAL(iconSourceChanged()));
546}
547
548QQuickAction1 *QQuickMenuItem1::action() const
549{
550 if (m_boundAction)
551 return m_boundAction;
552 return QQuickMenuText1::action();
553}
554
555void QQuickMenuItem1::setBoundAction(QQuickAction1 *a)
556{
557 if (a == m_boundAction)
558 return;
559
560 unbindFromAction(o: m_boundAction);
561
562 bindToAction(action: a);
563 emit actionChanged();
564}
565
566QString QQuickMenuItem1::text() const
567{
568 QString ownText = QQuickMenuText1::text();
569 if (!ownText.isNull())
570 return ownText;
571 return m_boundAction ? m_boundAction->text() : QString();
572}
573
574QUrl QQuickMenuItem1::iconSource() const
575{
576 QUrl ownIconSource = QQuickMenuText1::iconSource();
577 if (!ownIconSource.isEmpty())
578 return ownIconSource;
579 return m_boundAction ? m_boundAction->iconSource() : QUrl();
580}
581
582QString QQuickMenuItem1::iconName() const
583{
584 QString ownIconName = QQuickMenuText1::iconName();
585 if (!ownIconName.isEmpty())
586 return ownIconName;
587 return m_boundAction ? m_boundAction->iconName() : QString();
588}
589
590QIcon QQuickMenuItem1::icon() const
591{
592 QIcon ownIcon = QQuickMenuText1::icon();
593 if (!ownIcon.isNull())
594 return ownIcon;
595 return m_boundAction ? m_boundAction->icon() : QIcon();
596}
597
598QVariant QQuickMenuItem1::shortcut() const
599{
600 return action()->shortcut();
601}
602
603void QQuickMenuItem1::setShortcut(const QVariant &shortcut)
604{
605 if (!m_boundAction)
606 action()->setShortcut(shortcut);
607}
608
609void QQuickMenuItem1::updateShortcut()
610{
611#if QT_CONFIG(shortcut)
612 if (platformItem()) {
613 QKeySequence sequence;
614 QVariant var = shortcut();
615 if (var.type() == QVariant::Int)
616 sequence = QKeySequence(static_cast<QKeySequence::StandardKey>(var.toInt()));
617 else
618 sequence = QKeySequence::fromString(str: var.toString(), format: QKeySequence::NativeText);
619 platformItem()->setShortcut(sequence);
620 syncWithPlatformMenu();
621 }
622 emit shortcutChanged();
623#endif // QT_CONFIG(shortcut)
624}
625
626bool QQuickMenuItem1::checkable() const
627{
628 return action()->isCheckable();
629}
630
631void QQuickMenuItem1::setCheckable(bool checkable)
632{
633 if (!m_boundAction)
634 action()->setCheckable(checkable);
635}
636
637void QQuickMenuItem1::updateCheckable()
638{
639 if (platformItem()) {
640 platformItem()->setCheckable(checkable());
641 syncWithPlatformMenu();
642 }
643
644 emit checkableChanged();
645}
646
647bool QQuickMenuItem1::checked() const
648{
649 return action()->isChecked();
650}
651
652void QQuickMenuItem1::setChecked(bool checked)
653{
654 if (!m_boundAction)
655 action()->setChecked(checked);
656}
657
658void QQuickMenuItem1::updateChecked()
659{
660 bool checked = this->checked();
661 if (platformItem()) {
662 platformItem()->setChecked(checked);
663 syncWithPlatformMenu();
664 }
665
666 emit toggled(checked);
667}
668
669QQuickExclusiveGroup1 *QQuickMenuItem1::exclusiveGroup() const
670{
671 return action()->exclusiveGroup();
672}
673
674void QQuickMenuItem1::setExclusiveGroup(QQuickExclusiveGroup1 *eg)
675{
676 if (!m_boundAction)
677 action()->setExclusiveGroup(eg);
678}
679
680void QQuickMenuItem1::setEnabled(bool enabled)
681{
682 if (!m_boundAction)
683 action()->setEnabled(enabled);
684}
685
686void QQuickMenuItem1::trigger()
687{
688 QPointer<QQuickMenu1> menu(parentMenu());
689 if (menu)
690 menu->prepareItemTrigger(this);
691 action()->trigger(source: this);
692 if (menu)
693 menu->concludeItemTrigger(this);
694}
695
696QT_END_NAMESPACE
697

source code of qtquickcontrols/src/controls/qquickmenuitem.cpp