Coverage Report

Created: 2026-03-31 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/kernel/qshortcut.cpp
Line
Count
Source
1
// Copyright (C) 2019 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
// Qt-Security score:significant reason:default
4
5
#include "qshortcut.h"
6
#include "qshortcut_p.h"
7
8
#include <qevent.h>
9
#include <qguiapplication.h>
10
#include <qwindow.h>
11
#include <private/qguiapplication_p.h>
12
#include <qpa/qplatformmenu.h>
13
14
QT_BEGIN_NAMESPACE
15
16
#define QAPP_CHECK(functionName) \
17
0
    if (Q_UNLIKELY(!qApp)) {                                            \
18
0
        qWarning("QShortcut: Initialize QGuiApplication before calling '" functionName "'."); \
19
0
        return; \
20
0
    }
21
22
/*!
23
    \class QShortcut
24
    \brief The QShortcut class is used to create keyboard shortcuts.
25
26
    \ingroup events
27
    \inmodule QtGui
28
29
    The QShortcut class provides a way of connecting keyboard
30
    shortcuts to Qt's \l{signals and slots} mechanism, so that
31
    objects can be informed when a shortcut is executed. The shortcut
32
    can be set up to contain all the key presses necessary to
33
    describe a keyboard shortcut, including the states of modifier
34
    keys such as \uicontrol Shift, \uicontrol Ctrl, and \uicontrol Alt.
35
36
    \target mnemonic
37
38
    In widget applications, certain widgets can use '&' in front of a character.
39
    This will automatically create a mnemonic (a shortcut) for that character,
40
    e.g. "E&xit" will create the shortcut \uicontrol Alt+X (use '&&'
41
    to display an actual ampersand). The widget might consume and perform
42
    an action on a given shortcut. On X11 the ampersand will not be
43
    shown and the character will be underlined. On Windows, shortcuts
44
    are normally not displayed until the user presses the \uicontrol Alt
45
    key, but this is a setting the user can change. On Mac, shortcuts
46
    are disabled by default. Call \l qt_set_sequence_auto_mnemonic() to
47
    enable them. However, because mnemonic shortcuts do not fit in
48
    with Aqua's guidelines, Qt will not show the shortcut character
49
    underlined.
50
51
    For applications that use menus, it may be more convenient to
52
    use the convenience functions provided in the QMenu class to
53
    assign keyboard shortcuts to menu items as they are created.
54
    Alternatively, shortcuts may be associated with other types of
55
    actions in the QAction class.
56
57
    The simplest way to create a shortcut for a particular widget is
58
    to construct the shortcut with a key sequence. For example:
59
60
    \snippet code/src_gui_kernel_qshortcut.cpp 0
61
62
    When the user types the \l{QKeySequence}{key sequence}
63
    for a given shortcut, the shortcut's activated() signal is
64
    emitted. (In the case of ambiguity, the activatedAmbiguously()
65
    signal is emitted.) A shortcut is "listened for" by Qt's event
66
    loop when the shortcut's parent widget is receiving events.
67
68
    A shortcut's key sequence can be set with setKey() and retrieved
69
    with key(). A shortcut can be enabled or disabled with
70
    setEnabled(), and can have "What's This?" help text set with
71
    setWhatsThis().
72
73
    \sa QShortcutEvent, QKeySequence, QAction
74
*/
75
76
/*!
77
    \fn void QShortcut::activated()
78
79
    This signal is emitted when the user types the shortcut's key
80
    sequence.
81
82
    \sa activatedAmbiguously()
83
*/
84
85
/*!
86
    \fn void QShortcut::activatedAmbiguously()
87
88
    When a key sequence is being typed at the keyboard, it is said to
89
    be ambiguous as long as it matches the start of more than one
90
    shortcut.
91
92
    When a shortcut's key sequence is completed,
93
    activatedAmbiguously() is emitted if the key sequence is still
94
    ambiguous (i.e., it is the start of one or more other shortcuts).
95
    The activated() signal is not emitted in this case.
96
97
    \sa activated()
98
*/
99
100
bool QShortcutPrivate::simpleContextMatcher(QObject *object, Qt::ShortcutContext context)
101
0
{
102
0
    auto guiShortcut = qobject_cast<QShortcut *>(object);
103
0
    if (QGuiApplication::applicationState() != Qt::ApplicationActive || guiShortcut == nullptr)
104
0
        return false;
105
0
    if (context == Qt::ApplicationShortcut)
106
0
        return true;
107
0
    auto focusWindow = QGuiApplication::focusWindow();
108
0
    if (!focusWindow)
109
0
        return false;
110
0
    auto window = qobject_cast<const QWindow *>(guiShortcut->parent());
111
0
    if (!window)
112
0
        return false;
113
0
    if (focusWindow == window && focusWindow->isTopLevel())
114
0
        return context == Qt::WindowShortcut || context == Qt::WidgetWithChildrenShortcut;
115
0
    return focusWindow->isAncestorOf(window, QWindow::ExcludeTransients);
116
0
}
117
118
QShortcutMap::ContextMatcher QShortcutPrivate::contextMatcher() const
119
0
{
120
0
    return simpleContextMatcher;
121
0
}
122
123
void QShortcutPrivate::redoGrab(QShortcutMap &map)
124
0
{
125
0
    Q_Q(QShortcut);
126
0
    if (Q_UNLIKELY(!parent)) {
127
0
        qWarning("QShortcut: No window parent defined");
128
0
        return;
129
0
    }
130
131
0
    for (int id : std::as_const(sc_ids))
132
0
        map.removeShortcut(id, q);
133
134
0
    sc_ids.clear();
135
0
    if (sc_sequences.isEmpty())
136
0
        return;
137
0
    sc_ids.reserve(sc_sequences.size());
138
0
    for (const auto &keySequence : std::as_const(sc_sequences)) {
139
0
        if (keySequence.isEmpty())
140
0
            continue;
141
0
        int id = map.addShortcut(q, keySequence, sc_context, contextMatcher());
142
0
        sc_ids.append(id);
143
0
        if (!sc_enabled)
144
0
            map.setShortcutEnabled(false, id, q);
145
0
        if (!sc_autorepeat)
146
0
            map.setShortcutAutoRepeat(false, id, q);
147
0
    }
148
0
}
149
150
QShortcutPrivate *QGuiApplicationPrivate::createShortcutPrivate() const
151
0
{
152
0
    return new QShortcutPrivate;
153
0
}
154
155
/*!
156
    Constructs a QShortcut object for the \a parent, which should be a
157
    QWindow or a QWidget.
158
159
    Since no shortcut key sequence is specified, the shortcut will not emit any
160
    signals.
161
162
    \sa setKey()
163
*/
164
QShortcut::QShortcut(QObject *parent)
165
0
    : QObject(*QGuiApplicationPrivate::instance()->createShortcutPrivate(), parent)
