Coverage Report

Created: 2025-12-31 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qt/qtbase/src/gui/kernel/qshortcutmap.cpp
Line
Count
Source
1
// Copyright (C) 2016 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
4
#include "qshortcutmap_p.h"
5
#include "private/qobject_p.h"
6
#include "qkeysequence.h"
7
#include "qdebug.h"
8
#include "qevent.h"
9
#include "qlist.h"
10
#include "qguiapplication.h"
11
#include "qwindow.h"
12
#include <private/qkeymapper_p.h>
13
#include <QtCore/qloggingcategory.h>
14
#include <QtCore/qscopeguard.h>
15
16
#include <algorithm>
17
18
QT_BEGIN_NAMESPACE
19
20
Q_STATIC_LOGGING_CATEGORY(lcShortcutMap, "qt.gui.shortcutmap")
21
22
/* \internal
23
    Entry data for QShortcutMap
24
    Contains:
25
        Keysequence for entry
26
        Pointer to parent owning the sequence
27
*/
28
29
struct QShortcutEntry
30
{
31
    QShortcutEntry()
32
        : keySequence(0), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(nullptr), contextMatcher(nullptr)
33
0
    {}
34
35
    QShortcutEntry(const QKeySequence &k)
36
0
        : keySequence(k), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(nullptr), contextMatcher(nullptr)
37
0
    {}
38
39
    QShortcutEntry(QObject *o, const QKeySequence &k, Qt::ShortcutContext c, int i, bool a, QShortcutMap::ContextMatcher m)
40
0
        : keySequence(k), context(c), enabled(true), autorepeat(a), id(i), owner(o), contextMatcher(m)
41
0
    {}
42
43
0
    bool correctContext() const { return contextMatcher(owner, context); }
44
45
    bool operator<(const QShortcutEntry &f) const
46
0
    { return keySequence < f.keySequence; }
47
48
    QKeySequence keySequence;
49
    Qt::ShortcutContext context;
50
    bool enabled : 1;
51
    bool autorepeat : 1;
52
    signed int id;
53
    QObject *owner;
54
    QShortcutMap::ContextMatcher contextMatcher;
55
};
56
Q_DECLARE_TYPEINFO(QShortcutEntry, Q_RELOCATABLE_TYPE);
57
58
#ifdef Dump_QShortcutMap
59
/*! \internal
60
    QDebug operator<< for easy debug output of the shortcut entries.
61
*/
62
static QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se)
63
{
64
    QDebugStateSaver saver(dbg);
65
    if (!se)
66
        return dbg << "QShortcutEntry(0x0)";
67
    dbg.nospace()
68
        << "QShortcutEntry(" << se->keyseq
69
        << "), id(" << se->id << "), enabled(" << se->enabled << "), autorepeat(" << se->autorepeat
70
        << "), owner(" << se->owner << ')';
71
    return dbg;
72
}
73
#endif // Dump_QShortcutMap
74
75
/* \internal
76
    Private data for QShortcutMap
77
*/
78
class QShortcutMapPrivate
79
{
80
0
    Q_DECLARE_PUBLIC(QShortcutMap)
Unexecuted instantiation: QShortcutMapPrivate::q_func()
Unexecuted instantiation: QShortcutMapPrivate::q_func() const
81
0
82
0
public:
83
0
    QShortcutMapPrivate(QShortcutMap* parent)
84
6
        : q_ptr(parent), currentId(0), ambigCount(0), currentState(QKeySequence::NoMatch)
85
6
    {
86
6
        identicals.reserve(10);
87
6
        currentSequences.reserve(10);
88
6
    }
89
    QShortcutMap *q_ptr;                        // Private's parent
90
91
    QList<QShortcutEntry> shortcuts;            // All shortcuts!
92
93
    int currentId;                              // Global shortcut ID number
94
    int ambigCount;                             // Index of last enabled ambiguous dispatch
95
    QKeySequence::SequenceMatch currentState;
96
    QList<QKeySequence> currentSequences;       // Sequence for the current state
97
    QList<QKeySequence> newEntries;
98
    QKeySequence prevSequence;                  // Sequence for the previous identical match
99
    QList<const QShortcutEntry*> identicals;    // Last identical matches
100
};
101
102
103
/*! \internal
104
    QShortcutMap constructor.
105
*/
106
QShortcutMap::QShortcutMap()
107
6
    : d_ptr(new QShortcutMapPrivate(this))
