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