166
0
{
167
0
    Q_ASSERT(parent != nullptr);
168
0
}
169
170
/*!
171
    Constructs a QShortcut object for the \a parent, which should be a
172
    QWindow or a QWidget.
173
174
    The shortcut operates on its parent, listening for \l{QShortcutEvent}s that
175
    match the \a key sequence. Depending on the ambiguity of the event, the
176
    shortcut will call the \a member function, or the \a ambiguousMember function,
177
    if the key press was in the shortcut's \a context.
178
*/
179
QShortcut::QShortcut(const QKeySequence &key, QObject *parent,
180
                           const char *member, const char *ambiguousMember,
181
                           Qt::ShortcutContext context)
182
0
    : QShortcut(parent)
183
0
{
184
0
    Q_D(QShortcut);
185
0
    d->sc_context = context;
186
0
    if (!key.isEmpty()) {
187
0
        d->sc_sequences = { key };
188
0
        d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap);
189
0
    }
190
0
    if (member)
191
0
        connect(this, SIGNAL(activated()), parent, member);
192
0
    if (ambiguousMember)
193
0
        connect(this, SIGNAL(activatedAmbiguously()), parent, ambiguousMember);
194
0
}
195
196
/*!
197
    \since 6.0
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 standardKey. 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
*/
206
QShortcut::QShortcut(QKeySequence::StandardKey standardKey, QObject *parent,
207
                     const char *member, const char *ambiguousMember,
208
                     Qt::ShortcutContext context)
209
0
    : QShortcut(parent)
210
0
{
211
0
    Q_D(QShortcut);
212
0
    d->sc_context = context;
213
0
    d->sc_sequences = QKeySequence::keyBindings(standardKey);
214
0
    d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap);
215
0
    if (member)
216
0
        connect(this, SIGNAL(activated()), parent, member);
217
0
    if (ambiguousMember)
218
0
        connect(this, SIGNAL(activatedAmbiguously()), parent, ambiguousMember);
