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 "qquickaction_p.h"
41#include "qquickexclusivegroup_p.h"
42#include "qquickmenuitem_p.h"
43
44#include <QtGui/qguiapplication.h>
45#include <QtQuick/qquickitem.h>
46#include <QtQuick/qquickwindow.h>
47#include <private/qguiapplication_p.h>
48#include <qqmlfile.h>
49
50QT_BEGIN_NAMESPACE
51
52/*!
53 \qmltype Action
54 \instantiates QQuickAction1
55 \ingroup applicationwindow
56 \ingroup controls
57 \inqmlmodule QtQuick.Controls
58 \brief Action provides an abstract user interface action that can be bound to items.
59
60 \image menubar-action.png
61
62 In applications many common commands can be invoked via menus, toolbar buttons, and keyboard
63 shortcuts. Since the user expects each command to be performed in the same way, regardless of
64 the user interface used, it is useful to represent each command as an \e action.
65
66 An action can be bound to a menu item and a toolbar button, and it will automatically keep them in sync.
67 For example, in a word processor, if the user presses a Bold toolbar button, the Bold menu item will
68 automatically be checked.
69
70 QtQuick Controls supports actions in \l {QtQuick.Controls::}{Button}, \l ToolButton, and \l MenuItem.
71
72 \quotefromfile gallery/main.qml
73 \dots
74 \skipto Action
75 \printto TabView
76 \dots
77*/
78
79/*!
80 \qmlproperty string Action::text
81
82 Text for the action. This text will show as the button text, or
83 as title in a menu item.
84
85 Mnemonics are supported by prefixing the shortcut letter with \&.
86 For instance, \c "\&Open" will bind the \c Alt-O shortcut to the
87 \c "Open" menu item. Note that not all platforms support mnemonics.
88
89 Defaults to an empty string.
90*/
91
92/*!
93 \qmlproperty url Action::iconSource
94
95 Sets the icon file or resource url for the action. Defaults to an empty URL.
96*/
97
98/*!
99 \qmlproperty string Action::iconName
100
101 Sets the icon name for the action. This will pick the icon
102 with the given name from the current theme.
103
104 Defaults to an empty string.
105
106 \include icons.qdocinc iconName
107*/
108
109/*!
110 \qmlproperty string Action::tooltip
111
112 Tooltip to be shown when hovering the control bound to this action.
113 Not all controls support tooltips on all platforms, especially \l MenuItem.
114
115 Defaults to an empty string.
116*/
117
118/*!
119 \qmlproperty bool Action::enabled
120
121 Whether the action is enabled, and can be triggered. Defaults to \c true.
122
123 \sa trigger(), triggered
124*/
125
126/*!
127 \qmlproperty bool Action::checkable
128
129 Whether the menu item can be checked, or toggled. Defaults to \c false.
130
131 \sa checked, exclusiveGroup
132*/
133
134/*!
135 \qmlproperty bool Action::checked
136
137 If the action is \l checkable, this property reflects its checked state. Defaults to \c false.
138 Its value is also false while \l checkable is false.
139
140 \sa toggled, exclusiveGroup
141*/
142
143/*!
144 \qmlproperty ExclusiveGroup Action::exclusiveGroup
145
146 If an action is checkable, an \l ExclusiveGroup can be attached to it.
147 All the actions sharing the same exclusive group become mutually exclusive
148 selectable, meaning that only the last checked action will actually be checked.
149
150 Defaults to \c null, meaning no exclusive behavior is to be expected.
151
152 \sa checkable, checked
153*/
154
155/*!
156 \qmlproperty keysequence Action::shortcut
157
158 Shortcut bound to the action. The keysequence can be a string
159 or a \l {QKeySequence::StandardKey}{standard key}.
160
161 Defaults to an empty string.
162
163 \qml
164 Action {
165 id: copyAction
166 text: qsTr("&Copy")
167 shortcut: StandardKey.Copy
168 }
169 \endqml
170*/
171
172/*! \qmlsignal Action::triggered(QtObject source)
173
174 Emitted when either the menu item or its bound action have been activated. Includes
175 the object (\a source) that triggered the event if relevant (e.g. a Button or MenuItem).
176 You shouldn't need to emit this signal, use \l trigger() instead.
177
178 The corresponding handler is \c onTriggered.
179*/
180
181/*! \qmlmethod void Action::trigger(QtObject source)
182
183 Will emit the \l triggered signal on \a source if the action is enabled. You may provide
184 a source object if the Action would benefit from knowing the origin of the triggering
185 (e.g. for analytics). Will also emit the \l toggled signal if it is checkable.
186*/
187
188/*! \qmlsignal Action::toggled(checked)
189
190 Emitted whenever an action's \a checked property changes.
191 This usually happens at the same time as \l triggered.
192
193 The corresponding handler is \c onToggled.
194
195 \sa checked
196*/
197
198QQuickAction1::QQuickAction1(QObject *parent)
199 : QObject(parent)
200 , m_enabled(true)
201 , m_checkable(false)
202 , m_checked(false)
203{
204}
205
206QQuickAction1::~QQuickAction1()
207{
208 setShortcut(QString());
209 setMnemonicFromText(QString());
210 setExclusiveGroup(0);
211}
212
213void QQuickAction1::setText(const QString &text)
214{
215 if (text == m_text)
216 return;
217 m_text = text;
218 setMnemonicFromText(m_text);
219 emit textChanged();
220}
221
222namespace {
223
224bool qShortcutContextMatcher(QObject *o, Qt::ShortcutContext context)
225{
226 if (!static_cast<QQuickAction1*>(o)->isEnabled())
227 return false;
228
229 switch (context) {
230 case Qt::ApplicationShortcut:
231 return true;
232 case Qt::WindowShortcut: {
233 QObject *w = o;
234 while (w && !w->isWindowType()) {
235 w = w->parent();
236 if (QQuickItem * item = qobject_cast<QQuickItem*>(object: w))
237 w = item->window();
238 }
239 if (w && w == QGuiApplication::focusWindow())
240 return true;
241 }
242 case Qt::WidgetShortcut:
243 case Qt::WidgetWithChildrenShortcut:
244 break;
245 }
246
247 return false;
248}
249
250bool qMnemonicContextMatcher(QObject *o, Qt::ShortcutContext context)
251{
252 if (!static_cast<QQuickAction1*>(o)->isEnabled())
253 return false;
254
255 switch (context) {
256 case Qt::ApplicationShortcut:
257 return true;
258 case Qt::WindowShortcut: {
259 QObject *w = o;
260 while (w && !w->isWindowType()) {
261 w = w->parent();
262 if (QQuickItem * item = qobject_cast<QQuickItem*>(object: w))
263 w = item->window();
264 else if (QQuickMenuBase1 *mb = qobject_cast<QQuickMenuBase1 *>(object: w)) {
265 QQuickItem *vi = mb->visualItem();
266 if (vi && vi->isVisible())
267 w = vi->window();
268 else
269 break; // Non visible menu objects don't get mnemonic match
270 }
271 }
272 if (w && w == QGuiApplication::focusWindow())
273 return true;
274 }
275 case Qt::WidgetShortcut:
276 case Qt::WidgetWithChildrenShortcut:
277 break;
278 }
279
280 return false;
281}
282
283} // namespace
284
285QVariant QQuickAction1::shortcut() const
286{
287#if QT_CONFIG(shortcut)
288 return m_shortcut.toString(format: QKeySequence::NativeText);
289#else
290 return QString();
291#endif
292}
293
294void QQuickAction1::setShortcut(const QVariant &arg)
295{
296#if QT_CONFIG(shortcut)
297 QKeySequence sequence;
298 if (arg.type() == QVariant::Int)
299 sequence = QKeySequence(static_cast<QKeySequence::StandardKey>(arg.toInt()));
300 else
301 sequence = QKeySequence::fromString(str: arg.toString());
302
303 if (sequence == m_shortcut)
304 return;
305
306 if (!m_shortcut.isEmpty())
307 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id: 0, owner: this, key: m_shortcut);
308
309 m_shortcut = sequence;
310
311 if (!m_shortcut.isEmpty()) {
312 Qt::ShortcutContext context = Qt::WindowShortcut;
313 QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(owner: this, key: m_shortcut, context, matcher: qShortcutContextMatcher);
314 }
315 emit shortcutChanged(shortcut: shortcut());
316#endif // QT_CONFIG(shortcut)
317}
318
319void QQuickAction1::setMnemonicFromText(const QString &text)
320{
321#if QT_CONFIG(shortcut)
322 QKeySequence sequence = QKeySequence::mnemonic(text);
323 if (m_mnemonic == sequence)
324 return;
325
326 if (!m_mnemonic.isEmpty())
327 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id: 0, owner: this, key: m_mnemonic);
328
329 m_mnemonic = sequence;
330
331 if (!m_mnemonic.isEmpty()) {
332 Qt::ShortcutContext context = Qt::WindowShortcut;
333 QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(owner: this, key: m_mnemonic, context, matcher: qMnemonicContextMatcher);
334 }
335#endif // QT_CONFIG(shortcut)
336}
337
338void QQuickAction1::setIconSource(const QUrl &iconSource)
339{
340 if (iconSource == m_iconSource)
341 return;
342
343 m_iconSource = iconSource;
344 if (m_iconName.isEmpty() || m_icon.isNull()) {
345 QString fileString = QQmlFile::urlToLocalFileOrQrc(iconSource);
346 m_icon = QIcon(fileString);
347
348 emit iconChanged();
349 }
350 emit iconSourceChanged();
351}
352
353QString QQuickAction1::iconName() const
354{
355 return m_iconName;
356}
357
358void QQuickAction1::setIconName(const QString &iconName)
359{
360 if (iconName == m_iconName)
361 return;
362 m_iconName = iconName;
363 m_icon = QIcon::fromTheme(name: m_iconName, fallback: QIcon(QQmlFile::urlToLocalFileOrQrc(m_iconSource)));
364 emit iconNameChanged();
365 emit iconChanged();
366}
367
368void QQuickAction1::setTooltip(const QString &arg)
369{
370 if (m_tooltip != arg) {
371 m_tooltip = arg;
372 emit tooltipChanged(arg);
373 }
374}
375
376void QQuickAction1::setEnabled(bool e)
377{
378 if (e == m_enabled)
379 return;
380 m_enabled = e;
381
382 emit enabledChanged();
383}
384
385void QQuickAction1::setCheckable(bool c)
386{
387 if (c == m_checkable)
388 return;
389 m_checkable = c;
390 emit checkableChanged();
391
392 // Setting checkable to false will show checked as false, while setting checkable to
393 // true will reveal checked's value again. If checked's internal value is true, we send
394 // the signal to notify its visible value.
395 if (m_checked)
396 emit toggled(checked: m_checkable);
397}
398
399void QQuickAction1::setChecked(bool c)
400{
401 if (c == m_checked)
402 return;
403 m_checked = c;
404
405 // Its value shows as false while checkable is false. See also comment in setCheckable().
406 if (m_checkable)
407 emit toggled(checked: m_checked);
408}
409
410QQuickExclusiveGroup1 *QQuickAction1::exclusiveGroup() const
411{
412 return m_exclusiveGroup.data();
413}
414
415void QQuickAction1::setExclusiveGroup(QQuickExclusiveGroup1 *eg)
416{
417 if (m_exclusiveGroup == eg)
418 return;
419
420 if (m_exclusiveGroup)
421 m_exclusiveGroup->unbindCheckable(o: this);
422 m_exclusiveGroup = eg;
423 if (m_exclusiveGroup)
424 m_exclusiveGroup->bindCheckable(o: this);
425
426 emit exclusiveGroupChanged();
427}
428
429bool QQuickAction1::event(QEvent *e)
430{
431#if QT_CONFIG(shortcut)
432 if (!m_enabled)
433 return false;
434
435 if (e->type() != QEvent::Shortcut)
436 return false;
437
438 QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
439
440 Q_ASSERT_X(se->key() == m_shortcut || se->key() == m_mnemonic,
441 "QQuickAction::event",
442 "Received shortcut event from incorrect shortcut");
443 if (se->isAmbiguous()) {
444 qWarning(msg: "QQuickAction::event: Ambiguous shortcut overload: %s", se->key().toString(format: QKeySequence::NativeText).toLatin1().constData());
445 return false;
446 }
447
448 trigger();
449
450 return true;
451#else
452 return false;
453#endif // QT_CONFIG(shortcut)
454}
455
456void QQuickAction1::trigger(QObject *source)
457{
458 if (!m_enabled)
459 return;
460
461 if (m_checkable && !(m_checked && m_exclusiveGroup))
462 setChecked(!m_checked);
463
464 emit triggered(source);
465}
466
467QT_END_NAMESPACE
468

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