src/qt3support/other/q3accel.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 Qt3Support 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 "q3accel.h"
       
    43 
       
    44 #include "q3signal.h"
       
    45 #include "qapplication.h"
       
    46 #include "qwidget.h"
       
    47 #include "q3ptrlist.h"
       
    48 #include "qwhatsthis.h"
       
    49 #include "qpointer.h"
       
    50 #include "qstatusbar.h"
       
    51 #include "qdockwidget.h"
       
    52 #include "qevent.h"
       
    53 #include "qkeysequence.h"
       
    54 #include "private/qapplication_p.h"
       
    55 
       
    56 QT_BEGIN_NAMESPACE
       
    57 
       
    58 using namespace Qt;
       
    59 
       
    60 /*!
       
    61     \class Q3Accel
       
    62     \brief The Q3Accel class handles keyboard accelerator and shortcut keys.
       
    63 
       
    64     \compat
       
    65 
       
    66     A keyboard accelerator triggers an action when a certain key
       
    67     combination is pressed. The accelerator handles all keyboard
       
    68     activity for all the children of one top-level widget, so it is
       
    69     not affected by the keyboard focus.
       
    70 
       
    71     In most cases, you will not need to use this class directly. Use
       
    72     the QAction class to create actions with accelerators that can be
       
    73     used in both menus and toolbars. If you're only interested in
       
    74     menus use Q3MenuData::insertItem() or Q3MenuData::setAccel() to make
       
    75     accelerators for operations that are also available on menus. Many
       
    76     widgets automatically generate accelerators, such as QAbstractButton,
       
    77     QGroupBox, QLabel (with QLabel::setBuddy()), QMenuBar, and QTabBar.
       
    78     Example:
       
    79     \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 0
       
    80 
       
    81     A Q3Accel contains a list of accelerator items that can be
       
    82     manipulated using insertItem(), removeItem(), clear(), key() and
       
    83     findKey().
       
    84 
       
    85     Each accelerator item consists of an identifier and a \l
       
    86     QKeySequence. A single key sequence consists of a keyboard code
       
    87     combined with modifiers (Qt::SHIFT, Qt::CTRL, Qt::ALT, or
       
    88     Qt::UNICODE_ACCEL). For example, Qt::CTRL + Qt::Key_P could be a shortcut
       
    89     for printing a document. As an alternative, use Qt::UNICODE_ACCEL with the
       
    90     unicode code point of the character. For example, Qt::UNICODE_ACCEL
       
    91     + 'A' gives the same accelerator as Qt::Key_A.
       
    92 
       
    93     When an accelerator key is pressed, the accelerator sends out the
       
    94     signal activated() with a number that identifies this particular
       
    95     accelerator item. Accelerator items can also be individually
       
    96     connected, so that two different keys will activate two different
       
    97     slots (see connectItem() and disconnectItem()).
       
    98 
       
    99     The activated() signal is \e not emitted when two or more
       
   100     accelerators match the same key.  Instead, the first matching
       
   101     accelerator sends out the activatedAmbiguously() signal. By
       
   102     pressing the key multiple times, users can navigate between all
       
   103     matching accelerators. Some standard controls like QPushButton and
       
   104     QCheckBox connect the activatedAmbiguously() signal to the
       
   105     harmless setFocus() slot, whereas activated() is connected to a
       
   106     slot invoking the button's action. Most controls, like QLabel and
       
   107     QTabBar, treat activated() and activatedAmbiguously() as
       
   108     equivalent.
       
   109 
       
   110     Use setEnabled() to enable or disable all the items in an
       
   111     accelerator, or setItemEnabled() to enable or disable individual
       
   112     items. An item is active only when both the Q3Accel and the item
       
   113     itself are enabled.
       
   114 
       
   115     The function setWhatsThis() specifies a help text that appears
       
   116     when the user presses an accelerator key in What's This mode.
       
   117 
       
   118     The accelerator will be deleted when \e parent is deleted,
       
   119     and will consume relevant key events until then.
       
   120 
       
   121     Please note that the accelerator
       
   122     \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 1
       
   123     can be triggered with both the 'M' key, and with Shift+M,
       
   124     unless a second accelerator is defined for the Shift+M
       
   125     combination.
       
   126 
       
   127 
       
   128     Example:
       
   129     \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 2
       
   130 
       
   131     \sa QKeyEvent QWidget::keyPressEvent()
       
   132     QAbstractButton::setAccel() QLabel::setBuddy() QKeySequence
       
   133 */
       
   134 
       
   135 
       
   136 struct Q3AccelItem {                                // internal accelerator item
       
   137     Q3AccelItem(const QKeySequence &k, int i)
       
   138         { key=k; id=i; enabled=true; signal=0; }
       
   139    ~Q3AccelItem()               { delete signal; }
       
   140     int id;
       
   141     QKeySequence key;
       
   142     bool enabled;
       
   143     Q3Signal *signal;
       
   144     QString whatsthis;
       
   145 };
       
   146 
       
   147 
       
   148 typedef Q3PtrList<Q3AccelItem> Q3AccelList; // internal accelerator list
       
   149 
       
   150 class Q3AccelPrivate {
       
   151 public:
       
   152     Q3AccelPrivate(Q3Accel* p);
       
   153     ~Q3AccelPrivate();
       
   154     Q3AccelList aitems;
       
   155     bool enabled;
       
   156     QPointer<QWidget> watch;
       
   157     bool ignorewhatsthis;
       
   158     Q3Accel* parent;
       
   159 
       
   160     void activate(Q3AccelItem* item);
       
   161     void activateAmbiguously(Q3AccelItem* item);
       
   162 };
       
   163 
       
   164 class Q3AccelManager {
       
   165 public:
       
   166     static Q3AccelManager* self() { return self_ptr ? self_ptr : new Q3AccelManager; }
       
   167     void registerAccel(Q3AccelPrivate* a) { accels.append(a); }
       
   168     void unregisterAccel(Q3AccelPrivate* a) { accels.removeRef(a); if (accels.isEmpty()) delete this; }
       
   169     bool tryAccelEvent(QWidget* w, QKeyEvent* e);
       
   170     bool dispatchAccelEvent(QWidget* w, QKeyEvent* e);
       
   171     bool tryComposeUnicode(QWidget* w, QKeyEvent* e);
       
   172 
       
   173 private:
       
   174     Q3AccelManager()
       
   175         : currentState(QKeySequence::NoMatch), clash(-1), metaComposeUnicode(false),composedUnicode(0)
       
   176         { setFuncPtr(); self_ptr = this; }
       
   177     ~Q3AccelManager() { self_ptr = 0; }
       
   178     void setFuncPtr();
       
   179 
       
   180     bool correctSubWindow(QWidget *w, Q3AccelPrivate* d);
       
   181     QKeySequence::SequenceMatch match(QKeyEvent* e, Q3AccelItem* item, QKeySequence& temp);
       
   182     int translateModifiers(ButtonState state);
       
   183 
       
   184     Q3PtrList<Q3AccelPrivate> accels;
       
   185     static Q3AccelManager* self_ptr;
       
   186     QKeySequence::SequenceMatch currentState;
       
   187     QKeySequence intermediate;
       
   188     int clash;
       
   189     bool metaComposeUnicode;
       
   190     int composedUnicode;
       
   191 };
       
   192 Q3AccelManager* Q3AccelManager::self_ptr = 0;
       
   193 
       
   194 bool Q_COMPAT_EXPORT qt_tryAccelEvent(QWidget* w, QKeyEvent*  e){
       
   195     return Q3AccelManager::self()->tryAccelEvent(w, e);
       
   196 }
       
   197 
       
   198 bool Q_COMPAT_EXPORT qt_dispatchAccelEvent(QWidget* w, QKeyEvent*  e){
       
   199     return Q3AccelManager::self()->dispatchAccelEvent(w, e);
       
   200 }
       
   201 
       
   202 bool Q_COMPAT_EXPORT qt_tryComposeUnicode(QWidget* w, QKeyEvent*  e){
       
   203     return Q3AccelManager::self()->tryComposeUnicode(w, e);
       
   204 }
       
   205 
       
   206 void Q3AccelManager::setFuncPtr() {
       
   207     if (qApp->d_func()->qt_compat_used)
       
   208         return;
       
   209     QApplicationPrivate *data = static_cast<QApplicationPrivate*>(qApp->d_ptr.data());
       
   210     data->qt_tryAccelEvent = qt_tryAccelEvent;
       
   211     data->qt_tryComposeUnicode = qt_tryComposeUnicode;
       
   212     data->qt_dispatchAccelEvent = qt_dispatchAccelEvent;
       
   213     data->qt_compat_used = true;
       
   214 }
       
   215 
       
   216 #ifdef Q_WS_MAC
       
   217 static bool qt_accel_no_shortcuts = true;
       
   218 #else
       
   219 static bool qt_accel_no_shortcuts = false;
       
   220 #endif
       
   221 void Q_COMPAT_EXPORT qt_set_accel_auto_shortcuts(bool b) { qt_accel_no_shortcuts = b; }
       
   222 
       
   223 /*
       
   224     \internal
       
   225     Returns true if the accel is in the current subwindow, else false.
       
   226 */
       
   227 bool Q3AccelManager::correctSubWindow(QWidget* w, Q3AccelPrivate* d) {
       
   228 #if !defined (Q_OS_MACX)
       
   229      if (!d->watch || !d->watch->isVisible() || !d->watch->isEnabled())
       
   230 #else
       
   231     if (!d->watch || (!d->watch->isVisible() && !d->watch->inherits("QMenuBar")) || !d->watch->isEnabled())
       
   232 #endif
       
   233         return false;
       
   234     QWidget* tlw = w->window();
       
   235     QWidget* wtlw = d->watch->window();
       
   236 
       
   237     /* if we live in a floating dock window, keep our parent's
       
   238      * accelerators working */
       
   239 #ifndef QT_NO_MAINWINDOW
       
   240     if ((tlw->windowType() == Qt::Dialog) && tlw->parentWidget() && qobject_cast<QDockWidget*>(tlw))
       
   241         return tlw->parentWidget()->window() == wtlw;
       
   242 
       
   243     if (wtlw  != tlw)
       
   244         return false;
       
   245 #endif
       
   246     /* if we live in a MDI subwindow, ignore the event if we are
       
   247        not the active document window */
       
   248     QWidget* sw = d->watch;
       
   249     while (sw && sw->windowType() != Qt::SubWindow)
       
   250         sw = sw->parentWidget(true);
       
   251     if (sw)  { // we are in a subwindow indeed
       
   252         QWidget* fw = w;
       
   253         while (fw && fw != sw)
       
   254             fw = fw->parentWidget(true);
       
   255         if (fw != sw) // focus widget not in our subwindow
       
   256             return false;
       
   257     }
       
   258     return true;
       
   259 }
       
   260 
       
   261 inline int Q3AccelManager::translateModifiers(ButtonState state)
       
   262 {
       
   263     int result = 0;
       
   264     if (state & ShiftButton)
       
   265         result |= SHIFT;
       
   266     if (state & ControlButton)
       
   267         result |= CTRL;
       
   268     if (state & MetaButton)
       
   269         result |= META;
       
   270     if (state & AltButton)
       
   271         result |= ALT;
       
   272     return result;
       
   273 }
       
   274 
       
   275 /*
       
   276     \internal
       
   277     Matches the current intermediate key sequence + the latest
       
   278     keyevent, with and AccelItem. Returns Identical,
       
   279     PartialMatch or NoMatch, and fills \a temp with the
       
   280     resulting key sequence.
       
   281 */
       
   282 QKeySequence::SequenceMatch Q3AccelManager::match(QKeyEvent *e, Q3AccelItem* item, QKeySequence& temp)
       
   283 {
       
   284     QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
       
   285     int index = intermediate.count();
       
   286     temp = intermediate;
       
   287 
       
   288     int modifier = translateModifiers(e->state());
       
   289 
       
   290     if (e->key() && e->key() != Key_unknown) {
       
   291         int key = e->key()  | modifier;
       
   292         if (e->key() == Key_BackTab) {
       
   293             /*
       
   294             In QApplication, we map shift+tab to shift+backtab.
       
   295             This code here reverts the mapping in a way that keeps
       
   296             backtab and shift+tab accelerators working, in that
       
   297             order, meaning backtab has priority.*/
       
   298             key &= ~SHIFT;
       
   299 
       
   300             temp.setKey(key, index);
       
   301             if (QKeySequence::NoMatch != (result = temp.matches(item->key)))
       
   302                 return result;
       
   303             if (e->state() & ShiftButton)
       
   304                 key |= SHIFT;
       
   305             key = Key_Tab | (key & MODIFIER_MASK);
       
   306             temp.setKey(key, index);
       
   307             if (QKeySequence::NoMatch != (result = temp.matches(item->key)))
       
   308                 return result;
       
   309         } else {
       
   310             temp.setKey(key, index);
       
   311             if (QKeySequence::NoMatch != (result = temp.matches(item->key)))
       
   312                 return result;
       
   313         }
       
   314 
       
   315         if (key == Key_BackTab) {
       
   316             if (e->state() & ShiftButton)
       
   317                 key |= SHIFT;
       
   318             temp.setKey(key, index);
       
   319             if (QKeySequence::NoMatch != (result = temp.matches(item->key)))
       
   320                 return result;
       
   321         }
       
   322     }
       
   323     if (!e->text().isEmpty()) {
       
   324         temp.setKey((int)e->text()[0].unicode() | UNICODE_ACCEL | modifier, index);
       
   325         result = temp.matches(item->key);
       
   326     }
       
   327     return result;
       
   328 }
       
   329 
       
   330 bool Q3AccelManager::tryAccelEvent(QWidget* w, QKeyEvent* e)
       
   331 {
       
   332     if (QKeySequence::NoMatch == currentState) {
       
   333         e->t = QEvent::AccelOverride;
       
   334         e->ignore();
       
   335         QApplication::sendSpontaneousEvent(w, e);
       
   336         if (e->isAccepted())
       
   337             return false;
       
   338     }
       
   339     e->t = QEvent::Accel;
       
   340     e->ignore();
       
   341     QApplication::sendSpontaneousEvent(w, e);
       
   342     return e->isAccepted();
       
   343 }
       
   344 
       
   345 bool Q3AccelManager::tryComposeUnicode(QWidget* w, QKeyEvent* e)
       
   346 {
       
   347     if (metaComposeUnicode) {
       
   348         int value = e->key() - Key_0;
       
   349         // Ignore acceloverrides so we don't trigger
       
   350         // accels on keypad when Meta compose is on
       
   351         if ((e->type() == QEvent::AccelOverride) &&
       
   352              (e->state() == Qt::Keypad + Qt::MetaButton)) {
       
   353             e->accept();
       
   354         // Meta compose start/continue
       
   355         } else if ((e->type() == QEvent::KeyPress) &&
       
   356              (e->state() == Qt::Keypad + Qt::MetaButton)) {
       
   357             if (value >= 0 && value <= 9) {
       
   358                 composedUnicode *= 10;
       
   359                 composedUnicode += value;
       
   360                 return true;
       
   361             } else {
       
   362                 // Composing interrupted, dispatch!
       
   363                 if (composedUnicode) {
       
   364                     QChar ch(composedUnicode);
       
   365                     QString s(ch);
       
   366                     QKeyEvent kep(QEvent::KeyPress, 0, ch.row() ? 0 : ch.cell(), 0, s);
       
   367                     QKeyEvent ker(QEvent::KeyRelease, 0, ch.row() ? 0 : ch.cell(), 0, s);
       
   368                     QApplication::sendEvent(w, &kep);
       
   369                     QApplication::sendEvent(w, &ker);
       
   370                 }
       
   371                 composedUnicode = 0;
       
   372                 return true;
       
   373             }
       
   374         // Meta compose end, dispatch
       
   375         } else if ((e->type() == QEvent::KeyRelease) &&
       
   376                     (e->key() == Key_Meta) &&
       
   377                     (composedUnicode != 0)) {
       
   378             if ((composedUnicode > 0) &&
       
   379                  (composedUnicode < 0xFFFE)) {
       
   380                 QChar ch(composedUnicode);
       
   381                 QString s(ch);
       
   382                 QKeyEvent kep(QEvent::KeyPress, 0, ch.row() ? 0 : ch.cell(), 0, s);
       
   383                 QKeyEvent ker(QEvent::KeyRelease, 0, ch.row() ? 0 : ch.cell(), 0, s);
       
   384                 QApplication::sendEvent(w, &kep);
       
   385                 QApplication::sendEvent(w, &ker);
       
   386             }
       
   387             composedUnicode = 0;
       
   388             return true;
       
   389         }
       
   390     }
       
   391     return false;
       
   392 }
       
   393 
       
   394 /*
       
   395     \internal
       
   396     Checks for possible accelerators, if no widget
       
   397     ate the keypres, or we are in the middle of a
       
   398     partial key sequence.
       
   399 */
       
   400 bool Q3AccelManager::dispatchAccelEvent(QWidget* w, QKeyEvent* e)
       
   401 {
       
   402 #ifndef QT_NO_STATUSBAR
       
   403     // Needs to be declared and used here because of "goto doclash"
       
   404     QStatusBar* mainStatusBar = 0;
       
   405 #endif
       
   406 
       
   407     // Modifiers can NOT be accelerators...
       
   408     if (e->key() >= Key_Shift &&
       
   409          e->key() <= Key_Alt)
       
   410          return false;
       
   411 
       
   412     QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
       
   413     QKeySequence tocheck, partial;
       
   414     Q3AccelPrivate* accel = 0;
       
   415     Q3AccelItem* item = 0;
       
   416     Q3AccelPrivate* firstaccel = 0;
       
   417     Q3AccelItem* firstitem = 0;
       
   418     Q3AccelPrivate* lastaccel = 0;
       
   419     Q3AccelItem* lastitem = 0;
       
   420 
       
   421     QKeyEvent pe = *e;
       
   422     int n = -1;
       
   423     int hasShift = (e->state()&Qt::ShiftButton)?1:0;
       
   424     bool identicalDisabled = false;
       
   425     bool matchFound = false;
       
   426     do {
       
   427         accel = accels.first();
       
   428         matchFound = false;
       
   429         while (accel) {
       
   430             if (correctSubWindow(w, accel)) {
       
   431                 if (accel->enabled) {
       
   432                     item = accel->aitems.last();
       
   433                     while(item) {
       
   434                         if (QKeySequence::Identical == (result = match(&pe, item, tocheck))) {
       
   435                             if (item->enabled) {
       
   436                                 if (!firstaccel) {
       
   437                                     firstaccel = accel;
       
   438                                     firstitem = item;
       
   439                                 }
       
   440                                 lastaccel = accel;
       
   441                                 lastitem = item;
       
   442                                 n++;
       
   443                                 matchFound = true;
       
   444                                 if (n > QMAX(clash,0))
       
   445                                     goto doclash;
       
   446                             } else {
       
   447                                 identicalDisabled = true;
       
   448                             }
       
   449                         }
       
   450                         if (item->enabled && QKeySequence::PartialMatch == result) {
       
   451                             partial = tocheck;
       
   452                             matchFound = true;
       
   453                         }
       
   454                         item = accel->aitems.prev();
       
   455                     }
       
   456                 } else {
       
   457                     item = accel->aitems.last();
       
   458                     while(item) {
       
   459                         if (QKeySequence::Identical == match(&pe, item, tocheck))
       
   460                             identicalDisabled = true;
       
   461                         item = accel->aitems.prev();
       
   462                     }
       
   463                 }
       
   464             }
       
   465             accel = accels.next();
       
   466         }
       
   467         pe = QKeyEvent(QEvent::Accel, pe.key(), pe.ascii(), pe.state()&~Qt::ShiftButton, pe.text());
       
   468     } while (hasShift-- && !matchFound && !identicalDisabled);
       
   469 
       
   470 #ifndef QT_NO_STATUSBAR
       
   471     mainStatusBar = (QStatusBar*) w->window()->child(0, "QStatusBar");
       
   472 #endif
       
   473     if (n < 0) { // no match found
       
   474         currentState = partial.count() ? QKeySequence::PartialMatch : QKeySequence::NoMatch;
       
   475 #ifndef QT_NO_STATUSBAR
       
   476         // Only display message if we are, or were, in a partial match
       
   477         if (mainStatusBar && (QKeySequence::PartialMatch == currentState || intermediate.count())) {
       
   478             if (currentState == QKeySequence::PartialMatch) {
       
   479                 mainStatusBar->showMessage((QString)partial + QLatin1String(", ..."));
       
   480             } else if (!identicalDisabled) {
       
   481                 QString message = Q3Accel::tr("%1, %2 not defined").
       
   482                     arg((QString)intermediate).
       
   483                     arg(QKeySequence::encodeString(e->key() | translateModifiers(e->state())));
       
   484                 mainStatusBar->showMessage(message, 2000);
       
   485                 // Since we're a NoMatch, reset the clash count
       
   486                 clash = -1;
       
   487             } else {
       
   488             	mainStatusBar->clearMessage();
       
   489             }
       
   490         }
       
   491 #endif
       
   492 
       
   493         bool eatKey = (QKeySequence::PartialMatch == currentState || intermediate.count());
       
   494         intermediate = partial;
       
   495         if (eatKey)
       
   496             e->accept();
       
   497         return eatKey;
       
   498     } else if (n == 0) { // found exactly one match
       
   499         clash = -1; // reset
       
   500 #ifndef QT_NO_STATUSBAR
       
   501         if (currentState == QKeySequence::PartialMatch && mainStatusBar)
       
   502                 mainStatusBar->clearMessage();
       
   503 #endif
       
   504         currentState = QKeySequence::NoMatch; // Free sequence keylock
       
   505         intermediate = QKeySequence();
       
   506         lastaccel->activate(lastitem);
       
   507         e->accept();
       
   508         return true;
       
   509     }
       
   510 
       
   511  doclash: // found more than one match
       
   512 #ifndef QT_NO_STATUSBAR
       
   513     if (!mainStatusBar) // if "goto doclash", we need to get status bar again.
       
   514         mainStatusBar = (QStatusBar*) w->window()->child(0, "QStatusBar");
       
   515 #endif
       
   516 
       
   517     QString message = Q3Accel::tr("Ambiguous %1 not handled").arg((QString)tocheck);
       
   518     if (clash >= 0 && n > clash) { // pick next  match
       
   519         intermediate = QKeySequence();
       
   520         currentState = QKeySequence::NoMatch; // Free sequence keylock
       
   521         clash++;
       
   522 #ifndef QT_NO_STATUSBAR
       
   523         if (mainStatusBar &&
       
   524              !lastitem->signal &&
       
   525              !(lastaccel->parent->receivers(SIGNAL(activatedAmbiguously(int)))))
       
   526             mainStatusBar->showMessage(message, 2000);
       
   527 #endif
       
   528         lastaccel->activateAmbiguously(lastitem);
       
   529     } else { // start (or wrap) with the first matching
       
   530         intermediate = QKeySequence();
       
   531         currentState = QKeySequence::NoMatch; // Free sequence keylock
       
   532         clash = 0;
       
   533 #ifndef QT_NO_STATUSBAR
       
   534         if (mainStatusBar &&
       
   535              !firstitem->signal &&
       
   536              !(firstaccel->parent->receivers(SIGNAL(activatedAmbiguously(int)))))
       
   537             mainStatusBar->showMessage(message, 2000);
       
   538 #endif
       
   539         firstaccel->activateAmbiguously(firstitem);
       
   540     }
       
   541     e->accept();
       
   542     return true;
       
   543 }
       
   544 
       
   545 Q3AccelPrivate::Q3AccelPrivate(Q3Accel* p)
       
   546     : parent(p)
       
   547 {
       
   548     Q3AccelManager::self()->registerAccel(this);
       
   549     aitems.setAutoDelete(true);
       
   550     ignorewhatsthis = false;
       
   551 }
       
   552 
       
   553 Q3AccelPrivate::~Q3AccelPrivate()
       
   554 {
       
   555     Q3AccelManager::self()->unregisterAccel(this);
       
   556 }
       
   557 
       
   558 static Q3AccelItem *find_id(Q3AccelList &list, int id)
       
   559 {
       
   560     register Q3AccelItem *item = list.first();
       
   561     while (item && item->id != id)
       
   562         item = list.next();
       
   563     return item;
       
   564 }
       
   565 
       
   566 static Q3AccelItem *find_key(Q3AccelList &list, const QKeySequence &key)
       
   567 {
       
   568     register Q3AccelItem *item = list.first();
       
   569     while (item && !(item->key == key))
       
   570         item = list.next();
       
   571     return item;
       
   572 }
       
   573 
       
   574 /*!
       
   575     Constructs a Q3Accel object called \a name, with parent \a parent.
       
   576     The accelerator operates on \a parent.
       
   577 */
       
   578 
       
   579 Q3Accel::Q3Accel(QWidget *parent, const char *name)
       
   580     : QObject(parent, name)
       
   581 {
       
   582     d = new Q3AccelPrivate(this);
       
   583     d->enabled = true;
       
   584     d->watch = parent;
       
   585 #if defined(QT_CHECK_NULL)
       
   586     if (!d->watch)
       
   587         qWarning("Q3Accel: An accelerator must have a parent or a watch widget");
       
   588 #endif
       
   589 }
       
   590 
       
   591 /*!
       
   592     Constructs a Q3Accel object called \a name, that operates on \a
       
   593     watch, and is a child of \a parent.
       
   594 
       
   595     This constructor is not needed for normal application programming.
       
   596 */
       
   597 Q3Accel::Q3Accel(QWidget* watch, QObject *parent, const char *name)
       
   598     : QObject(parent, name)
       
   599 {
       
   600     d = new Q3AccelPrivate(this);
       
   601     d->enabled = true;
       
   602     d->watch = watch;
       
   603 #if defined(QT_CHECK_NULL)
       
   604     if (!d->watch)
       
   605         qWarning("Q3Accel: An accelerator must have a parent or a watch widget");
       
   606 #endif
       
   607 }
       
   608 
       
   609 /*!
       
   610     Destroys the accelerator object and frees all allocated resources.
       
   611 */
       
   612 
       
   613 Q3Accel::~Q3Accel()
       
   614 {
       
   615     delete d;
       
   616 }
       
   617 
       
   618 
       
   619 /*!
       
   620     \fn void Q3Accel::activated(int id)
       
   621 
       
   622     This signal is emitted when the user types the shortcut's key
       
   623     sequence. \a id is a number that identifies this particular
       
   624     accelerator item.
       
   625 
       
   626     \sa activatedAmbiguously()
       
   627 */
       
   628 
       
   629 /*!
       
   630     \fn void Q3Accel::activatedAmbiguously(int id)
       
   631 
       
   632     This signal is emitted when the user types a shortcut key
       
   633     sequence that is ambiguous. For example, if one key sequence is a
       
   634     "prefix" for another and the user types these keys it isn't clear
       
   635     if they want the shorter key sequence, or if they're about to
       
   636     type more to complete the longer key sequence. \a id is a number
       
   637     that identifies this particular accelerator item.
       
   638 
       
   639     \sa activated()
       
   640 */
       
   641 
       
   642 /*!
       
   643     Returns true if the accelerator is enabled; otherwise returns
       
   644     false.
       
   645 
       
   646     \sa setEnabled(), isItemEnabled()
       
   647 */
       
   648 
       
   649 bool Q3Accel::isEnabled() const
       
   650 {
       
   651     return d->enabled;
       
   652 }
       
   653 
       
   654 
       
   655 /*!
       
   656     Enables the accelerator if \a enable is true, or disables it if \a
       
   657     enable is false.
       
   658 
       
   659     Individual keys can also be enabled or disabled using
       
   660     setItemEnabled(). To work, a key must be an enabled item in an
       
   661     enabled Q3Accel.
       
   662 
       
   663     \sa isEnabled(), setItemEnabled()
       
   664 */
       
   665 
       
   666 void Q3Accel::setEnabled(bool enable)
       
   667 {
       
   668     d->enabled = enable;
       
   669 }
       
   670 
       
   671 
       
   672 /*!
       
   673     Returns the number of accelerator items in this accelerator.
       
   674 */
       
   675 
       
   676 uint Q3Accel::count() const
       
   677 {
       
   678     return d->aitems.count();
       
   679 }
       
   680 
       
   681 
       
   682 static int get_seq_id()
       
   683 {
       
   684     static int seq_no = -2;  // -1 is used as return value in findKey()
       
   685     return seq_no--;
       
   686 }
       
   687 
       
   688 /*!
       
   689     Inserts an accelerator item and returns the item's identifier.
       
   690 
       
   691     \a key is a key code and an optional combination of SHIFT, CTRL
       
   692     and ALT. \a id is the accelerator item id.
       
   693 
       
   694     If \a id is negative, then the item will be assigned a unique
       
   695     negative identifier less than -1.
       
   696 
       
   697     \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 3
       
   698 */
       
   699 
       
   700 int Q3Accel::insertItem(const QKeySequence& key, int id)
       
   701 {
       
   702     if (id == -1)
       
   703         id = get_seq_id();
       
   704     d->aitems.insert(0, new Q3AccelItem(key,id));
       
   705     return id;
       
   706 }
       
   707 
       
   708 /*!
       
   709     Removes the accelerator item with the identifier \a id.
       
   710 */
       
   711 
       
   712 void Q3Accel::removeItem(int id)
       
   713 {
       
   714     if (find_id(d->aitems, id))
       
   715         d->aitems.remove();
       
   716 }
       
   717 
       
   718 
       
   719 /*!
       
   720     Removes all accelerator items.
       
   721 */
       
   722 
       
   723 void Q3Accel::clear()
       
   724 {
       
   725     d->aitems.clear();
       
   726 }
       
   727 
       
   728 
       
   729 /*!
       
   730     Returns the key sequence of the accelerator item with identifier
       
   731     \a id, or an invalid key sequence (0) if the id cannot be found.
       
   732 */
       
   733 
       
   734 QKeySequence Q3Accel::key(int id)
       
   735 {
       
   736     Q3AccelItem *item = find_id(d->aitems, id);
       
   737     return item ? item->key : QKeySequence(0);
       
   738 }
       
   739 
       
   740 
       
   741 /*!
       
   742     Returns the identifier of the accelerator item with the key code
       
   743     \a key, or -1 if the item cannot be found.
       
   744 */
       
   745 
       
   746 int Q3Accel::findKey(const QKeySequence& key) const
       
   747 {
       
   748     Q3AccelItem *item = find_key(d->aitems, key);
       
   749     return item ? item->id : -1;
       
   750 }
       
   751 
       
   752 
       
   753 /*!
       
   754     Returns true if the accelerator item with the identifier \a id is
       
   755     enabled. Returns false if the item is disabled or cannot be found.
       
   756 
       
   757     \sa setItemEnabled(), isEnabled()
       
   758 */
       
   759 
       
   760 bool Q3Accel::isItemEnabled(int id) const
       
   761 {
       
   762     Q3AccelItem *item = find_id(d->aitems, id);
       
   763     return item ? item->enabled : false;
       
   764 }
       
   765 
       
   766 
       
   767 /*!
       
   768     Enables the accelerator item with the identifier \a id if \a
       
   769     enable is true, and disables item \a id if \a enable is false.
       
   770 
       
   771     To work, an item must be enabled and be in an enabled Q3Accel.
       
   772 
       
   773     \sa isItemEnabled(), isEnabled()
       
   774 */
       
   775 
       
   776 void Q3Accel::setItemEnabled(int id, bool enable)
       
   777 {
       
   778     Q3AccelItem *item = find_id(d->aitems, id);
       
   779     if (item)
       
   780         item->enabled = enable;
       
   781 }
       
   782 
       
   783 
       
   784 /*!
       
   785     Connects the accelerator item \a id to the slot \a member of \a
       
   786     receiver. Returns true if the connection is successful.
       
   787 
       
   788     \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 4
       
   789 
       
   790     Of course, you can also send a signal as \a member.
       
   791 
       
   792     Normally accelerators are connected to slots which then receive
       
   793     the \c activated(int id) signal with the id of the accelerator
       
   794     item that was activated. If you choose to connect a specific
       
   795     accelerator item using this function, the \c activated() signal is
       
   796     emitted if the associated key sequence is pressed but no \c
       
   797     activated(int id) signal is emitted.
       
   798 
       
   799     \sa disconnectItem(), QObject::connect()
       
   800 */
       
   801 
       
   802 bool Q3Accel::connectItem(int id, const QObject *receiver, const char *member)
       
   803 {
       
   804     Q3AccelItem *item = find_id(d->aitems, id);
       
   805     if (item) {
       
   806         if (!item->signal) {
       
   807             item->signal = new Q3Signal;
       
   808             Q_CHECK_PTR(item->signal);
       
   809         }
       
   810         return item->signal->connect(receiver, member);
       
   811     }
       
   812     return false;
       
   813 }
       
   814 
       
   815 /*!
       
   816     Disconnects the accelerator item identified by \a id from
       
   817     the function called \a member in the \a receiver object.
       
   818     Returns true if the connection existed and the disconnect
       
   819     was successful.
       
   820 
       
   821     \sa connectItem(), QObject::disconnect()
       
   822 */
       
   823 
       
   824 bool Q3Accel::disconnectItem(int id, const QObject *receiver,
       
   825                              const char *member)
       
   826 {
       
   827     Q3AccelItem *item = find_id(d->aitems, id);
       
   828     if (item && item->signal)
       
   829         return item->signal->disconnect(receiver, member);
       
   830     return false;
       
   831 }
       
   832 
       
   833 void Q3AccelPrivate::activate(Q3AccelItem* item)
       
   834 {
       
   835 #ifndef QT_NO_WHATSTHIS
       
   836     if (QWhatsThis::inWhatsThisMode() && !ignorewhatsthis) {
       
   837         QWhatsThis::showText(QCursor::pos(), item->whatsthis);
       
   838         return;
       
   839     }
       
   840 #endif
       
   841     if (item->signal)
       
   842         item->signal->activate();
       
   843     else
       
   844         emit parent->activated(item->id);
       
   845 }
       
   846 
       
   847 void Q3AccelPrivate::activateAmbiguously(Q3AccelItem* item)
       
   848 {
       
   849     if (item->signal)
       
   850         item->signal->activate();
       
   851     else
       
   852         emit parent->activatedAmbiguously(item->id);
       
   853 }
       
   854 
       
   855 
       
   856 /*!
       
   857     Returns the shortcut key sequence for \a str, or an invalid key
       
   858     sequence (0) if \a str has no shortcut sequence.
       
   859 
       
   860     For example, shortcutKey("E&xit") returns QKeySequence(Qt::ALT +
       
   861     Qt::Key_X), shortcutKey("&Quit") returns QKeySequence(Qt::ALT +
       
   862     Qt::Key_Q), and shortcutKey("Quit") returns QKeySequence().
       
   863 */
       
   864 
       
   865 QKeySequence Q3Accel::shortcutKey(const QString &str)
       
   866 {
       
   867     if(qt_accel_no_shortcuts)
       
   868         return QKeySequence();
       
   869 
       
   870     int p = 0;
       
   871     while (p >= 0) {
       
   872         p = str.find(QLatin1Char('&'), p) + 1;
       
   873         if (p <= 0 || p >= (int)str.length())
       
   874             return 0;
       
   875         if (str[p] != QLatin1Char('&')) {
       
   876             QChar c = str[p];
       
   877             if (c.isPrint()) {
       
   878                 char ltr = c.upper().latin1();
       
   879                 if (ltr >= (char)Key_A && ltr <= (char)Key_Z)
       
   880                     c = QLatin1Char(ltr);
       
   881                 else
       
   882                     c = c.lower();
       
   883                 return QKeySequence(c.unicode() + ALT + UNICODE_ACCEL);
       
   884             }
       
   885         }
       
   886         p++;
       
   887     }
       
   888     return QKeySequence();
       
   889 }
       
   890 
       
   891 /*! \obsolete
       
   892 
       
   893    Creates an accelerator string for the key \a k.
       
   894    For instance CTRL+Key_O gives "Ctrl+O". The "Ctrl" etc.
       
   895    are translated (using QObject::tr()) in the "Q3Accel" context.
       
   896 
       
   897    The function is superfluous. Cast the QKeySequence \a k to a
       
   898    QString for the same effect.
       
   899 */
       
   900 QString Q3Accel::keyToString(QKeySequence k)
       
   901 {
       
   902     return (QString) k;
       
   903 }
       
   904 
       
   905 /*!\obsolete
       
   906 
       
   907   Returns an accelerator code for the string \a s. For example
       
   908   "Ctrl+O" gives CTRL+UNICODE_ACCEL+'O'. The strings "Ctrl",
       
   909   "Shift", "Alt" are recognized, as well as their translated
       
   910   equivalents in the "Q3Accel" context (using QObject::tr()). Returns 0
       
   911   if \a s is not recognized.
       
   912 
       
   913   This function is typically used with \link QObject::tr() tr
       
   914   \endlink(), so that accelerator keys can be replaced in
       
   915   translations:
       
   916 
       
   917   \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 5
       
   918 
       
   919   Notice the "File|Open" translator comment. It is by no means
       
   920   necessary, but it provides some context for the human translator.
       
   921 
       
   922   The function is superfluous. Construct a QKeySequence from the
       
   923   string \a s for the same effect.
       
   924 
       
   925   \sa QObject::tr(), {Internationalization with Qt}
       
   926 */
       
   927 QKeySequence Q3Accel::stringToKey(const QString & s)
       
   928 {
       
   929     return QKeySequence(s);
       
   930 }
       
   931 
       
   932 
       
   933 /*!
       
   934     Sets a What's This help text for the accelerator item \a id to \a
       
   935     text.
       
   936 
       
   937     The text will be shown when the application is in What's This mode
       
   938     and the user hits the accelerator key.
       
   939 
       
   940     To set What's This help on a menu item (with or without an
       
   941     accelerator key), use Q3MenuData::setWhatsThis().
       
   942 
       
   943     \sa whatsThis(), QWhatsThis::inWhatsThisMode(), QAction::setWhatsThis()
       
   944 */
       
   945 void Q3Accel::setWhatsThis(int id, const QString& text)
       
   946 {
       
   947     Q3AccelItem *item = find_id(d->aitems, id);
       
   948     if (item)
       
   949         item->whatsthis = text;
       
   950 }
       
   951 
       
   952 /*!
       
   953     Returns the What's This help text for the specified item \a id or
       
   954     an empty string if no text has been specified.
       
   955 
       
   956     \sa setWhatsThis()
       
   957 */
       
   958 QString Q3Accel::whatsThis(int id) const
       
   959 {
       
   960 
       
   961     Q3AccelItem *item = find_id(d->aitems, id);
       
   962     return item? item->whatsthis : QString();
       
   963 }
       
   964 
       
   965 /*!\internal */
       
   966 void Q3Accel::setIgnoreWhatsThis(bool b)
       
   967 {
       
   968     d->ignorewhatsthis = b;
       
   969 }
       
   970 
       
   971 /*!\internal */
       
   972 bool Q3Accel::ignoreWhatsThis() const
       
   973 {
       
   974     return d->ignorewhatsthis;
       
   975 }
       
   976 
       
   977 /*!
       
   978     \fn void Q3Accel::repairEventFilter()
       
   979     \internal
       
   980 */
       
   981 
       
   982 QT_END_NAMESPACE