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
18QT_BEGIN_NAMESPACE
19
20Q_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
29struct QShortcutEntry
30{
31 QShortcutEntry()
32 : keyseq(0), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(nullptr), contextMatcher(nullptr)
33 {}
34
35 QShortcutEntry(const QKeySequence &k)
36 : keyseq(k), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(nullptr), contextMatcher(nullptr)
37 {}
38
39 QShortcutEntry(QObject *o, const QKeySequence &k, Qt::ShortcutContext c, int i, bool a, QShortcutMap::ContextMatcher m)
40 : keyseq(k), context(c), enabled(true), autorepeat(a), id(i), owner(o), contextMatcher(m)
41 {}
42
43 bool correctContext() const { return contextMatcher(owner, context); }
44
45 bool operator<(const QShortcutEntry &f) const
46 { return keyseq < f.keyseq; }
47
48 QKeySequence keyseq;
49 Qt::ShortcutContext context;
50 bool enabled : 1;
51 bool autorepeat : 1;
52 signed int id;
53 QObject *owner;
54 QShortcutMap::ContextMatcher contextMatcher;
55};
56Q_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*/
62static 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*/
78class QShortcutMapPrivate
79{
80 Q_DECLARE_PUBLIC(QShortcutMap)
81
82public:
83 QShortcutMapPrivate(QShortcutMap* parent)
84 : q_ptr(parent), currentId(0), ambigCount(0), currentState(QKeySequence::NoMatch)
85 {
86 identicals.reserve(asize: 10);
87 currentSequences.reserve(asize: 10);
88 }
89 QShortcutMap *q_ptr; // Private's parent
90
91 QList<QShortcutEntry> sequences; // All sequences!
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*/
106QShortcutMap::QShortcutMap()
107 : d_ptr(new QShortcutMapPrivate(this))
108{
109 resetState();
110}
111
112/*! \internal
113 QShortcutMap destructor.
114*/
115QShortcutMap::~QShortcutMap()
116{
117}
118
119/*! \internal
120 Adds a shortcut to the global map.
121 Returns the id of the newly added shortcut.
122*/
123int QShortcutMap::addShortcut(QObject *owner, const QKeySequence &key, Qt::ShortcutContext context, ContextMatcher matcher)
124{
125 Q_ASSERT_X(owner, "QShortcutMap::addShortcut", "All shortcuts need an owner");
126 Q_ASSERT_X(!key.isEmpty(), "QShortcutMap::addShortcut", "Cannot add keyless shortcuts to map");
127 Q_D(QShortcutMap);
128
129 QShortcutEntry newEntry(owner, key, context, --(d->currentId), true, matcher);
130 const auto it = std::upper_bound(first: d->sequences.begin(), last: d->sequences.end(), val: newEntry);
131 d->sequences.insert(before: it, t: newEntry); // Insert sorted
132 qCDebug(lcShortcutMap).nospace()
133 << "QShortcutMap::addShortcut(" << owner << ", "
134 << key << ", " << context << ") added shortcut with ID " << d->currentId;
135 return d->currentId;
136}
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
147int QShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &key)
148{
149 Q_D(QShortcutMap);
150 int itemsRemoved = 0;
151 bool allOwners = (owner == nullptr);
152 bool allKeys = key.isEmpty();
153 bool allIds = id == 0;
154
155 auto debug = qScopeGuard(f: [&](){
156 qCDebug(lcShortcutMap).nospace()
157 << "QShortcutMap::removeShortcut(" << id << ", " << owner << ", "
158 << key << ") removed " << itemsRemoved << " shortcuts(s)";
159 });
160
161 // Special case, remove everything
162 if (allOwners && allKeys && allIds) {
163 itemsRemoved = d->sequences.size();
164 d->sequences.clear();
165 return itemsRemoved;
166 }
167
168 int i = d->sequences.size()-1;
169 while (i>=0)
170 {
171 const QShortcutEntry &entry = d->sequences.at(i);
172 int entryId = entry.id;
173 if ((allOwners || entry.owner == owner)
174 && (allIds || entry.id == id)
175 && (allKeys || entry.keyseq == key)) {
176 d->sequences.removeAt(i);
177 ++itemsRemoved;
178 }
179 if (id == entryId)
180 return itemsRemoved;
181 --i;
182 }
183 return itemsRemoved;
184}
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*/
194int QShortcutMap::setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &key)
195{
196 Q_D(QShortcutMap);
197 int itemsChanged = 0;
198 bool allOwners = (owner == nullptr);
199 bool allKeys = key.isEmpty();
200 bool allIds = id == 0;
201
202 int i = d->sequences.size()-1;
203 while (i>=0)
204 {
205 QShortcutEntry entry = d->sequences.at(i);
206 if ((allOwners || entry.owner == owner)
207 && (allIds || entry.id == id)
208 && (allKeys || entry.keyseq == key)) {
209 d->sequences[i].enabled = enable;
210 ++itemsChanged;
211 }
212 if (id == entry.id)
213 return itemsChanged;
214 --i;
215 }
216 qCDebug(lcShortcutMap).nospace()
217 << "QShortcutMap::setShortcutEnabled(" << enable << ", " << id << ", "
218 << owner << ", " << key << ") = " << itemsChanged;
219 return itemsChanged;
220}
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*/
230int QShortcutMap::setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &key)
231{
232 Q_D(QShortcutMap);
233 int itemsChanged = 0;
234 bool allOwners = (owner == nullptr);
235 bool allKeys = key.isEmpty();
236 bool allIds = id == 0;
237
238 int i = d->sequences.size()-1;
239 while (i>=0)
240 {
241 QShortcutEntry entry = d->sequences.at(i);
242 if ((allOwners || entry.owner == owner)
243 && (allIds || entry.id == id)
244 && (allKeys || entry.keyseq == key)) {
245 d->sequences[i].autorepeat = on;
246 ++itemsChanged;
247 }
248 if (id == entry.id)
249 return itemsChanged;
250 --i;
251 }
252 qCDebug(lcShortcutMap).nospace()
253 << "QShortcutMap::setShortcutAutoRepeat(" << on << ", " << id << ", "
254 << owner << ", " << key << ") = " << itemsChanged;
255 return itemsChanged;
256}
257
258/*! \internal
259 Resets the state of the statemachine to NoMatch
260*/
261void QShortcutMap::resetState()
262{
263 Q_D(QShortcutMap);
264 d->currentState = QKeySequence::NoMatch;
265 clearSequence(ksl&: d->currentSequences);
266}
267
268/*! \internal
269 Returns the current state of the statemachine
270*/
271QKeySequence::SequenceMatch QShortcutMap::state()
272{
273 Q_D(QShortcutMap);
274 return d->currentState;
275}
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*/
286bool QShortcutMap::tryShortcut(QKeyEvent *e)
287{
288 Q_D(QShortcutMap);
289
290 if (e->key() == Qt::Key_unknown)
291 return false;
292
293 QKeySequence::SequenceMatch previousState = state();
294
295 switch (nextState(e)) {
296 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 return previousState == QKeySequence::PartialMatch;
301 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 return true;
305 case QKeySequence::ExactMatch: {
306 // Save number of identical matches before dispatching
307 // to keep QShortcutMap and tryShortcut reentrant.
308 const int identicalMatches = d->identicals.size();
309 resetState();
310 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 return identicalMatches > 0;
314 }
315 }
316 Q_UNREACHABLE_RETURN(false);
317}
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*/
325QKeySequence::SequenceMatch QShortcutMap::nextState(QKeyEvent *e)
326{
327 Q_D(QShortcutMap);
328 // Modifiers can NOT be shortcuts...
329 if (e->key() >= Qt::Key_Shift &&
330 e->key() <= Qt::Key_ScrollLock)
331 return d->currentState;
332
333 QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
334
335 // We start fresh each time..
336 d->identicals.clear();
337
338 result = find(e);
339 if (result == QKeySequence::NoMatch && (e->modifiers() & Qt::KeypadModifier)) {
340 // Try to find a match without keypad modifier
341 result = find(e, ignoredModifiers: Qt::KeypadModifier);
342 }
343 if (result == QKeySequence::NoMatch && e->modifiers() & Qt::ShiftModifier) {
344 // If Shift + Key_Backtab, also try Shift + Qt::Key_Tab
345 if (e->key() == Qt::Key_Backtab) {
346 QKeyEvent pe = QKeyEvent(e->type(), Qt::Key_Tab, e->modifiers(), e->text());
347 result = find(e: &pe);
348 }
349 }
350
351 // Does the new state require us to clean up?
352 if (result == QKeySequence::NoMatch)
353 clearSequence(ksl&: d->currentSequences);
354 d->currentState = result;
355
356 qCDebug(lcShortcutMap).nospace() << "QShortcutMap::nextState(" << e << ") = " << result;
357 return result;
358}
359
360
361/*! \internal
362 Determines if an enabled shortcut has a matching key sequence.
363*/
364bool QShortcutMap::hasShortcutForKeySequence(const QKeySequence &seq) const
365{
366 Q_D(const QShortcutMap);
367 QShortcutEntry entry(seq); // needed for searching
368 const auto itEnd = d->sequences.cend();
369 auto it = std::lower_bound(first: d->sequences.cbegin(), last: itEnd, val: entry);
370
371 for (;it != itEnd; ++it) {
372 if (matches(seq1: entry.keyseq, seq2: (*it).keyseq) == QKeySequence::ExactMatch && (*it).correctContext() && (*it).enabled) {
373 return true;
374 }
375 }
376
377 //end of the loop: we didn't find anything
378 return false;
379}
380
381/*! \internal
382 Returns the next state of the statemachine, based
383 on the new key event \a e.
384 Matches are appended to the list of identicals,
385 which can be access through matches().
386 \sa matches
387*/
388QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e, int ignoredModifiers)
389{
390 Q_D(QShortcutMap);
391 if (!d->sequences.size())
392 return QKeySequence::NoMatch;
393
394 createNewSequences(e, ksl&: d->newEntries, ignoredModifiers);
395 qCDebug(lcShortcutMap) << "Possible shortcut key sequences:" << d->newEntries;
396
397 // Should never happen
398 if (d->newEntries == d->currentSequences) {
399 Q_ASSERT_X(e->key() != Qt::Key_unknown || e->text().size(),
400 "QShortcutMap::find", "New sequence to find identical to previous");
401 return QKeySequence::NoMatch;
402 }
403
404 // Looking for new identicals, scrap old
405 d->identicals.clear();
406
407 bool partialFound = false;
408 bool identicalDisabledFound = false;
409 QList<QKeySequence> okEntries;
410 int result = QKeySequence::NoMatch;
411 for (int i = d->newEntries.size()-1; i >= 0 ; --i) {
412 QShortcutEntry entry(d->newEntries.at(i)); // needed for searching
413 qCDebug(lcShortcutMap) << "- checking entry" << entry.id << entry.keyseq;
414 const auto itEnd = d->sequences.constEnd();
415 auto it = std::lower_bound(first: d->sequences.constBegin(), last: itEnd, val: entry);
416
417 int oneKSResult = QKeySequence::NoMatch;
418 int tempRes = QKeySequence::NoMatch;
419 do {
420 if (it == itEnd)
421 break;
422 tempRes = matches(seq1: entry.keyseq, seq2: (*it).keyseq);
423 oneKSResult = qMax(a: oneKSResult, b: tempRes);
424 qCDebug(lcShortcutMap) << " - matches returned" << tempRes << "for" << entry.keyseq << it->keyseq
425 << "- correctContext()?" << it->correctContext();
426 if (tempRes != QKeySequence::NoMatch && (*it).correctContext()) {
427 if (tempRes == QKeySequence::ExactMatch) {
428 if ((*it).enabled)
429 d->identicals.append(t: &*it);
430 else
431 identicalDisabledFound = true;
432 } else if (tempRes == QKeySequence::PartialMatch) {
433 // We don't need partials, if we have identicals
434 if (d->identicals.size())
435 break;
436 // We only care about enabled partials, so we don't consume
437 // key events when all partials are disabled!
438 partialFound |= (*it).enabled;
439 }
440 }
441 ++it;
442 // If we got a valid match on this run, there might still be more keys to check against,
443 // so we'll loop once more. If we get NoMatch, there's guaranteed no more possible
444 // matches in the shortcutmap.
445 } while (tempRes != QKeySequence::NoMatch);
446
447 // If the type of match improves (ergo, NoMatch->Partial, or Partial->Exact), clear the
448 // previous list. If this match is equal or better than the last match, append to the list
449 if (oneKSResult > result) {
450 okEntries.clear();
451 qCDebug(lcShortcutMap) << "Found better match (" << d->newEntries << "), clearing key sequence list";
452 }
453 if (oneKSResult && oneKSResult >= result) {
454 okEntries << d->newEntries.at(i);
455 qCDebug(lcShortcutMap) << "Added ok key sequence" << d->newEntries;
456 }
457 }
458
459 if (d->identicals.size()) {
460 result = QKeySequence::ExactMatch;
461 } else if (partialFound) {
462 result = QKeySequence::PartialMatch;
463 } else if (identicalDisabledFound) {
464 result = QKeySequence::ExactMatch;
465 } else {
466 clearSequence(ksl&: d->currentSequences);
467 result = QKeySequence::NoMatch;
468 }
469 if (result != QKeySequence::NoMatch)
470 d->currentSequences = okEntries;
471 qCDebug(lcShortcutMap) << "Returning shortcut match == " << result;
472 return QKeySequence::SequenceMatch(result);
473}
474
475/*! \internal
476 Clears \a seq to an empty QKeySequence.
477 Same as doing (the slower)
478 \snippet code/src_gui_kernel_qshortcutmap.cpp 0
479*/
480void QShortcutMap::clearSequence(QList<QKeySequence> &ksl)
481{
482 ksl.clear();
483 d_func()->newEntries.clear();
484}
485
486/*! \internal
487 Alters \a seq to the new sequence state, based on the
488 current sequence state, and the new key event \a e.
489*/
490void QShortcutMap::createNewSequences(QKeyEvent *e, QList<QKeySequence> &ksl, int ignoredModifiers)
491{
492 Q_D(QShortcutMap);
493 QList<int> possibleKeys = QKeyMapper::possibleKeys(e);
494 qCDebug(lcShortcutMap) << "Creating new sequences for" << e
495 << "with ignoredModifiers=" << Qt::KeyboardModifiers(ignoredModifiers);
496 int pkTotal = possibleKeys.size();
497 if (!pkTotal)
498 return;
499
500 int ssActual = d->currentSequences.size();
501 int ssTotal = qMax(a: 1, b: ssActual);
502 // Resize to possible permutations of the current sequence(s).
503 ksl.resize(size: pkTotal * ssTotal);
504
505 int index = ssActual ? d->currentSequences.at(i: 0).count() : 0;
506 for (int pkNum = 0; pkNum < pkTotal; ++pkNum) {
507 for (int ssNum = 0; ssNum < ssTotal; ++ssNum) {
508 int i = (pkNum * ssTotal) + ssNum;
509 QKeySequence &curKsl = ksl[i];
510 if (ssActual) {
511 const QKeySequence &curSeq = d->currentSequences.at(i: ssNum);
512 curKsl.setKey(key: curSeq[0], index: 0);
513 curKsl.setKey(key: curSeq[1], index: 1);
514 curKsl.setKey(key: curSeq[2], index: 2);
515 curKsl.setKey(key: curSeq[3], index: 3);
516 } else {
517 curKsl.setKey(key: QKeyCombination::fromCombined(combined: 0), index: 0);
518 curKsl.setKey(key: QKeyCombination::fromCombined(combined: 0), index: 1);
519 curKsl.setKey(key: QKeyCombination::fromCombined(combined: 0), index: 2);
520 curKsl.setKey(key: QKeyCombination::fromCombined(combined: 0), index: 3);
521 }
522 curKsl.setKey(key: QKeyCombination::fromCombined(combined: possibleKeys.at(i: pkNum) & ~ignoredModifiers), index);
523 }
524 }
525}
526
527/*! \internal
528 Basically the same function as QKeySequence::matches(const QKeySequence &seq) const
529 only that is specially handles Key_hyphen as Key_Minus, as people mix these up all the time and
530 they conceptually the same.
531*/
532QKeySequence::SequenceMatch QShortcutMap::matches(const QKeySequence &seq1,
533 const QKeySequence &seq2) const
534{
535 uint userN = seq1.count(),
536 seqN = seq2.count();
537
538 if (userN > seqN)
539 return QKeySequence::NoMatch;
540
541 // If equal in length, we have a potential ExactMatch sequence,
542 // else we already know it can only be partial.
543 QKeySequence::SequenceMatch match = (userN == seqN
544 ? QKeySequence::ExactMatch
545 : QKeySequence::PartialMatch);
546
547 for (uint i = 0; i < userN; ++i) {
548 int userKey = seq1[i].toCombined(),
549 sequenceKey = seq2[i].toCombined();
550 if ((userKey & Qt::Key_unknown) == Qt::Key_hyphen)
551 userKey = (userKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
552 if ((sequenceKey & Qt::Key_unknown) == Qt::Key_hyphen)
553 sequenceKey = (sequenceKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
554 if (userKey != sequenceKey)
555 return QKeySequence::NoMatch;
556 }
557 return match;
558}
559
560
561/*! \internal
562 Converts keyboard button states into modifier states
563*/
564int QShortcutMap::translateModifiers(Qt::KeyboardModifiers modifiers)
565{
566 int result = 0;
567 if (modifiers & Qt::ShiftModifier)
568 result |= Qt::SHIFT;
569 if (modifiers & Qt::ControlModifier)
570 result |= Qt::CTRL;
571 if (modifiers & Qt::MetaModifier)
572 result |= Qt::META;
573 if (modifiers & Qt::AltModifier)
574 result |= Qt::ALT;
575 return result;
576}
577
578/*! \internal
579 Returns the list of QShortcutEntry's matching the last Identical state.
580*/
581QList<const QShortcutEntry*> QShortcutMap::matches() const
582{
583 Q_D(const QShortcutMap);
584 return d->identicals;
585}
586
587/*! \internal
588 Dispatches QShortcutEvents to widgets who grabbed the matched key sequence.
589*/
590void QShortcutMap::dispatchEvent(QKeyEvent *e)
591{
592 Q_D(QShortcutMap);
593 if (!d->identicals.size())
594 return;
595
596 const QKeySequence &curKey = d->identicals.at(i: 0)->keyseq;
597 if (d->prevSequence != curKey) {
598 d->ambigCount = 0;
599 d->prevSequence = curKey;
600 }
601 // Find next
602 const QShortcutEntry *current = nullptr, *next = nullptr;
603 int i = 0, enabledShortcuts = 0;
604 QList<const QShortcutEntry*> ambiguousShortcuts;
605 while(i < d->identicals.size()) {
606 current = d->identicals.at(i);
607 if (current->enabled || !next){
608 ++enabledShortcuts;
609 if (lcShortcutMap().isDebugEnabled())
610 ambiguousShortcuts.append(t: current);
611 if (enabledShortcuts > d->ambigCount + 1)
612 break;
613 next = current;
614 }
615 ++i;
616 }
617 d->ambigCount = (d->identicals.size() == i ? 0 : d->ambigCount + 1);
618 // Don't trigger shortcut if we're autorepeating and the shortcut is
619 // grabbed with not accepting autorepeats.
620 if (!next || (e->isAutoRepeat() && !next->autorepeat))
621 return;
622 // Dispatch next enabled
623 if (lcShortcutMap().isDebugEnabled()) {
624 if (ambiguousShortcuts.size() > 1) {
625 qCDebug(lcShortcutMap) << "The following shortcuts are about to be activated ambiguously:";
626 for (const QShortcutEntry *entry : std::as_const(t&: ambiguousShortcuts))
627 qCDebug(lcShortcutMap).nospace() << "- " << entry->keyseq << " (belonging to " << entry->owner << ")";
628 }
629
630 qCDebug(lcShortcutMap).nospace()
631 << "QShortcutMap::dispatchEvent(): Sending QShortcutEvent(\""
632 << next->keyseq.toString() << "\", " << next->id << ", "
633 << static_cast<bool>(enabledShortcuts>1) << ") to object(" << next->owner << ')';
634 }
635 QShortcutEvent se(next->keyseq, next->id, enabledShortcuts>1);
636 QCoreApplication::sendEvent(receiver: const_cast<QObject *>(next->owner), event: &se);
637}
638
639QList<QKeySequence> QShortcutMap::keySequences(bool getAll) const
640{
641 Q_D(const QShortcutMap);
642 QList<QKeySequence> keys;
643 for (auto sequence : d->sequences) {
644 bool addSequence = false;
645 if (sequence.enabled) {
646 if (getAll || sequence.context == Qt::ApplicationShortcut ||
647 sequence.owner == QGuiApplication::focusObject()) {
648 addSequence = true;
649 } else {
650 QObject *possibleWindow = sequence.owner;
651 while (possibleWindow) {
652 if (qobject_cast<QWindow *>(o: possibleWindow))
653 break;
654 possibleWindow = possibleWindow->parent();
655 }
656 if (possibleWindow == QGuiApplication::focusWindow()) {
657 if (sequence.context == Qt::WindowShortcut) {
658 addSequence = true;
659 } else if (sequence.context == Qt::WidgetWithChildrenShortcut) {
660 QObject *possibleWidget = QGuiApplication::focusObject();
661 while (possibleWidget->parent()) {
662 possibleWidget = possibleWidget->parent();
663 if (possibleWidget == sequence.owner) {
664 addSequence = true;
665 break;
666 }
667 }
668 }
669 }
670 }
671 if (addSequence)
672 keys << sequence.keyseq;
673 }
674 }
675 return keys;
676
677}
678
679/* \internal
680 QShortcutMap dump function, only available when DEBUG_QSHORTCUTMAP is
681 defined.
682*/
683#if defined(Dump_QShortcutMap)
684void QShortcutMap::dumpMap() const
685{
686 Q_D(const QShortcutMap);
687 for (int i = 0; i < d->sequences.size(); ++i)
688 qDebug().nospace() << &(d->sequences.at(i));
689}
690#endif
691
692QT_END_NAMESPACE
693

source code of qtbase/src/gui/kernel/qshortcutmap.cpp