• Skip to content
  • Skip to link menu
KDE 4.3 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(QKeySequence::NativeText),
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     if (keySequence.isEmpty())
00355         return true;
00356     return ! ( d->conflictWithLocalShortcuts(keySequence)
00357                || d->conflictWithGlobalShortcuts(keySequence)
00358                || d->conflictWithStandardShortcuts(keySequence));
00359 }
00360 
00361 
00362 bool KKeySequenceWidget::isModifierlessAllowed()
00363 {
00364     return d->allowModifierless;
00365 }
00366 
00367 
00368 void KKeySequenceWidget::setClearButtonShown(bool show)
00369 {
00370     d->clearButton->setVisible(show);
00371 }
00372 
00373 void KKeySequenceWidget::setCheckActionList(const QList<QAction*> &checkList) // deprecated
00374 {
00375     d->checkList = checkList;
00376     Q_ASSERT(d->checkActionCollections.isEmpty()); // don't call this method if you call setCheckActionCollections!
00377 }
00378 
00379 void KKeySequenceWidget::setCheckActionCollections(const QList<KActionCollection *>& actionCollections)
00380 {
00381     d->checkActionCollections = actionCollections;
00382 }
00383 
00384 //slot
00385 void KKeySequenceWidget::captureKeySequence()
00386 {
00387     d->startRecording();
00388 }
00389 
00390 
00391 QKeySequence KKeySequenceWidget::keySequence() const
00392 {
00393     return d->keySequence;
00394 }
00395 
00396 
00397 //slot
00398 void KKeySequenceWidget::setKeySequence(const QKeySequence &seq, Validation validate)
00399 {
00400     // oldKeySequence holds the key sequence before recording started, if setKeySequence()
00401     // is called while not recording then set oldKeySequence to the existing sequence so
00402     // that the keySequenceChanged() signal is emitted if the new and previous key
00403     // sequences are different
00404     if (!d->isRecording)
00405         d->oldKeySequence = d->keySequence;
00406 
00407     d->keySequence = seq;
00408     d->doneRecording(validate == Validate);
00409 }
00410 
00411 
00412 //slot
00413 void KKeySequenceWidget::clearKeySequence()
00414 {
00415     setKeySequence(QKeySequence());
00416 }
00417 
00418 //slot
00419 void KKeySequenceWidget::applyStealShortcut()
00420 {
00421     QSet<KActionCollection *> changedCollections;
00422 
00423     Q_FOREACH (KAction *stealAction, d->stealActions) {
00424 
00425         // Stealing a shortcut means setting it to an empty one.
00426         stealAction->setShortcut(KShortcut(), KAction::ActiveShortcut);
00427 
00428         // The following code will find the action we are about to
00429         // steal from and save it's actioncollection.
00430         KActionCollection* parentCollection = 0;
00431         foreach(KActionCollection* collection, d->checkActionCollections) {
00432             if (collection->actions().contains(stealAction)) {
00433                 parentCollection = collection;
00434                 break;
00435             }
00436         }
00437 
00438         // Remember the changed collection
00439         if (parentCollection) {
00440             changedCollections.insert(parentCollection);
00441         }
00442     }
00443 
00444     Q_FOREACH (KActionCollection *col, changedCollections) {
00445             col->writeSettings();
00446     }
00447 
00448     d->stealActions.clear();
00449 }
00450 
00451 void KKeySequenceButton::setText(const QString &text)
00452 {
00453     QPushButton::setText(text);
00454     //setFixedSize( sizeHint().width()+12, sizeHint().height()+8 );
00455 }
00456 
00457 
00458 void KKeySequenceWidgetPrivate::startRecording()
00459 {
00460     nKey = 0;
00461     modifierKeys = 0;
00462     oldKeySequence = keySequence;
00463     keySequence = QKeySequence();
00464     isRecording = true;
00465     keyButton->grabKeyboard();
00466 
00467     if (!QWidget::keyboardGrabber()) {
00468         kWarning() << "Failed to grab the keyboard! Most likely qt's nograb option is active";
00469     }
00470 
00471     keyButton->setDown(true);
00472     updateShortcutDisplay();
00473 }
00474 
00475 
00476 void KKeySequenceWidgetPrivate::doneRecording(bool validate)
00477 {
00478     modifierlessTimeout.stop();
00479     isRecording = false;
00480     keyButton->releaseKeyboard();
00481     keyButton->setDown(false);
00482     stealActions.clear();
00483 
00484     if (keySequence==oldKeySequence) {
00485         // The sequence hasn't changed
00486         updateShortcutDisplay();
00487         return;
00488     }
00489 
00490     if (validate && !q->isKeySequenceAvailable(keySequence)) {
00491         // The sequence had conflicts and the user said no to stealing it
00492         keySequence = oldKeySequence;
00493     } else {
00494         emit q->keySequenceChanged(keySequence);
00495     }
00496 
00497     updateShortcutDisplay();
00498 }
00499 
00500 
00501 bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(const QKeySequence &keySequence)
00502 {
00503     if (!(checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts)) {
00504         return false;
00505     }
00506 
00507     // Global shortcuts are on key+modifier shortcuts. They can clash with
00508     // each of the keys of a multi key shortcut.
00509     QHash<QKeySequence, QList<KGlobalShortcutInfo> > others;
00510     for (uint i=0; i<keySequence.count(); ++i) {
00511         QKeySequence tmp(keySequence[i]);
00512 
00513         if (!KGlobalAccel::isGlobalShortcutAvailable(tmp, componentName)) {
00514                 others.insert(tmp, KGlobalAccel::getGlobalShortcutsByKey(tmp));
00515         }
00516     }
00517 
00518     if (!others.isEmpty()
00519             && !promptStealShortcutSystemwide(q, others, keySequence)) {
00520         return true;
00521     }
00522 
00523     // The user approved stealing the shortcut. We have to steal
00524     // it immediately because KAction::setGlobalShortcut() refuses
00525     // to set a global shortcut that is already used. There is no
00526     // error it just silently fails. So be nice because this is
00527     // most likely the first action that is done in the slot
00528     // listening to keySequenceChanged().
00529     for (uint i=0; i<keySequence.count(); ++i) {
00530         KGlobalAccel::stealShortcutSystemwide(keySequence[i]);
00531     }
00532     return false;
00533 }
00534 
00535 
00536 bool KKeySequenceWidgetPrivate::conflictWithLocalShortcuts(const QKeySequence &keySequence)
00537 {
00538     if (!(checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts)) {
00539         return false;
00540     }
00541 
00542     // We have actions both in the deprecated checkList and the
00543     // checkActionCollections list. Add all the actions to a single list to
00544     // be able to process them in a single loop below.
00545     // Note that this can't be done in setCheckActionCollections(), because we
00546     // keep pointers to the action collections, and between the call to
00547     // setCheckActionCollections() and this function some actions might already be
00548     // removed from the collection again.
00549     QList<QAction*> allActions;
00550     allActions += checkList;
00551     foreach(KActionCollection* collection, checkActionCollections) {
00552         allActions += collection->actions();
00553     }
00554 
00555     // Because of multikey shortcuts we can have clashes with many shortcuts.
00556     //
00557     // Example 1:
00558     //
00559     // Application currently uses 'CTRL-X,a', 'CTRL-X,f' and 'CTRL-X,CTRL-F'
00560     // and the user wants to use 'CTRL-X'. 'CTRL-X' will only trigger as
00561     // 'activatedAmbiguously()' for obvious reasons.
00562     //
00563     // Example 2:
00564     //
00565     // Application currently uses 'CTRL-X'. User wants to use 'CTRL-X,CTRL-F'.
00566     // This will shadow 'CTRL-X' for the same reason as above.
00567     //
00568     // Example 3:
00569     //
00570     // Some weird combination of Example 1 and 2 with three shortcuts using
00571     // 1/2/3 key shortcuts. I think you can imagine.
00572     QList<KAction*> conflictingActions;
00573 
00574     //find conflicting shortcuts with existing actions
00575     foreach(QAction * qaction , allActions ) {
00576         KAction *kaction=qobject_cast<KAction*>(qaction);
00577         if(kaction) {
00578             if(kaction->shortcut().conflictsWith(keySequence)) {
00579                 // A conflict with a KAction. If that action is configurable
00580                 // ask the user what to do. If not reject this keySequence.
00581                 if(kaction->isShortcutConfigurable ()) {
00582                     conflictingActions.append(kaction);
00583                 } else {
00584                     wontStealShortcut(kaction, keySequence);
00585                     return true;
00586                 }
00587             }
00588         } else {
00589             if(qaction->shortcut() == keySequence) {
00590                 // A conflict with a QAction. Don't know why :-( but we won't
00591                 // steal from that kind of actions.
00592                 wontStealShortcut(qaction, keySequence);
00593                 return true;
00594             }
00595         }
00596     }
00597 
00598     if (conflictingActions.isEmpty()) {
00599         // No conflicting shortcuts found.
00600         return false;
00601     }
00602 
00603     if(stealShortcuts(conflictingActions, keySequence)) {
00604         stealActions = conflictingActions;
00605         // Announce that the user
00606         // agreed
00607         Q_FOREACH (KAction *stealAction, stealActions) {
00608             emit q->stealShortcut(
00609                 keySequence,
00610                 stealAction);
00611         }
00612         return false;
00613     } else {
00614         return true;
00615     }
00616 }
00617 
00618 
00619 bool KKeySequenceWidgetPrivate::conflictWithStandardShortcuts(const QKeySequence &keySequence)
00620 {
00621     if (!(checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts)) {
00622         return false;
00623     }
00624 
00625     KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence);
00626     if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) {
00627         return true;
00628     }
00629     return false;
00630 }
00631 
00632 
00633 bool KKeySequenceWidgetPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq)
00634 {
00635     QString title = i18n("Conflict with Standard Application Shortcut");
00636     QString message = i18n("The '%1' key combination is also used for the standard action "
00637                            "\"%2\" that some applications use.\n"
00638                            "Do you really want to use it as a global shortcut as well?",
00639                            seq.toString(QKeySequence::NativeText), KStandardShortcut::label(std));
00640 
00641     if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) {
00642         return false;
00643     }
00644     return true;
00645 }
00646 
00647 
00648 void KKeySequenceWidgetPrivate::updateShortcutDisplay()
00649 {
00650     //empty string if no non-modifier was pressed
00651     QString s = keySequence.toString(QKeySequence::NativeText);
00652     s.replace('&', QLatin1String("&&"));
00653 
00654     if (isRecording) {
00655         if (modifierKeys) {
00656             if (!s.isEmpty()) s.append(",");
00657             if (modifierKeys & Qt::META)  s += KKeyServer::modToStringUser(Qt::META) + '+';
00658 #if defined(Q_WS_MAC)
00659             if (modifierKeys & Qt::ALT)   s += KKeyServer::modToStringUser(Qt::ALT) + '+';
00660             if (modifierKeys & Qt::CTRL)  s += KKeyServer::modToStringUser(Qt::CTRL) + '+';
00661 #elif defined(Q_WS_X11)
00662             if (modifierKeys & Qt::CTRL)  s += KKeyServer::modToStringUser(Qt::CTRL) + '+';
00663             if (modifierKeys & Qt::ALT)   s += KKeyServer::modToStringUser(Qt::ALT) + '+';
00664 #endif
00665             if (modifierKeys & Qt::SHIFT) s += KKeyServer::modToStringUser(Qt::SHIFT) + '+';
00666 
00667         } else if (nKey == 0) {
00668             s = i18nc("What the user inputs now will be taken as the new shortcut", "Input");
00669         }
00670         //make it clear that input is still going on
00671         s.append(" ...");
00672     }
00673 
00674     if (s.isEmpty()) {
00675         s = i18nc("No shortcut defined", "None");
00676     }
00677 
00678     s.prepend(' ');
00679     s.append(' ');
00680     keyButton->setText(s);
00681 }
00682 
00683 
00684 KKeySequenceButton::~KKeySequenceButton()
00685 {
00686 }
00687 
00688 
00689 //prevent Qt from special casing Tab and Backtab
00690 bool KKeySequenceButton::event (QEvent* e)
00691 {
00692     if (d->isRecording && e->type() == QEvent::KeyPress) {
00693         keyPressEvent(static_cast<QKeyEvent *>(e));
00694         return true;
00695     }
00696 
00697     // The shortcut 'alt+c' ( or any other dialog local action shortcut )
00698     // ended the recording and triggered the action associated with the
00699     // action. In case of 'alt+c' ending the dialog.  It seems that those
00700     // ShortcutOverride events get sent even if grabKeyboard() is active.
00701     if (d->isRecording && e->type() == QEvent::ShortcutOverride) {
00702         e->accept();
00703         return true;
00704     }
00705 
00706     return QPushButton::event(e);
00707 }
00708 
00709 
00710 void KKeySequenceButton::keyPressEvent(QKeyEvent *e)
00711 {
00712     int keyQt = e->key();
00713     if (keyQt == -1) {
00714         // Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key.
00715         // We cannot do anything useful with those (several keys have -1, indistinguishable)
00716         // and QKeySequence.toString() will also yield a garbage string.
00717         KMessageBox::sorry(this,
00718                 i18n("The key you just pressed is not supported by Qt."),
00719                 i18n("Unsupported Key"));
00720         return d->cancelRecording();
00721     }
00722 
00723     uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
00724 
00725     //don't have the return or space key appear as first key of the sequence when they
00726     //were pressed to start editing - catch and them and imitate their effect
00727     if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) {
00728         d->startRecording();
00729         d->modifierKeys = newModifiers;
00730         d->updateShortcutDisplay();
00731         return;
00732     }
00733 
00734     // We get events even if recording isn't active.
00735     if (!d->isRecording)
00736         return QPushButton::keyPressEvent(e);
00737 
00738     e->accept();
00739     d->modifierKeys = newModifiers;
00740 
00741 
00742     switch(keyQt) {
00743     case Qt::Key_AltGr: //or else we get unicode salad
00744         return;
00745     case Qt::Key_Shift:
00746     case Qt::Key_Control:
00747     case Qt::Key_Alt:
00748     case Qt::Key_Meta:
00749     case Qt::Key_Menu: //unused (yes, but why?)
00750         d->controlModifierlessTimout();
00751         d->updateShortcutDisplay();
00752         break;
00753     default:
00754 
00755         if (d->nKey == 0 && !(d->modifierKeys & ~Qt::SHIFT)) {
00756             // It's the first key and no modifier pressed. Check if this is
00757             // allowed
00758             if (!(KKeySequenceWidgetPrivate::isOkWhenModifierless(keyQt)
00759                             || d->allowModifierless)) {
00760                 // No it's not
00761                 return;
00762             }
00763         }
00764 
00765         // We now have a valid key press.
00766         if (keyQt) {
00767             if ((keyQt == Qt::Key_Backtab) && (d->modifierKeys & Qt::SHIFT)) {
00768                 keyQt = Qt::Key_Tab | d->modifierKeys;
00769             }
00770             else if (d->isShiftAsModifierAllowed(keyQt)) {
00771                 keyQt |= d->modifierKeys;
00772             }
00773             else
00774                 keyQt |= (d->modifierKeys & ~Qt::SHIFT);
00775 
00776             if (d->nKey == 0) {
00777                 d->keySequence = QKeySequence(keyQt);
00778             } else {
00779                 d->keySequence =
00780                   KKeySequenceWidgetPrivate::appendToSequence(d->keySequence, keyQt);
00781             }
00782 
00783             d->nKey++;
00784             if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) {
00785                 d->doneRecording();
00786                 return;
00787             }
00788             d->controlModifierlessTimout();
00789             d->updateShortcutDisplay();
00790         }
00791     }
00792 }
00793 
00794 
00795 void KKeySequenceButton::keyReleaseEvent(QKeyEvent *e)
00796 {
00797     if (e->key() == -1) {
00798         // ignore garbage, see keyPressEvent()
00799         return;
00800     }
00801 
00802     if (!d->isRecording)
00803         return QPushButton::keyReleaseEvent(e);
00804 
00805     e->accept();
00806 
00807     uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
00808 
00809     //if a modifier that belongs to the shortcut was released...
00810     if ((newModifiers & d->modifierKeys) < d->modifierKeys) {
00811         d->modifierKeys = newModifiers;
00812         d->controlModifierlessTimout();
00813         d->updateShortcutDisplay();
00814     }
00815 }
00816 
00817 
00818 //static
00819 QKeySequence KKeySequenceWidgetPrivate::appendToSequence(const QKeySequence& seq, int keyQt)
00820 {
00821     switch (seq.count()) {
00822     case 0:
00823         return QKeySequence(keyQt);
00824     case 1:
00825         return QKeySequence(seq[0], keyQt);
00826     case 2:
00827         return QKeySequence(seq[0], seq[1], keyQt);
00828     case 3:
00829         return QKeySequence(seq[0], seq[1], seq[2], keyQt);
00830     default:
00831         return seq;
00832     }
00833 }
00834 
00835 
00836 //static
00837 bool KKeySequenceWidgetPrivate::isOkWhenModifierless(int keyQt)
00838 {
00839     //this whole function is a hack, but especially the first line of code
00840     if (QKeySequence(keyQt).toString().length() == 1)
00841         return false;
00842 
00843     switch (keyQt) {
00844     case Qt::Key_Return:
00845     case Qt::Key_Space:
00846     case Qt::Key_Tab:
00847     case Qt::Key_Backtab: //does this ever happen?
00848     case Qt::Key_Backspace:
00849     case Qt::Key_Delete:
00850         return false;
00851     default:
00852         return true;
00853     }
00854 }
00855 
00856 #include "kkeysequencewidget.moc"
00857 #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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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