219
0
}
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
    \fn template<typename Functor> QShortcut::QShortcut(QKeySequence::StandardKey key, QObject *parent, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
280
    \since 6.0
281
    \overload
282
283
    This is a QShortcut convenience constructor which connects the shortcut's
284
    \l{QShortcut::activated()}{activated()} signal to the \a functor.
285
*/
286
/*!
287
    \fn template<typename Functor> QShortcut::QShortcut(QKeySequence::StandardKey key, QObject *parent, const QObject *context, Functor functor, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
288
    \since 6.0
289
    \overload
290
291
    This is a QShortcut convenience constructor which connects the shortcut's
292
    \l{QShortcut::activated()}{activated()} signal to the \a functor.
293
294
    The \a functor can be a pointer to a member function of the \a context object.
295
296
    If the \a context object is destroyed, the \a functor will not be called.
297
*/
298
/*!
299
    \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(QKeySequence::StandardKey key, QObject *parent, const QObject *context, Functor functor, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
300
    \since 6.0
301
    \overload
302
303
    This is a QShortcut convenience constructor which connects the shortcut's
304
    \l{QShortcut::activated()}{activated()} signal to the \a functor and
305
    \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()}
306
    signal to the \a functorAmbiguous.
307
308
    The \a functor and \a functorAmbiguous can be a pointer to a member
309
    function of the \a context object.
310
311
    If the \a context object is destroyed, the \a functor and
312
    \a functorAmbiguous will not be called.
313
*/
314
/*!
315
    \fn template<typename Functor, typename FunctorAmbiguous> QShortcut::QShortcut(QKeySequence::StandardKey key, QObject *parent, const QObject *context1, Functor functor, const QObject *context2, FunctorAmbiguous functorAmbiguous, Qt::ShortcutContext shortcutContext = Qt::WindowShortcut)
316
    \since 6.0
317
    \overload
318
319
    This is a QShortcut convenience constructor which connects the shortcut's
320
    \l{QShortcut::activated()}{activated()} signal to the \a functor and
321
    \l{QShortcut::activatedAmbiguously()}{activatedAmbiguously()}
322
    signal to the \a functorAmbiguous.
323
324
    The \a functor can be a pointer to a member function of the
325
    \a context1 object.
326
    The \a functorAmbiguous can be a pointer to a member function of the
327
    \a context2 object.
328
329
    If the \a context1 object is destroyed, the \a functor will not be called.
330
    If the \a context2 object is destroyed, the \a functorAmbiguous
331
    will not be called.
332
*/
333
334
/*!
335
    Destroys the shortcut.
336
*/
337
QShortcut::~QShortcut()
338
0
{
339
0
    Q_D(QShortcut);
340
0
    if (qApp) {
341
0
         for (int id : std::as_const(d->sc_ids))
342
0
             QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id, this);
343
0
    }
344
0
}
345
346
/*!
347
    \property QShortcut::key
348
    \brief the shortcut's primary key sequence
349
350
    This is a key sequence with an optional combination of Shift, Ctrl,
351
    and Alt. The key sequence may be supplied in a number of ways:
352
353
    \snippet code/src_gui_kernel_qshortcut.cpp 1
354
355
    By default, this property contains an empty key sequence.
356
*/
357
void QShortcut::setKey(const QKeySequence &key)
358
0
{
359
0
    if (key.isEmpty())
360
0
        setKeys({});
361
0
    else
362
0
        setKeys({ key });
363
0
}
364
365
QKeySequence QShortcut::key() const
366
0
{
367
0
    Q_D(const QShortcut);
368
0
    if (d->sc_sequences.isEmpty())
369
0
        return QKeySequence();
370
0
    return d->sc_sequences.first();
371
0
}
372
373
/*!
374
    Sets \a keys as the list of key sequences that trigger the
375
    shortcut.
376
377
    \since 6.0
378
379
    \sa key, keys()
380
*/
381
void QShortcut::setKeys(const QList<QKeySequence> &keys)
382
0
{
383
0
    Q_D(QShortcut);
384
0
    if (d->sc_sequences == keys)
385
0
        return;
386
0
    QAPP_CHECK("setKeys");
387
0
    d->sc_sequences = keys;
388
0
    d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap);
389
0
}
390
391
/*!
392
    Sets the triggers to those matching the standard key \a key.
393
394
    \since 6.0
395
396
    \sa key, keys()
397
*/
398
void QShortcut::setKeys(QKeySequence::StandardKey key)
399
0
{
400
0
    setKeys(QKeySequence::keyBindings(key));
401
0
}
402
403
/*!
404
    Returns the list of key sequences which trigger this
405
    shortcut.
406
407
    \since 6.0
408
    \sa key, setKeys()
409
*/
410
QList<QKeySequence> QShortcut::keys() const
411
0
{
412
0
    Q_D(const QShortcut);
413
0
    return d->sc_sequences;
414
0
}
415
416
/*!
417
    \property QShortcut::enabled
418
    \brief whether the shortcut is enabled
419
420
    An enabled shortcut emits the activated() or activatedAmbiguously()
421
    signal when a QShortcutEvent occurs that matches the shortcut's
422
    key() sequence.
423
424
    If the application is in \c WhatsThis mode the shortcut will not emit
425
    the signals, but will show the "What's This?" text instead.
426
427
    By default, this property is \c true.
428
429
    \sa whatsThis
430
*/
431
void QShortcut::setEnabled(bool enable)
432
0
{
433
0
    Q_D(QShortcut);
434
0
    if (d->sc_enabled == enable)
435
0
        return;
436
0
    QAPP_CHECK("setEnabled");
437
0
    d->sc_enabled = enable;
438
0
    for (int id : d->sc_ids)
439
0
        QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enable, id, this);
