src/gui/kernel/qshortcutmap.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    62 QT_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 */
       
    73 struct 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 */
       
   106 static 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 */
       
   120 class QShortcutMapPrivate
       
   121 {
       
   122     Q_DECLARE_PUBLIC(QShortcutMap)
       
   123 
       
   124 public:
       
   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 */
       
   148 QShortcutMap::QShortcutMap()
       
   149     : d_ptr(new QShortcutMapPrivate(this))
       
   150 {
       
   151     resetState();
       
   152 }
       
   153 
       
   154 /*! \internal
       
   155     QShortcutMap destructor.
       
   156 */
       
   157 QShortcutMap::~QShortcutMap()
       
   158 {
       
   159 }
       
   160 
       
   161 /*! \internal
       
   162     Adds a shortcut to the global map.
       
   163     Returns the id of the newly added shortcut.
       
   164 */
       
   165 int 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 
       
   191 int 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 */
       
   237 int 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 */
       
   275 int 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 */
       
   308 void 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 */
       
   318 QKeySequence::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 */
       
   330 bool 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 */
       
   379 QKeySequence::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 */
       
   420 bool 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 */
       
   444 QKeySequence::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 */
       
   542 void 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 */
       
   552 void 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 */
       
   593 QKeySequence::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 */
       
   625 bool 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 
       
   654 bool 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
       
   717 bool 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
       
   777 bool 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 */
       
   816 int 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 */
       
   833 QVector<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 */
       
   842 void 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)
       
   887 void 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 
       
   895 QT_END_NAMESPACE
       
   896 
       
   897 #endif // QT_NO_SHORTCUT