source: trunk/src/gui/kernel/qshortcutmap.cpp@ 624

Last change on this file since 624 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 29.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qshortcutmap_p.h"
43#include "private/qobject_p.h"
44#include "qkeysequence.h"
45#include "qgraphicsscene.h"
46#include "qgraphicsview.h"
47#include "qdebug.h"
48#include "qevent.h"
49#include "qwidget.h"
50#include "qapplication.h"
51#include "qvector.h"
52#include "qmenu.h"
53#include "qmenubar.h"
54#include "qshortcut.h"
55#include "qapplication_p.h"
56#include <private/qaction_p.h>
57#include <private/qkeymapper_p.h>
58#include <private/qwidget_p.h>
59
60#ifndef QT_NO_SHORTCUT
61
62QT_BEGIN_NAMESPACE
63
64// To enable verbose output uncomment below
65//#define DEBUG_QSHORTCUTMAP
66
67/* \internal
68 Entry data for QShortcutMap
69 Contains:
70 Keysequence for entry
71 Pointer to parent owning the sequence
72*/
73struct QShortcutEntry
74{
75 QShortcutEntry()
76 : keyseq(0), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(0)
77 {}
78
79 QShortcutEntry(const QKeySequence &k)
80 : keyseq(k), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(0)
81 {}
82
83 QShortcutEntry(QObject *o, const QKeySequence &k, Qt::ShortcutContext c, int i)
84 : keyseq(k), context(c), enabled(true), autorepeat(1), id(i), owner(o)
85 {}
86
87 QShortcutEntry(QObject *o, const QKeySequence &k, Qt::ShortcutContext c, int i, bool a)
88 : keyseq(k), context(c), enabled(true), autorepeat(a), id(i), owner(o)
89 {}
90
91 bool operator<(const QShortcutEntry &f) const
92 { return keyseq < f.keyseq; }
93
94 QKeySequence keyseq;
95 Qt::ShortcutContext context;
96 bool enabled : 1;
97 bool autorepeat : 1;
98 signed int id;
99 QObject *owner;
100};
101
102#if 0 //ndef QT_NO_DEBUG_STREAM
103/*! \internal
104 QDebug operator<< for easy debug output of the shortcut entries.
105*/
106static QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) {
107 if (!se)
108 return dbg << "QShortcutEntry(0x0)";
109 dbg.nospace()
110 << "QShortcutEntry(" << se->keyseq
111 << "), id(" << se->id << "), enabled(" << se->enabled << "), autorepeat(" << se->autorepeat
112 << "), owner(" << se->owner << ')';
113 return dbg.space();
114}
115#endif // QT_NO_DEBUGSTREAM
116
117/* \internal
118 Private data for QShortcutMap
119*/
120class QShortcutMapPrivate
121{
122 Q_DECLARE_PUBLIC(QShortcutMap)
123
124public:
125 QShortcutMapPrivate(QShortcutMap* parent)
126 : q_ptr(parent), currentId(0), ambigCount(0), currentState(QKeySequence::NoMatch)
127 {
128 identicals.reserve(10);
129 currentSequences.reserve(10);
130 }
131 QShortcutMap *q_ptr; // Private's parent
132
133 QList<QShortcutEntry> sequences; // All sequences!
134
135 int currentId; // Global shortcut ID number
136 int ambigCount; // Index of last enabled ambiguous dispatch
137 QKeySequence::SequenceMatch currentState;
138 QVector<QKeySequence> currentSequences; // Sequence for the current state
139 QVector<QKeySequence> newEntries;
140 QKeySequence prevSequence; // Sequence for the previous identical match
141 QVector<const QShortcutEntry*> identicals; // Last identical matches
142};
143
144
145/*! \internal
146 QShortcutMap constructor.
147*/
148QShortcutMap::QShortcutMap()
149 : d_ptr(new QShortcutMapPrivate(this))
150{
151 resetState();
152}
153
154/*! \internal
155 QShortcutMap destructor.
156*/
157QShortcutMap::~QShortcutMap()
158{
159}
160
161/*! \internal
162 Adds a shortcut to the global map.
163 Returns the id of the newly added shortcut.
164*/
165int QShortcutMap::addShortcut(QObject *owner, const QKeySequence &key, Qt::ShortcutContext context)
166{
167 Q_ASSERT_X(owner, "QShortcutMap::addShortcut", "All shortcuts need an owner");
168 Q_ASSERT_X(!key.isEmpty(), "QShortcutMap::addShortcut", "Cannot add keyless shortcuts to map");
169 Q_D(QShortcutMap);
170
171 QShortcutEntry newEntry(owner, key, context, --(d->currentId), true);
172 QList<QShortcutEntry>::iterator it = qUpperBound(d->sequences.begin(), d->sequences.end(), newEntry);
173 d->sequences.insert(it, newEntry); // Insert sorted
174#if defined(DEBUG_QSHORTCUTMAP)
175 qDebug().nospace()
176 << "QShortcutMap::addShortcut(" << owner << ", "
177 << key << ", " << context << ") = " << d->currentId;
178#endif
179 return d->currentId;
180}
181
182/*! \internal
183 Removes a shortcut from the global map.
184 If \a owner is 0, all entries in the map with the key sequence specified
185 is removed. If \a key is null, all sequences for \a owner is removed from
186 the map. If \a id is 0, any identical \a key sequences owned by \a owner
187 are removed.
188 Returns the number of sequences removed from the map.
189*/
190
191int QShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &key)
192{
193 Q_D(QShortcutMap);
194 int itemsRemoved = 0;
195 bool allOwners = (owner == 0);
196 bool allKeys = key.isEmpty();
197 bool allIds = id == 0;
198
199 // Special case, remove everything
200 if (allOwners && allKeys && id == 0) {
201 itemsRemoved = d->sequences.size();
202 d->sequences.clear();
203 return itemsRemoved;
204 }
205
206 int i = d->sequences.size()-1;
207 while (i>=0)
208 {
209 const QShortcutEntry &entry = d->sequences.at(i);
210 int entryId = entry.id;
211 if ((allOwners || entry.owner == owner)
212 && (allIds || entry.id == id)
213 && (allKeys || entry.keyseq == key)) {
214 d->sequences.removeAt(i);
215 ++itemsRemoved;
216 }
217 if (id == entryId)
218 return itemsRemoved;
219 --i;
220 }
221#if defined(DEBUG_QSHORTCUTMAP)
222 qDebug().nospace()
223 << "QShortcutMap::removeShortcut(" << id << ", " << owner << ", "
224 << key << ") = " << itemsRemoved;
225#endif
226 return itemsRemoved;
227}
228
229/*! \internal
230 Changes the enable state of a shortcut to \a enable.
231 If \a owner is 0, all entries in the map with the key sequence specified
232 is removed. If \a key is null, all sequences for \a owner is removed from
233 the map. If \a id is 0, any identical \a key sequences owned by \a owner
234 are changed.
235 Returns the number of sequences which are matched in the map.
236*/
237int QShortcutMap::setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &key)
238{
239 Q_D(QShortcutMap);
240 int itemsChanged = 0;
241 bool allOwners = (owner == 0);
242 bool allKeys = key.isEmpty();
243 bool allIds = id == 0;
244
245 int i = d->sequences.size()-1;
246 while (i>=0)
247 {
248 QShortcutEntry entry = d->sequences.at(i);
249 if ((allOwners || entry.owner == owner)
250 && (allIds || entry.id == id)
251 && (allKeys || entry.keyseq == key)) {
252 d->sequences[i].enabled = enable;
253 ++itemsChanged;
254 }
255 if (id == entry.id)
256 return itemsChanged;
257 --i;
258 }
259#if defined(DEBUG_QSHORTCUTMAP)
260 qDebug().nospace()
261 << "QShortcutMap::setShortcutEnabled(" << enable << ", " << id << ", "
262 << owner << ", " << key << ") = " << itemsChanged;
263#endif
264 return itemsChanged;
265}
266
267/*! \internal
268 Changes the auto repeat state of a shortcut to \a enable.
269 If \a owner is 0, all entries in the map with the key sequence specified
270 is removed. If \a key is null, all sequences for \a owner is removed from
271 the map. If \a id is 0, any identical \a key sequences owned by \a owner
272 are changed.
273 Returns the number of sequences which are matched in the map.
274*/
275int QShortcutMap::setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &key)
276{
277 Q_D(QShortcutMap);
278 int itemsChanged = 0;
279 bool allOwners = (owner == 0);
280 bool allKeys = key.isEmpty();
281 bool allIds = id == 0;
282
283 int i = d->sequences.size()-1;
284 while (i>=0)
285 {
286 QShortcutEntry entry = d->sequences.at(i);
287 if ((allOwners || entry.owner == owner)
288 && (allIds || entry.id == id)
289 && (allKeys || entry.keyseq == key)) {
290 d->sequences[i].autorepeat = on;
291 ++itemsChanged;
292 }
293 if (id == entry.id)
294 return itemsChanged;
295 --i;
296 }
297#if defined(DEBUG_QSHORTCUTMAP)
298 qDebug().nospace()
299 << "QShortcutMap::setShortcutAutoRepeat(" << on << ", " << id << ", "
300 << owner << ", " << key << ") = " << itemsChanged;
301#endif
302 return itemsChanged;
303}
304
305/*! \internal
306 Resets the state of the statemachine to NoMatch
307*/
308void QShortcutMap::resetState()
309{
310 Q_D(QShortcutMap);
311 d->currentState = QKeySequence::NoMatch;
312 clearSequence(d->currentSequences);
313}
314
315/*! \internal
316 Returns the current state of the statemachine
317*/
318QKeySequence::SequenceMatch QShortcutMap::state()
319{
320 Q_D(QShortcutMap);
321 return d->currentState;
322}
323
324/*! \internal
325 Uses ShortcutOverride event to see if any widgets want to override
326 the event. If not, uses nextState(QKeyEvent) to check for a grabbed
327 Shortcut, and dispatchEvent() is found an identical.
328 \sa nextState dispatchEvent
329*/
330bool QShortcutMap::tryShortcutEvent(QObject *o, QKeyEvent *e)
331{
332 Q_D(QShortcutMap);
333
334 bool wasAccepted = e->isAccepted();
335 bool wasSpontaneous = e->spont;
336 if (d->currentState == QKeySequence::NoMatch) {
337 ushort orgType = e->t;
338 e->t = QEvent::ShortcutOverride;
339 e->ignore();
340 QApplication::sendEvent(o, e);
341 e->t = orgType;
342 e->spont = wasSpontaneous;
343 if (e->isAccepted()) {
344 if (!wasAccepted)
345 e->ignore();
346 return false;
347 }
348 }
349
350 QKeySequence::SequenceMatch result = nextState(e);
351 bool stateWasAccepted = e->isAccepted();
352 if (wasAccepted)
353 e->accept();
354 else
355 e->ignore();
356
357 int identicalMatches = d->identicals.count();
358
359 switch(result) {
360 case QKeySequence::NoMatch:
361 return stateWasAccepted;
362 case QKeySequence::ExactMatch:
363 resetState();
364 dispatchEvent(e);
365 default:
366 break;
367 }
368 // If nextState is QKeySequence::ExactMatch && identicals.count == 0
369 // we've only found disabled shortcuts
370 return identicalMatches > 0 || result == QKeySequence::PartialMatch;
371}
372
373/*! \internal
374 Returns the next state of the statemachine
375 If return value is SequenceMatch::ExactMatch, then a call to matches()
376 will return a QObjects* list of all matching objects for the last matching
377 sequence.
378*/
379QKeySequence::SequenceMatch QShortcutMap::nextState(QKeyEvent *e)
380{
381 Q_D(QShortcutMap);
382 // Modifiers can NOT be shortcuts...
383 if (e->key() >= Qt::Key_Shift &&
384 e->key() <= Qt::Key_Alt)
385 return d->currentState;
386
387 QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
388
389 // We start fresh each time..
390 d->identicals.resize(0);
391
392 result = find(e);
393 if (result == QKeySequence::NoMatch && e->modifiers() & Qt::ShiftModifier) {
394 // If Shift + Key_Backtab, also try Shift + Qt::Key_Tab
395 if (e->key() == Qt::Key_Backtab) {
396 QKeyEvent pe = QKeyEvent(e->type(), Qt::Key_Tab, e->modifiers(), e->text());
397 result = find(&pe);
398 }
399 }
400
401 // Should we eat this key press?
402 if (d->currentState == QKeySequence::PartialMatch
403 || (d->currentState == QKeySequence::ExactMatch && d->identicals.count()))
404 e->accept();
405 // Does the new state require us to clean up?
406 if (result == QKeySequence::NoMatch)
407 clearSequence(d->currentSequences);
408 d->currentState = result;
409
410#if defined(DEBUG_QSHORTCUTMAP)
411 qDebug().nospace() << "QShortcutMap::nextState(" << e << ") = " << result;
412#endif
413 return result;
414}
415
416
417/*! \internal
418 Determines if an enabled shortcut has a matcing key sequence.
419*/
420bool QShortcutMap::hasShortcutForKeySequence(const QKeySequence &seq) const
421{
422 Q_D(const QShortcutMap);
423 QShortcutEntry entry(seq); // needed for searching
424 QList<QShortcutEntry>::ConstIterator itEnd = d->sequences.constEnd();
425 QList<QShortcutEntry>::ConstIterator it = qLowerBound(d->sequences.constBegin(), itEnd, entry);
426
427 for (;it != itEnd; ++it) {
428 if (matches(entry.keyseq, (*it).keyseq) == QKeySequence::ExactMatch && correctContext(*it) && (*it).enabled) {
429 return true;
430 }
431 }
432
433 //end of the loop: we didn't find anything
434 return false;
435}
436
437/*! \internal
438 Returns the next state of the statemachine, based
439 on the new key event \a e.
440 Matches are appended to the vector of identicals,
441 which can be access through matches().
442 \sa matches
443*/
444QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e)
445{
446 Q_D(QShortcutMap);
447 if (!d->sequences.count())
448 return QKeySequence::NoMatch;
449
450 createNewSequences(e, d->newEntries);
451#if defined(DEBUG_QSHORTCUTMAP)
452 qDebug() << "Possible shortcut key sequences:" << d->newEntries;
453#endif
454
455 // Should never happen
456 if (d->newEntries == d->currentSequences) {
457 Q_ASSERT_X(e->key() != Qt::Key_unknown || e->text().length(),
458 "QShortcutMap::find", "New sequence to find identical to previous");
459 return QKeySequence::NoMatch;
460 }
461
462 // Looking for new identicals, scrap old
463 d->identicals.resize(0);
464
465 bool partialFound = false;
466 bool identicalDisabledFound = false;
467 QVector<QKeySequence> okEntries;
468 int result = QKeySequence::NoMatch;
469 for (int i = d->newEntries.count()-1; i >= 0 ; --i) {
470 QShortcutEntry entry(d->newEntries.at(i)); // needed for searching
471 QList<QShortcutEntry>::ConstIterator itEnd = d->sequences.constEnd();
472 QList<QShortcutEntry>::ConstIterator it =
473 qLowerBound(d->sequences.constBegin(), itEnd, entry);
474
475 int oneKSResult = QKeySequence::NoMatch;
476 int tempRes = QKeySequence::NoMatch;
477 do {
478 if (it == itEnd)
479 break;
480 tempRes = matches(entry.keyseq, (*it).keyseq);
481 oneKSResult = qMax(oneKSResult, tempRes);
482 if (tempRes != QKeySequence::NoMatch && correctContext(*it)) {
483 if (tempRes == QKeySequence::ExactMatch) {
484 if ((*it).enabled)
485 d->identicals.append(&*it);
486 else
487 identicalDisabledFound = true;
488 } else if (tempRes == QKeySequence::PartialMatch) {
489 // We don't need partials, if we have identicals
490 if (d->identicals.size())
491 break;
492 // We only care about enabled partials, so we don't consume
493 // key events when all partials are disabled!
494 partialFound |= (*it).enabled;
495 }
496 }
497 ++it;
498 // If we got a valid match on this run, there might still be more keys to check against,
499 // so we'll loop once more. If we get NoMatch, there's guaranteed no more possible
500 // matches in the shortcutmap.
501 } while (tempRes != QKeySequence::NoMatch);
502
503 // If the type of match improves (ergo, NoMatch->Partial, or Partial->Exact), clear the
504 // previous list. If this match is equal or better than the last match, append to the list
505 if (oneKSResult > result) {
506 okEntries.clear();
507#if defined(DEBUG_QSHORTCUTMAP)
508 qDebug() << "Found better match (" << d->newEntries << "), clearing key sequence list";
509#endif
510 }
511 if (oneKSResult && oneKSResult >= result) {
512 okEntries << d->newEntries.at(i);
513#if defined(DEBUG_QSHORTCUTMAP)
514 qDebug() << "Added ok key sequence" << d->newEntries;
515#endif
516 }
517 }
518
519 if (d->identicals.size()) {
520 result = QKeySequence::ExactMatch;
521 } else if (partialFound) {
522 result = QKeySequence::PartialMatch;
523 } else if (identicalDisabledFound) {
524 result = QKeySequence::ExactMatch;
525 } else {
526 clearSequence(d->currentSequences);
527 result = QKeySequence::NoMatch;
528 }
529 if (result != QKeySequence::NoMatch)
530 d->currentSequences = okEntries;
531#if defined(DEBUG_QSHORTCUTMAP)
532 qDebug() << "Returning shortcut match == " << result;
533#endif
534 return QKeySequence::SequenceMatch(result);
535}
536
537/*! \internal
538 Clears \a seq to an empty QKeySequence.
539 Same as doing (the slower)
540 \snippet doc/src/snippets/code/src_gui_kernel_qshortcutmap.cpp 0
541*/
542void QShortcutMap::clearSequence(QVector<QKeySequence> &ksl)
543{
544 ksl.clear();
545 d_func()->newEntries.clear();
546}
547
548/*! \internal
549 Alters \a seq to the new sequence state, based on the
550 current sequence state, and the new key event \a e.
551*/
552void QShortcutMap::createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl)
553{
554 Q_D(QShortcutMap);
555 QList<int> possibleKeys = QKeyMapper::possibleKeys(e);
556 int pkTotal = possibleKeys.count();
557 if (!pkTotal)
558 return;
559
560 int ssActual = d->currentSequences.count();
561 int ssTotal = qMax(1, ssActual);
562 // Resize to possible permutations of the current sequence(s).
563 ksl.resize(pkTotal * ssTotal);
564
565 int index = ssActual ? d->currentSequences.at(0).count() : 0;
566 for (int pkNum = 0; pkNum < pkTotal; ++pkNum) {
567 for (int ssNum = 0; ssNum < ssTotal; ++ssNum) {
568 int i = (pkNum * ssTotal) + ssNum;
569 QKeySequence &curKsl = ksl[i];
570 if (ssActual) {
571 const QKeySequence &curSeq = d->currentSequences.at(ssNum);
572 curKsl.setKey(curSeq[0], 0);
573 curKsl.setKey(curSeq[1], 1);
574 curKsl.setKey(curSeq[2], 2);
575 curKsl.setKey(curSeq[3], 3);
576 } else {
577 curKsl.setKey(0, 0);
578 curKsl.setKey(0, 1);
579 curKsl.setKey(0, 2);
580 curKsl.setKey(0, 3);
581 }
582 // Filtering keycode here with 0xdfffffff to ignore the Keypad modifier
583 curKsl.setKey(possibleKeys.at(pkNum) & 0xdfffffff, index);
584 }
585 }
586}
587
588/*! \internal
589 Basically the same function as QKeySequence::matches(const QKeySequence &seq) const
590 only that is specially handles Key_hyphen as Key_Minus, as people mix these up all the time and
591 they conceptually the same.
592*/
593QKeySequence::SequenceMatch QShortcutMap::matches(const QKeySequence &seq1,
594 const QKeySequence &seq2) const
595{
596 uint userN = seq1.count(),
597 seqN = seq2.count();
598
599 if (userN > seqN)
600 return QKeySequence::NoMatch;
601
602 // If equal in length, we have a potential ExactMatch sequence,
603 // else we already know it can only be partial.
604 QKeySequence::SequenceMatch match = (userN == seqN
605 ? QKeySequence::ExactMatch
606 : QKeySequence::PartialMatch);
607
608 for (uint i = 0; i < userN; ++i) {
609 int userKey = seq1[i],
610 sequenceKey = seq2[i];
611 if ((userKey & Qt::Key_unknown) == Qt::Key_hyphen)
612 userKey = (userKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
613 if ((sequenceKey & Qt::Key_unknown) == Qt::Key_hyphen)
614 sequenceKey = (sequenceKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
615 if (userKey != sequenceKey)
616 return QKeySequence::NoMatch;
617 }
618 return match;
619}
620
621/*! \internal
622 Returns true if the widget \a w is a logical sub window of the current
623 top-level widget.
624*/
625bool QShortcutMap::correctContext(const QShortcutEntry &item) const {
626 Q_ASSERT_X(item.owner, "QShortcutMap", "Shortcut has no owner. Illegal map state!");
627
628 QWidget *active_window = QApplication::activeWindow();
629
630 // popups do not become the active window,
631 // so we fake it here to get the correct context
632 // for the shortcut system.
633 if (QApplication::activePopupWidget())
634 active_window = QApplication::activePopupWidget();
635
636 if (!active_window)
637 return false;
638#ifndef QT_NO_ACTION
639 if (QAction *a = qobject_cast<QAction *>(item.owner))
640 return correctContext(item.context, a, active_window);
641#endif
642#ifndef QT_NO_GRAPHICSVIEW
643 if (QGraphicsWidget *gw = qobject_cast<QGraphicsWidget *>(item.owner))
644 return correctGraphicsWidgetContext(item.context, gw, active_window);
645#endif
646 QWidget *w = qobject_cast<QWidget *>(item.owner);
647 if (!w) {
648 QShortcut *s = qobject_cast<QShortcut *>(item.owner);
649 w = s->parentWidget();
650 }
651 return correctWidgetContext(item.context, w, active_window);
652}
653
654bool QShortcutMap::correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidget *active_window) const
655{
656 bool visible = w->isVisible();
657#ifdef Q_WS_MAC
658 if (!qApp->testAttribute(Qt::AA_DontUseNativeMenuBar) && qobject_cast<QMenuBar *>(w))
659 visible = true;
660#endif
661
662 if (!visible || !w->isEnabled())
663 return false;
664
665 if (context == Qt::ApplicationShortcut)
666 return QApplicationPrivate::tryModalHelper(w, 0); // true, unless w is shadowed by a modal dialog
667
668 if (context == Qt::WidgetShortcut)
669 return w == QApplication::focusWidget();
670
671 if (context == Qt::WidgetWithChildrenShortcut) {
672 const QWidget *tw = QApplication::focusWidget();
673 while (tw && tw != w && (tw->windowType() == Qt::Widget || tw->windowType() == Qt::Popup))
674 tw = tw->parentWidget();
675 return tw == w;
676 }
677
678 // Below is Qt::WindowShortcut context
679 QWidget *tlw = w->window();
680#ifndef QT_NO_GRAPHICSVIEW
681 if (QWExtra *topData = tlw->d_func()->extra) {
682 if (topData->proxyWidget) {
683 bool res = correctGraphicsWidgetContext(context, (QGraphicsWidget *)topData->proxyWidget, active_window);
684 return res;
685 }
686 }
687#endif
688
689 /* if a floating tool window is active, keep shortcuts on the
690 * parent working */
691 if (active_window != tlw && active_window && active_window->windowType() == Qt::Tool && active_window->parentWidget()) {
692 active_window = active_window->parentWidget()->window();
693 }
694
695 if (active_window != tlw)
696 return false;
697
698 /* if we live in a MDI subwindow, ignore the event if we are
699 not the active document window */
700 const QWidget* sw = w;
701 while (sw && !(sw->windowType() == Qt::SubWindow) && !sw->isWindow())
702 sw = sw->parentWidget();
703 if (sw && (sw->windowType() == Qt::SubWindow)) {
704 QWidget *focus_widget = QApplication::focusWidget();
705 while (focus_widget && focus_widget != sw)
706 focus_widget = focus_widget->parentWidget();
707 return sw == focus_widget;
708 }
709
710#if defined(DEBUG_QSHORTCUTMAP)
711 qDebug().nospace() << "..true [Pass-through]";
712#endif
713 return true;
714}
715
716#ifndef QT_NO_GRAPHICSVIEW
717bool QShortcutMap::correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsWidget *w, QWidget *active_window) const
718{
719 bool visible = w->isVisible();
720#ifdef Q_WS_MAC
721 if (!qApp->testAttribute(Qt::AA_DontUseNativeMenuBar) && qobject_cast<QMenuBar *>(w))
722 visible = true;
723#endif
724
725 if (!visible || !w->isEnabled() || !w->scene())
726 return false;
727
728 if (context == Qt::ApplicationShortcut) {
729 // Applicationwide shortcuts are always reachable unless their owner
730 // is shadowed by modality. In QGV there's no modality concept, but we
731 // must still check if all views are shadowed.
732 QList<QGraphicsView *> views = w->scene()->views();
733 for (int i = 0; i < views.size(); ++i) {
734 if (QApplicationPrivate::tryModalHelper(views.at(i), 0))
735 return true;
736 }
737 return false;
738 }
739
740 if (context == Qt::WidgetShortcut)
741 return static_cast<QGraphicsItem *>(w) == w->scene()->focusItem();
742
743 if (context == Qt::WidgetWithChildrenShortcut) {
744 const QGraphicsItem *ti = w->scene()->focusItem();
745 if (ti && ti->isWidget()) {
746 const QGraphicsWidget *tw = static_cast<const QGraphicsWidget *>(ti);
747 while (tw && tw != w && (tw->windowType() == Qt::Widget || tw->windowType() == Qt::Popup))
748 tw = tw->parentWidget();
749 return tw == w;
750 }
751 return false;
752 }
753
754 // Below is Qt::WindowShortcut context
755
756 // Find the active view (if any).
757 QList<QGraphicsView *> views = w->scene()->views();
758 QGraphicsView *activeView = 0;
759 for (int i = 0; i < views.size(); ++i) {
760 QGraphicsView *view = views.at(i);
761 if (view->window() == active_window) {
762 activeView = view;
763 break;
764 }
765 }
766 if (!activeView)
767 return false;
768
769 // The shortcut is reachable if owned by a windowless widget, or if the
770 // widget's window is the same as the focus item's window.
771 QGraphicsWidget *a = w->scene()->activeWindow();
772 return !w->window() || a == w->window();
773}
774#endif
775
776#ifndef QT_NO_ACTION
777bool QShortcutMap::correctContext(Qt::ShortcutContext context, QAction *a, QWidget *active_window) const
778{
779 const QList<QWidget *> &widgets = a->d_func()->widgets;
780#if defined(DEBUG_QSHORTCUTMAP)
781 if (widgets.isEmpty())
782 qDebug() << a << "not connected to any widgets; won't trigger";
783#endif
784 for (int i = 0; i < widgets.size(); ++i) {
785 QWidget *w = widgets.at(i);
786#ifndef QT_NO_MENU
787 if (QMenu *menu = qobject_cast<QMenu *>(w)) {
788 QAction *a = menu->menuAction();
789 if (correctContext(context, a, active_window))
790 return true;
791 } else
792#endif
793 if (correctWidgetContext(context, w, active_window))
794 return true;
795 }
796
797#ifndef QT_NO_GRAPHICSVIEW
798 const QList<QGraphicsWidget *> &graphicsWidgets = a->d_func()->graphicsWidgets;
799#if defined(DEBUG_QSHORTCUTMAP)
800 if (graphicsWidgets.isEmpty())
801 qDebug() << a << "not connected to any widgets; won't trigger";
802#endif
803 for (int i = 0; i < graphicsWidgets.size(); ++i) {
804 QGraphicsWidget *w = graphicsWidgets.at(i);
805 if (correctGraphicsWidgetContext(context, w, active_window))
806 return true;
807 }
808#endif
809 return false;
810}
811#endif // QT_NO_ACTION
812
813/*! \internal
814 Converts keyboard button states into modifier states
815*/
816int QShortcutMap::translateModifiers(Qt::KeyboardModifiers modifiers)
817{
818 int result = 0;
819 if (modifiers & Qt::ShiftModifier)
820 result |= Qt::SHIFT;
821 if (modifiers & Qt::ControlModifier)
822 result |= Qt::CTRL;
823 if (modifiers & Qt::MetaModifier)
824 result |= Qt::META;
825 if (modifiers & Qt::AltModifier)
826 result |= Qt::ALT;
827 return result;
828}
829
830/*! \internal
831 Returns the vector of QShortcutEntry's matching the last Identical state.
832*/
833QVector<const QShortcutEntry*> QShortcutMap::matches() const
834{
835 Q_D(const QShortcutMap);
836 return d->identicals;
837}
838
839/*! \internal
840 Dispatches QShortcutEvents to widgets who grabbed the matched key sequence.
841*/
842void QShortcutMap::dispatchEvent(QKeyEvent *e)
843{
844 Q_D(QShortcutMap);
845 if (!d->identicals.size())
846 return;
847
848 const QKeySequence &curKey = d->identicals.at(0)->keyseq;
849 if (d->prevSequence != curKey) {
850 d->ambigCount = 0;
851 d->prevSequence = curKey;
852 }
853 // Find next
854 const QShortcutEntry *current = 0, *next = 0;
855 int i = 0, enabledShortcuts = 0;
856 while(i < d->identicals.size()) {
857 current = d->identicals.at(i);
858 if (current->enabled || !next){
859 ++enabledShortcuts;
860 if (enabledShortcuts > d->ambigCount + 1)
861 break;
862 next = current;
863 }
864 ++i;
865 }
866 d->ambigCount = (d->identicals.size() == i ? 0 : d->ambigCount + 1);
867 // Don't trigger shortcut if we're autorepeating and the shortcut is
868 // grabbed with not accepting autorepeats.
869 if (!next || (e->isAutoRepeat() && !next->autorepeat))
870 return;
871 // Dispatch next enabled
872#if defined(DEBUG_QSHORTCUTMAP)
873 qDebug().nospace()
874 << "QShortcutMap::dispatchEvent(): Sending QShortcutEvent(\""
875 << (QString)next->keyseq << "\", " << next->id << ", "
876 << (bool)(enabledShortcuts>1) << ") to object(" << next->owner << ')';
877#endif
878 QShortcutEvent se(next->keyseq, next->id, enabledShortcuts>1);
879 QApplication::sendEvent(const_cast<QObject *>(next->owner), &se);
880}
881
882/* \internal
883 QShortcutMap dump function, only available when DEBUG_QSHORTCUTMAP is
884 defined.
885*/
886#if defined(Dump_QShortcutMap)
887void QShortcutMap::dumpMap() const
888{
889 Q_D(const QShortcutMap);
890 for (int i = 0; i < d->sequences.size(); ++i)
891 qDebug().nospace() << &(d->sequences.at(i));
892}
893#endif
894
895QT_END_NAMESPACE
896
897#endif // QT_NO_SHORTCUT
Note: See TracBrowser for help on using the repository browser.