108
6
{
109
6
    resetState();
110
6
}
111
112
/*! \internal
113
    QShortcutMap destructor.
114
*/
115
QShortcutMap::~QShortcutMap()
116
6
{
117
6
}
118
119
/*! \internal
120
    Adds a shortcut to the global map.
121
    Returns the id of the newly added shortcut.
122
*/
123
int QShortcutMap::addShortcut(QObject *owner, const QKeySequence &keySequence, Qt::ShortcutContext context, ContextMatcher matcher)
124
0
{
125
0
    Q_ASSERT_X(owner, "QShortcutMap::addShortcut", "All shortcuts need an owner");
126
0
    Q_ASSERT_X(!keySequence.isEmpty(), "QShortcutMap::addShortcut", "Cannot add keyless shortcuts to map");
127
0
    Q_D(QShortcutMap);
128
129
0
    QShortcutEntry newEntry(owner, keySequence, context, --(d->currentId), true, matcher);
130
0
    const auto it = std::upper_bound(d->shortcuts.begin(), d->shortcuts.end(), newEntry);
131
0
    d->shortcuts.insert(it, newEntry); // Insert sorted
132
0
    qCDebug(lcShortcutMap).nospace()
133
0
        << "QShortcutMap::addShortcut(" << owner << ", "
134
0
        << keySequence << ", " << context << ") added shortcut with ID " << d->currentId;
135
0
    return d->currentId;
136
0
}
137
138
/*! \internal
139
    Removes a shortcut from the global map.
140
    If \a owner is \nullptr, all entries in the map with the key sequence specified
141
    is removed. If \a key is null, all sequences for \a owner is removed from
142
    the map. If \a id is 0, any identical \a key sequences owned by \a owner
143
    are removed.
144
    Returns the number of sequences removed from the map.
145
*/
146
147
int QShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &keySequence)
148
0
{
149
0
    Q_D(QShortcutMap);
150
0
    int itemsRemoved = 0;
151
0
    bool allOwners = (owner == nullptr);
152
0
    bool allKeys = keySequence.isEmpty();
153
0
    bool allIds = id == 0;
154
155
0
    auto debug = qScopeGuard([&](){
156
0
        qCDebug(lcShortcutMap).nospace()
157
0
            << "QShortcutMap::removeShortcut(" << id << ", " << owner << ", "
158
0
            << keySequence << ") removed " << itemsRemoved << " shortcuts(s)";
159
0
    });
160
161
    // Special case, remove everything
162
0
    if (allOwners && allKeys && allIds) {
163
0
        itemsRemoved = d->shortcuts.size();
164
0
        d->shortcuts.clear();
165
0
        return itemsRemoved;
166
0
    }
167
168
0
    int i = d->shortcuts.size()-1;
169
0
    while (i>=0)
170
0
    {
171
0
        const QShortcutEntry &entry = d->shortcuts.at(i);
172
0
        int entryId = entry.id;
173
0
        if ((allOwners || entry.owner == owner)
174
0
            && (allIds || entry.id == id)
175
0
            && (allKeys || entry.keySequence == keySequence)) {
176
0
            d->shortcuts.removeAt(i);
177
0
            ++itemsRemoved;
178
0
        }
179
0
        if (id == entryId)
180
0
            return itemsRemoved;
181
0
        --i;
182
0
    }
183
0
    return itemsRemoved;
184
0
}
185
186
/*! \internal
187
    Changes the enable state of a shortcut to \a enable.
188
    If \a owner is \nullptr, all entries in the map with the key sequence specified
189
    is removed. If \a key is null, all sequences for \a owner is removed from
190
    the map. If \a id is 0, any identical \a key sequences owned by \a owner
191
    are changed.
192
    Returns the number of sequences which are matched in the map.
193
*/
194
int QShortcutMap::setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &keySequence)
195
0
{
196
0
    Q_D(QShortcutMap);
197
0
    int itemsChanged = 0;
198
0
    bool allOwners = (owner == nullptr);
199
0
    bool allKeys = keySequence.isEmpty();
200
0
    bool allIds = id == 0;
201
202
0
    int i = d->shortcuts.size()-1;
203
0
    while (i>=0)
204
0
    {
205
0
        const QShortcutEntry &entry = d->shortcuts.at(i);
206
0
        if ((allOwners || entry.owner == owner)
207
0
            && (allIds || entry.id == id)
208
0
            && (allKeys || entry.keySequence == keySequence)) {
209
0
            d->shortcuts[i].enabled = enable;
210
0
            ++itemsChanged;
211
0
        }
212
0
        if (id == entry.id)
213
0
            return itemsChanged;
214
0
        --i;
215
0
    }
216
0
    qCDebug(lcShortcutMap).nospace()
217
0
        << "QShortcutMap::setShortcutEnabled(" << enable << ", " << id << ", "
218
0
        << owner << ", " << keySequence << ") = " << itemsChanged;
219
0
    return itemsChanged;
220
0
}
221
222
/*! \internal
223
    Changes the auto repeat state of a shortcut to \a enable.
224
    If \a owner is \nullptr, all entries in the map with the key sequence specified
225
    is removed. If \a key is null, all sequences for \a owner is removed from
226
    the map. If \a id is 0, any identical \a key sequences owned by \a owner
227
    are changed.
228
    Returns the number of sequences which are matched in the map.
229
*/
230
int QShortcutMap::setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &keySequence)
231
0
{
232
0
    Q_D(QShortcutMap);
233
0
    int itemsChanged = 0;
234
0
    bool allOwners = (owner == nullptr);
235
0
    bool allKeys = keySequence.isEmpty();
236
0
    bool allIds = id == 0;
237
238
0
    int i = d->shortcuts.size()-1;
239
0
    while (i>=0)
240
0
    {
241
0
        QShortcutEntry entry = d->shortcuts.at(i);
242
0
        if ((allOwners || entry.owner == owner)
243
0
            && (allIds || entry.id == id)
244
0
            && (allKeys || entry.keySequence == keySequence)) {
245
0
                d->shortcuts[i].autorepeat = on;
246
0
                ++itemsChanged;
247
0
        }
248
0
        if (id == entry.id)
249
0
            return itemsChanged;
250
0
        --i;
251
0
    }
252
0
    qCDebug(lcShortcutMap).nospace()
253
0
        << "QShortcutMap::setShortcutAutoRepeat(" << on << ", " << id << ", "
254
0
        << owner << ", " << keySequence << ") = " << itemsChanged;
255
0
    return itemsChanged;
256
0
}
257
258
/*! \internal
259
    Resets the state of the statemachine to NoMatch
260
*/
261
void QShortcutMap::resetState()
262
6
{
263
6
    Q_D(QShortcutMap);
264
6
    d->currentState = QKeySequence::NoMatch;
265
6
    clearSequence(d->currentSequences);
266
6
}
267
268
/*! \internal
269
    Returns the current state of the statemachine
270
*/
271
QKeySequence::SequenceMatch QShortcutMap::state()
272
0
{
273
0
    Q_D(QShortcutMap);
274
0
    return d->currentState;
275
0
}
276
277
/*! \internal
278
    Uses nextState(QKeyEvent) to check for a grabbed shortcut.
279
280
    If so, it is dispatched using dispatchEvent().
281
282
    Returns true if a shortcut handled the event.
283
284
    \sa nextState, dispatchEvent
285
*/
286
bool QShortcutMap::tryShortcut(QKeyEvent *e)
287
0
{
288
0
    Q_D(QShortcutMap);
289
290
0
    if (e->key() == Qt::Key_unknown)
291
0
        return false;
292
293
0
    QKeySequence::SequenceMatch previousState = state();
294
295
0
    switch (nextState(e)) {
296
0
    case QKeySequence::NoMatch:
297
        // In the case of going from a partial match to no match we handled the
298
        // event, since we already stated that we did for the partial match. But
299
        // in the normal case of directly going to no match we say we didn't.
300
0
        return previousState == QKeySequence::PartialMatch;
301
0
    case QKeySequence::PartialMatch:
302
        // For a partial match we don't know yet if we will handle the shortcut
303
        // but we need to say we did, so that we get the follow-up key-presses.
304
0
        return true;
305
0
    case QKeySequence::ExactMatch: {
306
        // Save number of identical matches before dispatching
307
        // to keep QShortcutMap and tryShortcut reentrant.
308
0
        const int identicalMatches = d->identicals.size();
309
0
        resetState();
310
0
        dispatchEvent(e);
311
        // If there are no identicals we've only found disabled shortcuts, and
312
        // shouldn't say that we handled the event.
313
0
        return identicalMatches > 0;
314
0
    }
315
0
    }
316
0
    Q_UNREACHABLE_RETURN(false);
317
0
}
318
319
/*! \internal
320
    Returns the next state of the statemachine
321
    If return value is SequenceMatch::ExactMatch, then a call to matches()
322
    will return a QObjects* list of all matching objects for the last matching
323
    sequence.
324
*/
325
QKeySequence::SequenceMatch QShortcutMap::nextState(QKeyEvent *e)
326
0
{
327
0
    Q_D(QShortcutMap);
328
    // Modifiers can NOT be shortcuts...
329
0
    if (e->key() >= Qt::Key_Shift &&
330
0
        e->key() <= Qt::Key_ScrollLock)
331
0
        return d->currentState;
332
333
0
    QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
334
335
    // We start fresh each time..
336
0
    d->identicals.clear();
337
338
0
    result = find(e);
339
0
    if (result == QKeySequence::NoMatch && (e->modifiers() & Qt::KeypadModifier)) {
340
        // Try to find a match without keypad modifier
341
0
        result = find(e, Qt::KeypadModifier);
342
0
    }
343
0
    if (result == QKeySequence::NoMatch && e->modifiers() & Qt::ShiftModifier) {
344
        // If Shift + Key_Backtab, also try Shift + Qt::Key_Tab
345
0
        if (e->key() == Qt::Key_Backtab) {
346
0
            QKeyEvent pe = QKeyEvent(e->type(), Qt::Key_Tab, e->modifiers(), e->text());
347
0
            result = find(&pe);
348
0
        }
349
0
    }
350
351
    // Does the new state require us to clean up?
352
0
    if (result == QKeySequence::NoMatch)
353
0
        clearSequence(d->currentSequences);
354
0
    d->currentState = result;
355
356
0
    qCDebug(lcShortcutMap).nospace() << "QShortcutMap::nextState(" << e << ") = " << result;
357
0
    return result;
358
0
}
359
360
361
/*! \internal
362
    Determines if an enabled shortcut has a matching key sequence.
363
*/
364
bool QShortcutMap::hasShortcutForKeySequence(const QKeySequence &seq) const
365
0
{
366
0
    Q_D(const QShortcutMap);
367
0
    QShortcutEntry entry(seq); // needed for searching
368
0
    const auto itEnd = d->shortcuts.cend();
369
0
    auto it = std::lower_bound(d->shortcuts.cbegin(), itEnd, entry);
370
371
0
    for (;it != itEnd; ++it) {
372
0
        if (entry.keySequence.matches(it->keySequence) == QKeySequence::ExactMatch
373
0
                && (*it).correctContext() && (*it).enabled) {
374
0
            return true;
375
0
        }
376
0
    }
377
378
    //end of the loop: we didn't find anything
379
0
    return false;
380
0
}
381
382
/*! \internal
383
    Returns the next state of the statemachine, based
384
    on the new key event \a e.
385
    Matches are appended to the list of identicals,
386
    which can be access through matches().
387
    \sa matches
388
*/
389
QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e, int ignoredModifiers)
390
0
{
391
0
    Q_D(QShortcutMap);
392
0
    if (!d->shortcuts.size())
393
0
        return QKeySequence::NoMatch;
394
395
0
    createNewSequences(e, d->newEntries, ignoredModifiers);
396
0
    qCDebug(lcShortcutMap) << "Possible input sequences:" << d->newEntries;
397
398
    // Should never happen
399
0
    if (d->newEntries == d->currentSequences) {
400
0
        Q_ASSERT_X(e->key() != Qt::Key_unknown || e->text().size(),
401
0
                   "QShortcutMap::find", "New sequence to find identical to previous");
402
0
        return QKeySequence::NoMatch;
403
0
    }
404
405
    // Looking for new identicals, scrap old
406
0
    d->identicals.clear();
407
408
0
    bool partialFound = false;
409
0
    bool identicalDisabledFound = false;
410
0
    QList<QKeySequence> okEntries;
411
0
    QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
412
0
    for (int i = d->newEntries.size()-1; i >= 0 ; --i) {
413
0
        QShortcutEntry entry(d->newEntries.at(i)); // needed for searching
414
0
        qCDebug(lcShortcutMap) << "Looking for shortcuts matching" << entry.keySequence;
415
416
0
        QKeySequence::SequenceMatch bestMatchForEntry = QKeySequence::NoMatch;
417
418
0
        const auto itEnd = d->shortcuts.constEnd();
419
0
        auto it = std::lower_bound(d->shortcuts.constBegin(), itEnd, entry);
420
0
        for (; it != itEnd; ++it) {
421
0
            QKeySequence::SequenceMatch match = entry.keySequence.matches(it->keySequence);
422
0
            qCDebug(lcShortcutMap) << " -" << match << "for shortcut" << it->keySequence;
423
424
            // If we got a valid match, there might still be more keys to check against,
425
            // but if we get no match, we know that there are no more possible matches.
426
0
            if (match == QKeySequence::NoMatch)
427
0
                break;
428
429
0
            bestMatchForEntry = qMax(bestMatchForEntry, match);
430
431
0
            if ((*it).correctContext()) {
432
0
                if (match == QKeySequence::ExactMatch) {
433
0
                    if ((*it).enabled)
434
0
                        d->identicals.append(&*it);
435
0
                    else
436
0
                        identicalDisabledFound = true;
437
0
                } else if (match == QKeySequence::PartialMatch) {
438
                    // We don't need partials, if we have identicals
439
0
                    if (d->identicals.size())
440
0
                        break;
441
                    // We only care about enabled partials, so we don't consume
442
                    // key events when all partials are disabled!
443
0
                    partialFound |= (*it).enabled;
444
0
                }
445
0
            } else {
446
0
                qCDebug(lcShortcutMap) << "  - But context was not correct";
447
0
            }
448
0
        }
449
450
        // If the type of match improves (ergo, NoMatch->Partial, or Partial->Exact), clear the
451
        // previous list. If this match is equal or better than the last match, append to the list
452
0
        if (bestMatchForEntry > result) {
453
0
            okEntries.clear();
454
0
            qCDebug(lcShortcutMap) << "Found better match (" << d->newEntries << "), clearing key sequence list";
455
0
        }
456
0
        if (bestMatchForEntry && bestMatchForEntry >= result) {
457
0
            okEntries << d->newEntries.at(i);
458
0
            qCDebug(lcShortcutMap) << "Added ok key sequence" << d->newEntries;
459
0
        }
460
0
    }
461
462
0
    if (d->identicals.size()) {
463
0
        result = QKeySequence::ExactMatch;
464
0
    } else if (partialFound) {
465
0
        result = QKeySequence::PartialMatch;
466
0
    } else if (identicalDisabledFound) {
467
0
        result = QKeySequence::ExactMatch;
468
0
    } else {
469
0
        clearSequence(d->currentSequences);
470
0
        result = QKeySequence::NoMatch;
471
0
    }
472
0
    if (result != QKeySequence::NoMatch)
473
0
        d->currentSequences = okEntries;
474
0
    qCDebug(lcShortcutMap) << "Returning shortcut match == " << result;
475
0
    return result;
476
0
}
477
478
/*! \internal
479
    Clears \a seq to an empty QKeySequence.
480
    Same as doing (the slower)
481
    \snippet code/src_gui_kernel_qshortcutmap.cpp 0
482
*/
483
void QShortcutMap::clearSequence(QList<QKeySequence> &ksl)
484
6
{
485
6
    ksl.clear();
486
6
    d_func()->newEntries.clear();
487
6
}
488
489
/*! \internal
490
    Alters \a seq to the new sequence state, based on the
491
    current sequence state, and the new key event \a e.
492
*/
493
void QShortcutMap::createNewSequences(QKeyEvent *e, QList<QKeySequence> &ksl, int ignoredModifiers)
494
0
{
495
0
    Q_D(QShortcutMap);
496
0
    QList<QKeyCombination> possibleKeys = QKeyMapper::possibleKeys(e);
497
0
    qCDebug(lcShortcutMap) << "Creating new sequences for" << e
498
0
        << "with ignoredModifiers=" << Qt::KeyboardModifiers(ignoredModifiers);
499
0
    int pkTotal = possibleKeys.size();
500
0
    if (!pkTotal)
501
0
        return;
502
503
0
    int ssActual = d->currentSequences.size();
504
0
    int ssTotal = qMax(1, ssActual);
505
    // Resize to possible permutations of the current sequence(s).
506
0
    ksl.resize(pkTotal * ssTotal);
507
508
0
    int index = ssActual ? d->currentSequences.at(0).count() : 0;
509
0
    for (int pkNum = 0; pkNum < pkTotal; ++pkNum) {
510
0
        for (int ssNum = 0; ssNum < ssTotal; ++ssNum) {
511
0
            int i = (pkNum * ssTotal) + ssNum;
512
0
            QKeySequence &curKsl = ksl[i];
513
0
            if (ssActual) {
514
0
                const QKeySequence &curSeq = d->currentSequences.at(ssNum);
515
0
                curKsl.setKey(curSeq[0], 0);
516
0
                curKsl.setKey(curSeq[1], 1);
517
0
                curKsl.setKey(curSeq[2], 2);
518
0
                curKsl.setKey(curSeq[3], 3);
519
0
            } else {
520
0
                curKsl.setKey(QKeyCombination::fromCombined(0), 0);
521
0
                curKsl.setKey(QKeyCombination::fromCombined(0), 1);
522
0
                curKsl.setKey(QKeyCombination::fromCombined(0), 2);
523
0
                curKsl.setKey(QKeyCombination::fromCombined(0), 3);
524
0
            }
525
0
            const int key = possibleKeys.at(pkNum).toCombined();
526
0
            curKsl.setKey(QKeyCombination::fromCombined(key & ~ignoredModifiers), index);
527
0
        }
528
0
    }
529
0
}
530
531
/*! \internal
532
    Converts keyboard button states into modifier states
533
*/
534
int QShortcutMap::translateModifiers(Qt::KeyboardModifiers modifiers)
535
0
{
536
0
    int result = 0;
537
0
    if (modifiers & Qt::ShiftModifier)
538
0
        result |= Qt::SHIFT;
539
0
    if (modifiers & Qt::ControlModifier)
540
0
        result |= Qt::CTRL;
541
0
    if (modifiers & Qt::MetaModifier)
542
0
        result |= Qt::META;
543
0
    if (modifiers & Qt::AltModifier)
544
0
        result |= Qt::ALT;
545
0
    return result;
546
0
}
547
548
/*! \internal
549
    Returns the list of QShortcutEntry's matching the last Identical state.
550
*/
551
QList<const QShortcutEntry*> QShortcutMap::matches() const
552
0
{
553
0
    Q_D(const QShortcutMap);
554
0
    return d->identicals;
555
0
}
556
557
/*! \internal
558
    Dispatches QShortcutEvents to widgets who grabbed the matched key sequence.
559
*/
560
void QShortcutMap::dispatchEvent(QKeyEvent *e)
561
0
{
562
0
    Q_D(QShortcutMap);
563
0
    if (!d->identicals.size())
564
0
        return;
565
566
0
    const QKeySequence &curKey = d->identicals.at(0)->keySequence;
567
0
    if (d->prevSequence != curKey) {
568
0
        d->ambigCount = 0;
569
0
        d->prevSequence = curKey;
570
0
    }
571
    // Find next
572
0
    const QShortcutEntry *current = nullptr, *next = nullptr;
573
0
    int i = 0, enabledShortcuts = 0;
574
0
    QList<const QShortcutEntry*> ambiguousShortcuts;
575
0
    while(i < d->identicals.size()) {
576
0
        current = d->identicals.at(i);
577
0
        if (current->enabled || !next){
578
0
            ++enabledShortcuts;
579
0
            if (lcShortcutMap().isDebugEnabled())
580
0
                ambiguousShortcuts.append(current);
581
0
            if (enabledShortcuts > d->ambigCount + 1)
582
0
                break;
583
0
            next = current;
584
0
        }
585
0
        ++i;
586
0
    }
587
0
    d->ambigCount = (d->identicals.size() == i ? 0 : d->ambigCount + 1);
588
    // Don't trigger shortcut if we're autorepeating and the shortcut is
589
    // grabbed with not accepting autorepeats.
590
0
    if (!next || (e->isAutoRepeat() && !next->autorepeat))
591
0
        return;
592
    // Dispatch next enabled
593
0
    if (lcShortcutMap().isDebugEnabled()) {
594
0
        if (ambiguousShortcuts.size() > 1) {
595
0
            qCDebug(lcShortcutMap) << "The following shortcuts are about to be activated ambiguously:";
596
0
            for (const QShortcutEntry *entry : std::as_const(ambiguousShortcuts))
597
0
                qCDebug(lcShortcutMap).nospace() << "- " << entry->keySequence << " (belonging to " << entry->owner << ")";
598
0
        }
599
600
0
        qCDebug(lcShortcutMap).nospace()
601
0
            << "QShortcutMap::dispatchEvent(): Sending QShortcutEvent(\""
602
0
            << next->keySequence.toString() << "\", " << next->id << ", "
603
0
            << static_cast<bool>(enabledShortcuts>1) << ") to object(" << next->owner << ')';
604
0
    }
605
0
    QShortcutEvent se(next->keySequence, next->id, enabledShortcuts > 1);
606
0
    QCoreApplication::sendEvent(const_cast<QObject *>(next->owner), &se);
607
0
}
608
609
QList<QKeySequence> QShortcutMap::keySequences(bool getAll) const
610
0
{
611
0
    Q_D(const QShortcutMap);
612
0
    QList<QKeySequence> keys;
613
0
    for (auto sequence : d->shortcuts) {
614
0
        bool addSequence = false;
615
0
        if (sequence.enabled) {
616
0
            if (getAll || sequence.context == Qt::ApplicationShortcut ||
617
0
                sequence.owner == QGuiApplication::focusObject()) {
618
0
                addSequence = true;
619
0
            } else {
620
0
                QObject *possibleWindow = sequence.owner;
621
0
                while (possibleWindow) {
622
0
                    if (qobject_cast<QWindow *>(possibleWindow))
623
0
                        break;
624
0
                    possibleWindow = possibleWindow->parent();
625
0
                }
626
0
                if (possibleWindow == QGuiApplication::focusWindow()) {
627
0
                    if (sequence.context == Qt::WindowShortcut) {
628
0
                        addSequence = true;
629
0
                    } else if (sequence.context == Qt::WidgetWithChildrenShortcut) {
630
0
                        QObject *possibleWidget = QGuiApplication::focusObject();
631
0
                        while (possibleWidget->parent()) {
632
0
                            possibleWidget = possibleWidget->parent();
633
0
                            if (possibleWidget == sequence.owner) {
634
0
                                addSequence = true;
635
0
                                break;
636
0
                            }
637
0
                        }
638
0
                    }
639
0
                }
640
0
            }
641
0
            if (addSequence)
642
0
                keys << sequence.keySequence;
643
0
        }
644
0
    }
645
0
    return keys;
646
647
0
}
648
649
/* \internal
650
    QShortcutMap dump function, only available when DEBUG_QSHORTCUTMAP is
651
    defined.
652
*/
653
#if defined(Dump_QShortcutMap)
654
void QShortcutMap::dumpMap() const
655
{
656
    Q_D(const QShortcutMap);
657
    for (int i = 0; i < d->shortcuts.size(); ++i)
658
        qDebug().nospace() << &(d->shortcuts.at(i));
659
}
660
#endif
661
662
QT_END_NAMESPACE