1/****************************************************************************
2**
3** Copyright (C) 2019 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 "qshortcut.h"
41#include "qshortcut_p.h"
42
43#include <qevent.h>
44#include <qguiapplication.h>
45#include <qwindow.h>
46#include <private/qguiapplication_p.h>
47#include <qpa/qplatformmenu.h>
48
49QT_BEGIN_NAMESPACE
50
51#define QAPP_CHECK(functionName) \
52 if (Q_UNLIKELY(!qApp)) { \
53 qWarning("QShortcut: Initialize QGuiApplication before calling '" functionName "'."); \
54 return; \
55 }
56
57/*!
58 \class QShortcut
59 \brief The QShortcut class is used to create keyboard shortcuts.
60
61 \ingroup events
62 \inmodule QtGui
63
64 The QShortcut class provides a way of connecting keyboard
65 shortcuts to Qt's \l{signals and slots} mechanism, so that
66 objects can be informed when a shortcut is executed. The shortcut
67 can be set up to contain all the key presses necessary to
68 describe a keyboard shortcut, including the states of modifier
69 keys such as \uicontrol Shift, \uicontrol Ctrl, and \uicontrol Alt.
70
71 \target mnemonic
72
73 In widget applications, certain widgets can use '&' in front of a character.
74 This will automatically create a mnemonic (a shortcut) for that character,
75 e.g. "E&xit" will create the shortcut \uicontrol Alt+X (use '&&'
76 to display an actual ampersand). The widget might consume and perform
77 an action on a given shortcut. On X11 the ampersand will not be
78 shown and the character will be underlined. On Windows, shortcuts
79 are normally not displayed until the user presses the \uicontrol Alt
80 key, but this is a setting the user can change. On Mac, shortcuts
81 are disabled by default. Call \l qt_set_sequence_auto_mnemonic() to
82 enable them. However, because mnemonic shortcuts do not fit in
83 with Aqua's guidelines, Qt will not show the shortcut character
84 underlined.
85
86 For applications that use menus, it may be more convenient to
87 use the convenience functions provided in the QMenu class to
88 assign keyboard shortcuts to menu items as they are created.
89 Alternatively, shortcuts may be associated with other types of
90 actions in the QAction class.
91
92 The simplest way to create a shortcut for a particular widget is
93 to construct the shortcut with a key sequence. For example:
94
95 \snippet code/src_gui_kernel_qshortcut.cpp 0
96
97 When the user types the \l{QKeySequence}{key sequence}
98 for a given shortcut, the shortcut's activated() signal is
99 emitted. (In the case of ambiguity, the activatedAmbiguously()
100 signal is emitted.) A shortcut is "listened for" by Qt's event
101 loop when the shortcut's parent widget is receiving events.
102
103 A shortcut's key sequence can be set with setKey() and retrieved
104 with key(). A shortcut can be enabled or disabled with
105 setEnabled(), and can have "What's This?" help text set with
106 setWhatsThis().
107
108 \sa QShortcutEvent, QKeySequence, QAction
109*/
110
111/*!
112 \fn void QShortcut::activated()
113
114 This signal is emitted when the user types the shortcut's key
115 sequence.
116
117 \sa activatedAmbiguously()
118*/
119
120/*!
121 \fn void QShortcut::activatedAmbiguously()
122
123 When a key sequence is being typed at the keyboard, it is said to
124 be ambiguous as long as it matches the start of more than one
125 shortcut.
126
127 When a shortcut's key sequence is completed,
128 activatedAmbiguously() is emitted if the key sequence is still
129 ambiguous (i.e., it is the start of one or more other shortcuts).
130 The activated() signal is not emitted in this case.
131
132 \sa activated()
133*/
134
135static bool simpleContextMatcher(QObject *object, Qt::ShortcutContext context)
136{
137 auto guiShortcut = qobject_cast<QShortcut *>(object);
138 if (QGuiApplication::applicationState() != Qt::ApplicationActive || guiShortcut == nullptr)
139 return false;
140 if (context == Qt::ApplicationShortcut)
141 return true;
142 auto focusWindow = QGuiApplication::focusWindow();
143 if (!focusWindow)
144 return false;
145 auto window = qobject_cast<const QWindow *>(guiShortcut->parent());
146 if (!window)
147 return false;
148 if (focusWindow == window && focusWindow->isTopLevel())
149 return context == Qt::WindowShortcut || context == Qt::WidgetWithChildrenShortcut;
150 return focusWindow->isAncestorOf(window, QWindow::ExcludeTransients);
151}
152
153QShortcutMap::ContextMatcher QShortcutPrivate::contextMatcher() const
154{
155 return simpleContextMatcher;
156}
157
158void QShortcutPrivate::redoGrab(QShortcutMap &map)
159{
160 Q_Q(QShortcut);
161 if (Q_UNLIKELY(!parent)) {
162 qWarning("QShortcut: No window parent defined");
163 return;
164 }
165
166 if (sc_id)
167 map.removeShortcut(sc_id, q);
168 if (sc_sequence.isEmpty())
169 return;
170 sc_id = map.addShortcut(q, sc_sequence, sc_context, contextMatcher());
171 if (!sc_enabled)
172 map.setShortcutEnabled(false, sc_id, q);
173 if (!sc_autorepeat)
174 map.setShortcutAutoRepeat(false, sc_id, q);
175}
176
177QShortcutPrivate *QGuiApplicationPrivate::createShortcutPrivate() const
178{
179 return new QShortcutPrivate;
180}
181
182/*!
183 Constructs a QShortcut object for the \a parent, which should be a
184 QWindow or a QWidget.
185
186 Since no shortcut key sequence is specified, the shortcut will not emit any
187 signals.
188
189 \sa setKey()
190*/
191QShortcut::QShortcut(QObject *parent)
192 : QObject(*QGuiApplicationPrivate::instance()->createShortcutPrivate(), parent)
193{
194 Q_ASSERT(parent != nullptr);
195}
196
197/*!
198 Constructs a QShortcut object for the \a parent, which should be a
199 QWindow or a QWidget.
200
201 The shortcut operates on its parent, listening for \l{QShortcutEvent}s that
202 match the \a key sequence. Depending on the ambiguity of the event, the
203 shortcut will call the \a member function, or the \a ambiguousMember function,
204 if the key press was in the shortcut's \a context.
205*/
206QShortcut::QShortcut(const QKeySequence &key, QObject *parent,
207 const char *member, const char *ambiguousMember,
208 Qt::ShortcutContext context)
209 : QShortcut(parent)
210{
211 Q_D(QShortcut);
212 d->sc_context = context;
213 d->sc_sequence = key;
214 d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap);
215 if (member)
216 connect(this, SIGNAL(activated()), parent, member);
217 if (ambiguousMember)
218 connect(this, SIGNAL(activatedAmbiguously()), parent, ambiguousMember);
219}
220
221
222/*!
223 \fn template<typename Functor> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
224 \since 5.15
225 \overload
226
227 This is a QShortcut convenience constructor which connects the shortcut's
228 \l{QShortcut::activated()}{activated()} signal to the \a functor.
229*/
230/*!
231 \fn template<typename Functor> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, const QObject *context, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
232 \since 5.15
233 \overload
234
235 This is a QShortcut convenience constructor which connects the shortcut's
236 \l{QShortcut::activated()}{activated()} signal to the \a functor.
237
238 The \a functor can be a pointer to a member function of the \a context object.
239
240 If the \a context object is destroyed, the \a functor will not be called.
241*/
242/*!
243 \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, const QObject *context, Functor functor, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
244 \since 5.15
245 \overload
246
247 This is a QShortcut convenience constructor which connects the shortcut's
248 \l{QShortcut::activated()}{activated()} signal to the \a functor and
249 \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()}
250 signal to the \a functorAmbiguous.
251
252 The \a functor and \a functorAmbiguous can be a pointer to a member
253 function of the \a context object.
254
255 If the \a context object is destroyed, the \a functor and
256 \a functorAmbiguous will not be called.
257*/
258/*!
259 \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(const QKeySequence &key, QObject *parent, const QObject *context1, Functor functor, const QObject *context2, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
260 \since 5.15
261 \overload
262
263 This is a QShortcut convenience constructor which connects the shortcut's
264 \l{QShortcut::activated()}{activated()} signal to the \a functor and
265 \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()}
266 signal to the \a functorAmbiguous.
267
268 The \a functor can be a pointer to a member function of the
269 \a context1 object.
270 The \a functorAmbiguous can be a pointer to a member function of the
271 \a context2 object.
272
273 If the \a context1 object is destroyed, the \a functor will not be called.
274 If the \a context2 object is destroyed, the \a functorAmbiguous
275 will not be called.
276*/
277
278/*!
279 Destroys the shortcut.
280*/
281QShortcut::~QShortcut()
282{
283 Q_D(QShortcut);
284 if (qApp)
285 QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(d->sc_id, this);
286}
287
288/*!
289 \property QShortcut::key
290 \brief the shortcut's key sequence
291
292 This is a key sequence with an optional combination of Shift, Ctrl,
293 and Alt. The key sequence may be supplied in a number of ways:
294
295 \snippet code/src_gui_kernel_qshortcut.cpp 1
296
297 By default, this property contains an empty key sequence.
298*/
299void QShortcut::setKey(const QKeySequence &key)
300{
301 Q_D(QShortcut);
302 if (d->sc_sequence == key)
303 return;
304 QAPP_CHECK("setKey");
305 d->sc_sequence = key;
306 d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap);
307}
308
309QKeySequence QShortcut::key() const
310{
311 Q_D(const QShortcut);
312 return d->sc_sequence;
313}
314
315/*!
316 \property QShortcut::enabled
317 \brief whether the shortcut is enabled
318
319 An enabled shortcut emits the activated() or activatedAmbiguously()
320 signal when a QShortcutEvent occurs that matches the shortcut's
321 key() sequence.
322
323 If the application is in \c WhatsThis mode the shortcut will not emit
324 the signals, but will show the "What's This?" text instead.
325
326 By default, this property is \c true.
327
328 \sa whatsThis
329*/
330void QShortcut::setEnabled(bool enable)
331{
332 Q_D(QShortcut);
333 if (d->sc_enabled == enable)
334 return;
335 QAPP_CHECK("setEnabled");
336 d->sc_enabled = enable;
337 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enable, d->sc_id, this);
338}
339
340bool QShortcut::isEnabled() const
341{
342 Q_D(const QShortcut);
343 return d->sc_enabled;
344}
345
346/*!
347 \property QShortcut::context
348 \brief the context in which the shortcut is valid
349
350 A shortcut's context decides in which circumstances a shortcut is
351 allowed to be triggered. The normal context is Qt::WindowShortcut,
352 which allows the shortcut to trigger if the parent (the widget
353 containing the shortcut) is a subwidget of the active top-level
354 window.
355
356 By default, this property is set to Qt::WindowShortcut.
357*/
358void QShortcut::setContext(Qt::ShortcutContext context)
359{
360 Q_D(QShortcut);
361 if (d->sc_context == context)
362 return;
363 QAPP_CHECK("setContext");
364 d->sc_context = context;
365 d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap);
366}
367
368Qt::ShortcutContext QShortcut::context() const
369{
370 Q_D(const QShortcut);
371 return d->sc_context;
372}
373
374/*!
375 \property QShortcut::autoRepeat
376 \brief whether the shortcut can auto repeat
377
378 If true, the shortcut will auto repeat when the keyboard shortcut
379 combination is held down, provided that keyboard auto repeat is
380 enabled on the system.
381 The default value is true.
382*/
383void QShortcut::setAutoRepeat(bool on)
384{
385 Q_D(QShortcut);
386 if (d->sc_autorepeat == on)
387 return;
388 QAPP_CHECK("setAutoRepeat");
389 d->sc_autorepeat = on;
390 QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(on, d->sc_id, this);
391}
392
393bool QShortcut::autoRepeat() const
394{
395 Q_D(const QShortcut);
396 return d->sc_autorepeat;
397}
398
399
400/*!
401 Sets the shortcut's "What's This?" help \a text.
402
403 The text will be shown when a widget application is in "What's
404 This?" mode and the user types the shortcut key() sequence.
405
406 To set "What's This?" help on a menu item (with or without a
407 shortcut key), set the help on the item's action.
408
409 By default, the help text is an empty string.
410
411 This function has no effect in applications that don't use
412 widgets.
413
414 \sa QWhatsThis::inWhatsThisMode(), QAction::setWhatsThis()
415*/
416void QShortcut::setWhatsThis(const QString &text)
417{
418 Q_D(QShortcut);
419 d->sc_whatsthis = text;
420}
421
422/*!
423 Returns the shortcut's "What's This?" help text.
424
425 \sa setWhatsThis()
426*/
427QString QShortcut::whatsThis() const
428{
429 Q_D(const QShortcut);
430 return d->sc_whatsthis;
431}
432
433/*!
434 Returns the shortcut's ID.
435
436 \sa QShortcutEvent::shortcutId()
437*/
438int QShortcut::id() const
439{
440 Q_D(const QShortcut);
441 return d->sc_id;
442}
443
444/*!
445 \fn QWidget *QShortcut::parentWidget() const
446
447 Returns the shortcut's parent widget.
448*/
449
450/*!
451 \internal
452*/
453bool QShortcut::event(QEvent *e)
454{
455 Q_D(QShortcut);
456 if (d->sc_enabled && e->type() == QEvent::Shortcut) {
457 auto se = static_cast<QShortcutEvent *>(e);
458 if (se->shortcutId() == d->sc_id && se->key() == d->sc_sequence
459 && !d->handleWhatsThis()) {
460 if (se->isAmbiguous())
461 emit activatedAmbiguously();
462 else
463 emit activated();
464 return true;
465 }
466 }
467 return QObject::event(e);
468}
469
470QT_END_NAMESPACE
471
472#include "moc_qshortcut.cpp"
473