440
0
}
441
442
bool QShortcut::isEnabled() const
443
0
{
444
0
    Q_D(const QShortcut);
445
0
    return d->sc_enabled;
446
0
}
447
448
/*!
449
    \property QShortcut::context
450
    \brief the context in which the shortcut is valid
451
452
    A shortcut's context decides in which circumstances a shortcut is
453
    allowed to be triggered. The normal context is Qt::WindowShortcut,
454
    which allows the shortcut to trigger if the parent (the widget
455
    containing the shortcut) is a subwidget of the active top-level
456
    window.
457
458
    By default, this property is set to Qt::WindowShortcut.
459
*/
460
void QShortcut::setContext(Qt::ShortcutContext context)
461
0
{
462
0
    Q_D(QShortcut);
463
0
    if (d->sc_context == context)
464
0
        return;
465
0
    QAPP_CHECK("setContext");
466
0
    d->sc_context = context;
467
0
    d->redoGrab(QGuiApplicationPrivate::instance()->shortcutMap);
468
0
}
469
470
Qt::ShortcutContext QShortcut::context() const
471
0
{
472
0
    Q_D(const QShortcut);
473
0
    return d->sc_context;
474
0
}
475
476
/*!
477
    \property QShortcut::autoRepeat
478
    \brief whether the shortcut can auto repeat
479
480
    If true, the shortcut will auto repeat when the keyboard shortcut
481
    combination is held down, provided that keyboard auto repeat is
482
    enabled on the system.
483
    The default value is true.
484
*/
485
void QShortcut::setAutoRepeat(bool on)
486
0
{
487
0
    Q_D(QShortcut);
488
0
    if (d->sc_autorepeat == on)
489
0
        return;
490
0
    QAPP_CHECK("setAutoRepeat");
491
0
    d->sc_autorepeat = on;
492
0
    for (int id : d->sc_ids)
493
0
        QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(on, id, this);
494
0
}
495
496
bool QShortcut::autoRepeat() const
497
0
{
498
0
    Q_D(const QShortcut);
499
0
    return d->sc_autorepeat;
500
0
}
501
502
503
/*!
504
    Sets the shortcut's "What's This?" help \a text.
505
506
    The text will be shown when a widget application is in "What's
507
    This?" mode and the user types the shortcut key() sequence.
508
509
    To set "What's This?" help on a menu item (with or without a
510
    shortcut key), set the help on the item's action.
511
512
    By default, the help text is an empty string.
513
514
    This function has no effect in applications that don't use
515
    widgets.
516
517
    \sa QWhatsThis::inWhatsThisMode(), QAction::setWhatsThis()
518
*/
519
void QShortcut::setWhatsThis(const QString &text)
520
0
{
521
0
    Q_D(QShortcut);
522
0
    d->sc_whatsthis = text;
523
0
}
524
525
/*!
526
    Returns the shortcut's "What's This?" help text.
527
528
    \sa setWhatsThis()
529
*/
530
QString QShortcut::whatsThis() const
531
0
{
532
0
    Q_D(const QShortcut);
533
0
    return d->sc_whatsthis;
534
0
}
535
536
#if QT_DEPRECATED_SINCE(6,0)
537
/*!
538
    Returns the primary key binding's ID.
539
540
    \deprecated
541
542
    \sa QShortcutEvent::shortcutId()
543
*/
544
int QShortcut::id() const
545
0
{
546
0
    Q_D(const QShortcut);
547
0
    if (d->sc_ids.isEmpty())
548
0
        return 0;
549
0
    return d->sc_ids.first();
550
0
}
551
#endif
552
553
/*!
554
    \fn QWidget *QShortcut::parentWidget() const
555
556
    Returns the shortcut's parent widget.
557
*/
558
559
/*!
560
    \internal
561
*/
562
bool QShortcut::event(QEvent *e)
563
0
{
564
0
    Q_D(QShortcut);
565
0
    if (d->sc_enabled && e->type() == QEvent::Shortcut) {
566
0
        auto se = static_cast<QShortcutEvent *>(e);
567
0
        if (!d->handleWhatsThis()) {
568
0
            Q_ASSERT_X(d->sc_ids.contains(se->shortcutId()), "QShortcut::event", "Received shortcut event from wrong shortcut");
569
0
            if (se->isAmbiguous())
570
0
                emit activatedAmbiguously();
571
0
            else
572
0
                emit activated();
573
0
            return true;
574
0
        }
575
0
    }
576
0
    return QObject::event(e);
577
0
}
578
579
QT_END_NAMESPACE
580
581
#undef QAPP_CHECK
582
583
#include "moc_qshortcut.cpp"