• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

kkeysequencewidget.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002     Copyright (C) 1998 Mark Donohoe <donohoe@kde.org>
00003     Copyright (C) 2001 Ellis Whitehead <ellis@kde.org>
00004     Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019     Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "kkeysequencewidget.h"
00023 #include "kkeysequencewidget_p.h"
00024 
00025 #include "kkeyserver.h"
00026 
00027 #include <QKeyEvent>
00028 #include <QTimer>
00029 #include <QtCore/QHash>
00030 #include <QHBoxLayout>
00031 #include <QToolButton>
00032 #include <QApplication>
00033 
00034 #include <kglobalaccel.h>
00035 #include <kicon.h>
00036 #include <klocale.h>
00037 #include <kmessagebox.h>
00038 #include <kshortcut.h>
00039 #include <kaction.h>
00040 #include <kactioncollection.h>
00041 
00042 #include "kdebug.h"
00043 
00044 class KKeySequenceWidgetPrivate
00045 {
00046 public:
00047     KKeySequenceWidgetPrivate(KKeySequenceWidget *q);
00048 
00049     void init();
00050 
00051     static QKeySequence appendToSequence(const QKeySequence& seq, int keyQt);
00052     static bool isOkWhenModifierless(int keyQt);
00053 
00054     void updateShortcutDisplay();
00055     void startRecording();
00056 
00061     bool conflictWithStandardShortcuts(const QKeySequence &seq);
00062 
00067     bool conflictWithLocalShortcuts(const QKeySequence &seq);
00068 
00073     bool conflictWithGlobalShortcuts(const QKeySequence &seq);
00074 
00078     bool stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq);
00079 
00080     bool checkAgainstStandardShortcuts() const
00081     {
00082         return checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts;
00083     }
00084 
00085     bool checkAgainstGlobalShortcuts() const
00086     {
00087         return checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts;
00088     }
00089 
00090     bool checkAgainstLocalShortcuts() const
00091     {
00092         return checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts;
00093     }
00094 
00095     void controlModifierlessTimout()
00096     {
00097         if (nKey != 0 && !modifierKeys) {
00098             // No modifier key pressed currently. Start the timout
00099             modifierlessTimeout.start(600);
00100         } else {
00101             // A modifier is pressed. Stop the timeout
00102             modifierlessTimeout.stop();
00103         }
00104 
00105     }
00106 
00107 
00108     void cancelRecording()
00109     {
00110         keySequence = oldKeySequence;
00111         doneRecording();
00112     }
00113 
00114 
00115     bool isShiftAsModifierAllowed(int keyQt)
00116     {
00117         // Shift only works as a modifier with certain keys. It's not possible
00118         // to enter the SHIFT+5 key sequence for me because this is handled as
00119         // '%' by qt on my keyboard.
00120         // The working keys are all hardcoded here :-(
00121         if (keyQt >= Qt::Key_F1 && keyQt <= Qt::Key_F35)
00122             return true;
00123 
00124         if (QChar(keyQt).isLetter())
00125             return true;
00126 
00127         switch (keyQt) {
00128             case Qt::Key_Return:
00129             case Qt::Key_Space:
00130             case Qt::Key_Backspace:
00131             case Qt::Key_Escape:
00132             case Qt::Key_Print:
00133             case Qt::Key_ScrollLock:
00134             case Qt::Key_Pause:
00135             case Qt::Key_PageUp:
00136             case Qt::Key_PageDown:
00137             case Qt::Key_Insert:
00138             case Qt::Key_Delete:
00139             case Qt::Key_Home:
00140             case Qt::Key_End:
00141             case Qt::Key_Up:
00142             case Qt::Key_Down:
00143             case Qt::Key_Left:
00144             case Qt::Key_Right:
00145                 return true;
00146 
00147             default:
00148                 return false;
00149         }
00150     }
00151 
00152 
00153     bool promptStealShortcutSystemwide(
00154             QWidget *parent,
00155             const QHash<QKeySequence, QList<KGlobalShortcutInfo> > &shortcuts,
00156             const QKeySequence &sequence)
00157     {
00158         if (shortcuts.isEmpty()) {
00159             // Usage error. Just say no
00160             return false;
00161         }
00162 
00163         QString clashingKeys = "";
00164         Q_FOREACH (const QKeySequence &seq, shortcuts.keys()) {
00165             Q_FOREACH (const KGlobalShortcutInfo &info, shortcuts[seq]) {
00166                 clashingKeys += i18n("Shortcut '%1' in Application %2 for action %3\n",
00167                         seq.toString(),
00168                         info.componentFriendlyName(),
00169                         info.friendlyName());
00170             }
00171         }
00172 
00173         QString message = i18n("The shortcut '%1' conflicts with the following key combinations:\n",
00174                 sequence.toString());
00175         message+=clashingKeys;
00176 
00177         QString title = i18n("Conflict With Registered Global Shortcut(s)");
00178 
00179         return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18n("Reassign")))
00180                == KMessageBox::Continue;
00181     }
00182 
00183 
00184 //private slot
00185     void doneRecording(bool validate = true);
00186 
00187 //members
00188     KKeySequenceWidget *const q;
00189     QHBoxLayout *layout;
00190     KKeySequenceButton *keyButton;
00191     QToolButton *clearButton;
00192 
00193     QKeySequence keySequence;
00194     QKeySequence oldKeySequence;
00195     QTimer modifierlessTimeout;
00196     bool allowModifierless;
00197     uint nKey;
00198     uint modifierKeys;
00199     bool isRecording;
00200     bool multiKeyShortcutsAllowed;
00201     QString componentName;
00202 
00204     KKeySequenceWidget::ShortcutTypes checkAgainstShortcutTypes;
00205 
00209     QList<QAction*> checkList; // deprecated
00210 
00214     QList<KActionCollection*> checkActionCollections;
00215 
00219     QList<KAction*> stealActions;
00220 
00221     bool stealShortcuts(const QList<KAction *> &actions, const QKeySequence &seq);
00222     void wontStealShortcut(QAction *item, const QKeySequence &seq);
00223 
00224 };
00225 
00226 KKeySequenceWidgetPrivate::KKeySequenceWidgetPrivate(KKeySequenceWidget *q)
00227     : q(q)
00228      ,layout(NULL)
00229      ,keyButton(NULL)
00230      ,clearButton(NULL)
00231      ,allowModifierless(false)
00232      ,nKey(0)
00233      ,modifierKeys(0)
00234      ,isRecording(false)
00235      ,multiKeyShortcutsAllowed(true)
00236      ,componentName()
00237      ,checkAgainstShortcutTypes(KKeySequenceWidget::LocalShortcuts & KKeySequenceWidget::GlobalShortcuts)
00238      ,stealActions()
00239 {}
00240 
00241 
00242 bool KKeySequenceWidgetPrivate::stealShortcuts(
00243         const QList<KAction *> &actions,
00244         const QKeySequence &seq)
00245 {
00246     QString title = i18n("Shortcut Conflict(s)");
00247 
00248     QString conflictingShortcuts;
00249     Q_FOREACH(const KAction *action, actions) {
00250         conflictingShortcuts += i18n("Shortcut(s) '%1' for action '%2'\n",
00251                 action->shortcut().toString(),
00252                 KGlobal::locale()->removeAcceleratorMarker(action->text()));
00253     }
00254     QString message = i18n(
00255             "The \"%1\" shortcut is ambiguous with the following shortcuts.\n"
00256             "Do you want to assign an empty shortcut to these actions?\n"
00257             "%2",
00258             seq.toString(QKeySequence::NativeText),
00259             conflictingShortcuts);
00260 
00261     if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue)
00262         return false;
00263 
00264     return true;
00265 }
00266 
00267 void KKeySequenceWidgetPrivate::wontStealShortcut(QAction *item, const QKeySequence &seq)
00268 {
00269     QString title( i18n( "Shortcut conflict" ) );
00270     QString msg( i18n( "<qt>The '%1' key combination is already used by the <b>%2</b> action.<br>"
00271             "Please select a different one.</qt>", seq.toString(QKeySequence::NativeText) ,
00272             KGlobal::locale()->removeAcceleratorMarker(item->text()) ) );
00273     KMessageBox::sorry( q, msg );
00274 }
00275 
00276 
00277 KKeySequenceWidget::KKeySequenceWidget(QWidget *parent)
00278  : QWidget(parent),
00279    d(new KKeySequenceWidgetPrivate(this))
00280 {
00281     d->init();
00282     connect(d->keyButton, SIGNAL(clicked()), this, SLOT(captureKeySequence()));
00283     connect(d->clearButton, SIGNAL(clicked()), this, SLOT(clearKeySequence()));
00284     connect(&d->modifierlessTimeout, SIGNAL(timeout()), this, SLOT(doneRecording()));
00285     //TODO: how to adopt style changes at runtime?
00286     /*QFont modFont = d->clearButton->font();
00287     modFont.setStyleHint(QFont::TypeWriter);
00288     d->clearButton->setFont(modFont);*/
00289     d->updateShortcutDisplay();
00290 }
00291 
00292 
00293 void KKeySequenceWidgetPrivate::init()
00294 {
00295     layout = new QHBoxLayout(q);
00296 
00297     keyButton = new KKeySequenceButton(this, q);
00298     keyButton->setFocusPolicy(Qt::StrongFocus);
00299     keyButton->setIcon(KIcon("configure"));
00300     layout->addWidget(keyButton);
00301 
00302     clearButton = new QToolButton(q);
00303     layout->addWidget(clearButton);
00304 
00305     if (qApp->isLeftToRight())
00306         clearButton->setIcon(KIcon("edit-clear-locationbar-rtl"));
00307     else
00308         clearButton->setIcon(KIcon("edit-clear-locationbar-ltr"));
00309 }
00310 
00311 
00312 KKeySequenceWidget::~KKeySequenceWidget ()
00313 {
00314     delete d;
00315 }
00316 
00317 
00318 KKeySequenceWidget::ShortcutTypes KKeySequenceWidget::checkForConflictsAgainst() const
00319 {
00320     return d->checkAgainstShortcutTypes;
00321 }
00322 
00323 
00324 void KKeySequenceWidget::setComponentName(const QString &componentName)
00325 {
00326     d->componentName = componentName;
00327 }
00328 
00329 bool KKeySequenceWidget::multiKeyShortcutsAllowed() const
00330 {
00331     return d->multiKeyShortcutsAllowed;
00332 }
00333 
00334 
00335 void KKeySequenceWidget::setMultiKeyShortcutsAllowed(bool allowed)
00336 {
00337     d->multiKeyShortcutsAllowed = allowed;
00338 }
00339 
00340 
00341 void KKeySequenceWidget::setCheckForConflictsAgainst(ShortcutTypes types)
00342 {
00343     d->checkAgainstShortcutTypes = types;
00344 }
00345 
00346 void KKeySequenceWidget::setModifierlessAllowed(bool allow)
00347 {
00348     d->allowModifierless = allow;
00349 }
00350 
00351 
00352 bool KKeySequenceWidget::isKeySequenceAvailable(const QKeySequence &keySequence) const
00353 {
00354     return ! ( d->conflictWithLocalShortcuts(keySequence)
00355                || d->conflictWithGlobalShortcuts(keySequence)
00356                || d->conflictWithStandardShortcuts(keySequence));
00357 }
00358 
00359 
00360 bool KKeySequenceWidget::isModifierlessAllowed()
00361 {
00362     return d->allowModifierless;
00363 }
00364 
00365 
00366 void KKeySequenceWidget::setClearButtonShown(bool show)
00367 {
00368     d->clearButton->setVisible(show);
00369 }
00370 
00371 void KKeySequenceWidget::setCheckActionList(const QList<QAction*> &checkList) // deprecated
00372 {
00373     d->checkList = checkList;
00374     Q_ASSERT(d->checkActionCollections.isEmpty()); // don't call this method if you call setCheckActionCollections!
00375 }
00376 
00377 void KKeySequenceWidget::setCheckActionCollections(const QList<KActionCollection *>& actionCollections)
00378 {
00379     d->checkActionCollections = actionCollections;
00380 }
00381 
00382 //slot
00383 void KKeySequenceWidget::captureKeySequence()
00384 {
00385     d->startRecording();
00386 }
00387 
00388 
00389 QKeySequence KKeySequenceWidget::keySequence() const
00390 {
00391     return d->keySequence;
00392 }
00393 
00394 
00395 //slot
00396 void KKeySequenceWidget::setKeySequence(const QKeySequence &seq, Validation validate)
00397 {
00398     // oldKeySequence holds the key sequence before recording started, if setKeySequence()
00399     // is called while not recording then set oldKeySequence to the existing sequence so
00400     // that the keySequenceChanged() signal is emitted if the new and previous key
00401     // sequences are different
00402     if (!d->isRecording)
00403         d->oldKeySequence = d->keySequence;
00404 
00405     d->keySequence = seq;
00406     d->doneRecording(validate == Validate);
00407 }
00408 
00409 
00410 //slot
00411 void KKeySequenceWidget::clearKeySequence()
00412 {
00413     setKeySequence(QKeySequence());
00414 }
00415 
00416 //slot
00417 void KKeySequenceWidget::applyStealShortcut()
00418 {
00419     QSet<KActionCollection *> changedCollections;
00420 
00421     Q_FOREACH (KAction *stealAction, d->stealActions) {
00422 
00423         // Stealing a shortcut means setting it to an empty one.
00424         stealAction->setShortcut(KShortcut(), KAction::ActiveShortcut);
00425 
00426         // The following code will find the action we are about to
00427         // steal from and save it's actioncollection.
00428         KActionCollection* parentCollection = 0;
00429         foreach(KActionCollection* collection, d->checkActionCollections) {
00430             if (collection->actions().contains(stealAction)) {
00431                 parentCollection = collection;
00432                 break;
00433             }
00434         }
00435 
00436         // Remember the changed collection
00437         if (parentCollection) {
00438             changedCollections.insert(parentCollection);
00439         }
00440     }
00441 
00442     Q_FOREACH (KActionCollection *col, changedCollections) {
00443             col->writeSettings();
00444     }
00445 
00446     d->stealActions.clear();
00447 }
00448 
00449 void KKeySequenceButton::setText(const QString &text)
00450 {
00451     QPushButton::setText(text);
00452     //setFixedSize( sizeHint().width()+12, sizeHint().height()+8 );
00453 }
00454 
00455 
00456 void KKeySequenceWidgetPrivate::startRecording()
00457 {
00458     nKey = 0;
00459     modifierKeys = 0;
00460     oldKeySequence = keySequence;
00461     keySequence = QKeySequence();
00462     isRecording = true;
00463     keyButton->grabKeyboard();
00464 
00465     if (!QWidget::keyboardGrabber()) {
00466         kWarning() << "Failed to grab the keyboard! Most likely qt's nograb option is active";
00467     }
00468 
00469     keyButton->setDown(true);
00470     updateShortcutDisplay();
00471 }
00472 
00473 
00474 void KKeySequenceWidgetPrivate::doneRecording(bool validate)
00475 {
00476     modifierlessTimeout.stop();
00477     isRecording = false;
00478     keyButton->releaseKeyboard();
00479     keyButton->setDown(false);
00480     stealActions.clear();
00481 
00482     if (keySequence==oldKeySequence) {
00483         // The sequence hasn't changed
00484         updateShortcutDisplay();
00485         return;
00486     }
00487 
00488     if (validate && !q->isKeySequenceAvailable(keySequence)) {
00489         // The sequence had conflicts and the user said no to stealing it
00490         keySequence = oldKeySequence;
00491     } else {
00492         emit q->keySequenceChanged(keySequence);
00493     }
00494 
00495     updateShortcutDisplay();
00496 }
00497 
00498 
00499 bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(const QKeySequence &keySequence)
00500 {
00501     if (!(checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts)) {
00502         return false;
00503     }
00504 
00505     // Global shortcuts are on key+modifier shortcuts. They can clash with
00506     // each of the keys of a multi key shortcut.
00507     QHash<QKeySequence, QList<KGlobalShortcutInfo> > others;
00508     for (uint i=0; i<keySequence.count(); ++i) {
00509         QKeySequence tmp(keySequence[i]);
00510 
00511         if (!KGlobalAccel::isGlobalShortcutAvailable(tmp, componentName)) {
00512                 others.insert(tmp, KGlobalAccel::getGlobalShortcutsByKey(tmp));
00513         }
00514     }
00515 
00516     if (!others.isEmpty()
00517             && !promptStealShortcutSystemwide(q, others, keySequence)) {
00518         return true;
00519     }
00520 
00521     // The user approved stealing the shortcut. We have to steal
00522     // it immediately because KAction::setGlobalShortcut() refuses
00523     // to set a global shortcut that is already used. There is no
00524     // error it just silently fails. So be nice because this is
00525     // most likely the first action that is done in the slot
00526     // listening to keySequenceChanged().
00527     for (uint i=0; i<keySequence.count(); ++i) {
00528         KGlobalAccel::stealShortcutSystemwide(keySequence[i]);
00529     }
00530     return false;
00531 }
00532 
00533 
00534 bool KKeySequenceWidgetPrivate::conflictWithLocalShortcuts(const QKeySequence &keySequence)
00535 {
00536     if (!(checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts)) {
00537         return false;
00538     }
00539 
00540     // We have actions both in the deprecated checkList and the
00541     // checkActionCollections list. Add all the actions to a single list to
00542     // be able to process them in a single loop below.
00543     // Note that this can't be done in setCheckActionCollections(), because we
00544     // keep pointers to the action collections, and between the call to
00545     // setCheckActionCollections() and this function some actions might already be
00546     // removed from the collection again.
00547     QList<QAction*> allActions;
00548     allActions += checkList;
00549     foreach(KActionCollection* collection, checkActionCollections) {
00550         allActions += collection->actions();
00551     }
00552 
00553     // Because of multikey shortcuts we can have clashes with many shortcuts.
00554     //
00555     // Example 1:
00556     //
00557     // Application currently uses 'CTRL-X,a', 'CTRL-X,f' and 'CTRL-X,CTRL-F'
00558     // and the user wants to use 'CTRL-X'. 'CTRL-X' will only trigger as
00559     // 'activatedAmbiguously()' for obvious reasons.
00560     //
00561     // Example 2:
00562     //
00563     // Application currently uses 'CTRL-X'. User wants to use 'CTRL-X,CTRL-F'.
00564     // This will shadow 'CTRL-X' for the same reason as above.
00565     //
00566     // Example 3:
00567     //
00568     // Some weird combination of Example 1 and 2 with three shortcuts using
00569     // 1/2/3 key shortcuts. I think you can imagine.
00570     QList<KAction*> conflictingActions;
00571 
00572     //find conflicting shortcuts with existing actions
00573     foreach(QAction * qaction , allActions ) {
00574         KAction *kaction=qobject_cast<KAction*>(qaction);
00575         if(kaction) {
00576             if(kaction->shortcut().conflictsWith(keySequence)) {
00577                 // A conflict with a KAction. If that action is configurable
00578                 // ask the user what to do. If not reject this keySequence.
00579                 if(kaction->isShortcutConfigurable ()) {
00580                     conflictingActions.append(kaction);
00581                 } else {
00582                     wontStealShortcut(kaction, keySequence);
00583                     return true;
00584                 }
00585             }
00586         } else {
00587             if(qaction->shortcut() == keySequence) {
00588                 // A conflict with a QAction. Don't know why :-( but we won't
00589                 // steal from that kind of actions.
00590                 wontStealShortcut(qaction, keySequence);
00591                 return true;
00592             }
00593         }
00594     }
00595 
00596     if (conflictingActions.isEmpty()) {
00597         // No conflicting shortcuts found.
00598         return false;
00599     }
00600 
00601     if(stealShortcuts(conflictingActions, keySequence)) {
00602         stealActions = conflictingActions;
00603         // Announce that the user
00604         // agreed
00605         Q_FOREACH (KAction *stealAction, stealActions) {
00606             emit q->stealShortcut(
00607                 keySequence,
00608                 stealAction);
00609         }
00610         return false;
00611     } else {
00612         return true;
00613     }
00614 }
00615 
00616 
00617 bool KKeySequenceWidgetPrivate::conflictWithStandardShortcuts(const QKeySequence &keySequence)
00618 {
00619     if (!(checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts)) {
00620         return false;
00621     }
00622 
00623     KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence);
00624     if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) {
00625         return true;
00626     }
00627     return false;
00628 }
00629 
00630 
00631 bool KKeySequenceWidgetPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq)
00632 {
00633     QString title = i18n("Conflict with Standard Application Shortcut");
00634     QString message = i18n("The '%1' key combination is also used for the standard action "
00635                            "\"%2\" that some applications use.\n"
00636                            "Do you really want to use it as a global shortcut as well?",
00637                            seq.toString(QKeySequence::NativeText), KStandardShortcut::label(std));
00638 
00639     if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) {
00640         return false;
00641     }
00642     return true;
00643 }
00644 
00645 
00646 void KKeySequenceWidgetPrivate::updateShortcutDisplay()
00647 {
00648     //empty string if no non-modifier was pressed
00649     QString s = keySequence.toString(QKeySequence::NativeText);
00650     s.replace('&', QLatin1String("&&"));
00651 
00652     if (isRecording) {
00653         if (modifierKeys) {
00654             if (!s.isEmpty()) s.append(",");
00655             if (modifierKeys & Qt::META)  s += KKeyServer::modToStringUser(Qt::META) + '+';
00656 #if defined(Q_WS_MAC)
00657             if (modifierKeys & Qt::ALT)   s += KKeyServer::modToStringUser(Qt::ALT) + '+';
00658             if (modifierKeys & Qt::CTRL)  s += KKeyServer::modToStringUser(Qt::CTRL) + '+';
00659 #elif defined(Q_WS_X11)
00660             if (modifierKeys & Qt::CTRL)  s += KKeyServer::modToStringUser(Qt::CTRL) + '+';
00661             if (modifierKeys & Qt::ALT)   s += KKeyServer::modToStringUser(Qt::ALT) + '+';
00662 #endif
00663             if (modifierKeys & Qt::SHIFT) s += KKeyServer::modToStringUser(Qt::SHIFT) + '+';
00664 
00665         } else if (nKey == 0) {
00666             s = i18nc("What the user inputs now will be taken as the new shortcut", "Input");
00667         }
00668         //make it clear that input is still going on
00669         s.append(" ...");
00670     }
00671 
00672     if (s.isEmpty()) {
00673         s = i18nc("No shortcut defined", "None");
00674     }
00675 
00676     s.prepend(' ');
00677     s.append(' ');
00678     keyButton->setText(s);
00679 }
00680 
00681 
00682 KKeySequenceButton::~KKeySequenceButton()
00683 {
00684 }
00685 
00686 
00687 //prevent Qt from special casing Tab and Backtab
00688 bool KKeySequenceButton::event (QEvent* e)
00689 {
00690     if (d->isRecording && e->type() == QEvent::KeyPress) {
00691         keyPressEvent(static_cast<QKeyEvent *>(e));
00692         return true;
00693     }
00694 
00695     // The shortcut 'alt+c' ( or any other dialog local action shortcut )
00696     // ended the recording and triggered the action associated with the
00697     // action. In case of 'alt+c' ending the dialog.  It seems that those
00698     // ShortcutOverride events get sent even if grabKeyboard() is active.
00699     if (d->isRecording && e->type() == QEvent::ShortcutOverride) {
00700         e->accept();
00701         return true;
00702     }
00703 
00704     return QPushButton::event(e);
00705 }
00706 
00707 
00708 void KKeySequenceButton::keyPressEvent(QKeyEvent *e)
00709 {
00710     int keyQt = e->key();
00711     if (keyQt == -1) {
00712         // Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key.
00713         // We cannot do anything useful with those (several keys have -1, indistinguishable)
00714         // and QKeySequence.toString() will also yield a garbage string.
00715         KMessageBox::sorry(this,
00716                 i18n("The key you just pressed isn't supported by Qt."),
00717                 i18n("Unsupported Key"));
00718         return d->cancelRecording();
00719     }
00720 
00721     uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
00722 
00723     //don't have the return or space key appear as first key of the sequence when they
00724     //were pressed to start editing - catch and them and imitate their effect
00725     if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) {
00726         d->startRecording();
00727         d->modifierKeys = newModifiers;
00728         d->updateShortcutDisplay();
00729         return;
00730     }
00731 
00732     // We get events even if recording isn't active.
00733     if (!d->isRecording)
00734         return QPushButton::keyPressEvent(e);
00735 
00736     e->accept();
00737     d->modifierKeys = newModifiers;
00738 
00739 
00740     switch(keyQt) {
00741     case Qt::Key_AltGr: //or else we get unicode salad
00742         return;
00743     case Qt::Key_Shift:
00744     case Qt::Key_Control:
00745     case Qt::Key_Alt:
00746     case Qt::Key_Meta:
00747     case Qt::Key_Menu: //unused (yes, but why?)
00748         d->controlModifierlessTimout();
00749         d->updateShortcutDisplay();
00750         break;
00751     default:
00752 
00753         if (d->nKey == 0 && !(d->modifierKeys & ~Qt::SHIFT)) {
00754             // It's the first key and no modifier pressed. Check if this is
00755             // allowed
00756             if (!(KKeySequenceWidgetPrivate::isOkWhenModifierless(keyQt)
00757                             || d->allowModifierless)) {
00758                 // No it's not
00759                 return;
00760             }
00761         }
00762 
00763         // We now have a valid key press.
00764         if (keyQt) {
00765             if (d->isShiftAsModifierAllowed(keyQt)) {
00766                 keyQt |= d->modifierKeys;
00767             }
00768             else
00769                 keyQt |= (d->modifierKeys & ~Qt::SHIFT);
00770 
00771             if (d->nKey == 0) {
00772                 d->keySequence = QKeySequence(keyQt);
00773             } else {
00774                 d->keySequence =
00775                   KKeySequenceWidgetPrivate::appendToSequence(d->keySequence, keyQt);
00776             }
00777 
00778             d->nKey++;
00779             if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) {
00780                 d->doneRecording();
00781                 return;
00782             }
00783             d->controlModifierlessTimout();
00784             d->updateShortcutDisplay();
00785         }
00786     }
00787 }
00788 
00789 
00790 void KKeySequenceButton::keyReleaseEvent(QKeyEvent *e)
00791 {
00792     if (e->key() == -1) {
00793         // ignore garbage, see keyPressEvent()
00794         return;
00795     }
00796 
00797     if (!d->isRecording)
00798         return QPushButton::keyReleaseEvent(e);
00799 
00800     e->accept();
00801 
00802     uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
00803 
00804     //if a modifier that belongs to the shortcut was released...
00805     if ((newModifiers & d->modifierKeys) < d->modifierKeys) {
00806         d->modifierKeys = newModifiers;
00807         d->controlModifierlessTimout();
00808         d->updateShortcutDisplay();
00809     }
00810 }
00811 
00812 
00813 //static
00814 QKeySequence KKeySequenceWidgetPrivate::appendToSequence(const QKeySequence& seq, int keyQt)
00815 {
00816     switch (seq.count()) {
00817     case 0:
00818         return QKeySequence(keyQt);
00819     case 1:
00820         return QKeySequence(seq[0], keyQt);
00821     case 2:
00822         return QKeySequence(seq[0], seq[1], keyQt);
00823     case 3:
00824         return QKeySequence(seq[0], seq[1], seq[2], keyQt);
00825     default:
00826         return seq;
00827     }
00828 }
00829 
00830 
00831 //static
00832 bool KKeySequenceWidgetPrivate::isOkWhenModifierless(int keyQt)
00833 {
00834     //this whole function is a hack, but especially the first line of code
00835     if (QKeySequence(keyQt).toString().length() == 1)
00836         return false;
00837 
00838     switch (keyQt) {
00839     case Qt::Key_Return:
00840     case Qt::Key_Space:
00841     case Qt::Key_Tab:
00842     case Qt::Key_Backtab: //does this ever happen?
00843     case Qt::Key_Backspace:
00844     case Qt::Key_Delete:
00845         return false;
00846     default:
00847         return true;
00848     }
00849 }
00850 
00851 #include "kkeysequencewidget.moc"
00852 #include "kkeysequencewidget_p.moc"

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal