• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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         const int hashSize = shortcuts.size();
00174 
00175         QString message = i18ncp("%1 is the number of conflicts (hidden), %2 is the key sequence of the shortcut that is problematic",
00176                                  "The shortcut '%2' conflicts with the following key combination:\n",
00177                                  "The shortcut '%2' conflicts with the following key combinations:\n",
00178                                  hashSize, sequence.toString());
00179         message+=clashingKeys;
00180 
00181         QString title = i18ncp("%1 is the number of shortcuts with which there is a conflict",
00182                                "Conflict with Registered Global Shortcut", "Conflict with Registered Global Shortcuts", hashSize);
00183 
00184         return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18n("Reassign")))
00185                == KMessageBox::Continue;
00186     }
00187 
00188 
00189 //private slot
00190     void doneRecording(bool validate = true);
00191 
00192 //members
00193     KKeySequenceWidget *const q;
00194     QHBoxLayout *layout;
00195     KKeySequenceButton *keyButton;
00196     QToolButton *clearButton;
00197 
00198     QKeySequence keySequence;
00199     QKeySequence oldKeySequence;
00200     QTimer modifierlessTimeout;
00201     bool allowModifierless;
00202     uint nKey;
00203     uint modifierKeys;
00204     bool isRecording;
00205     bool multiKeyShortcutsAllowed;
00206     QString componentName;
00207 
00209     KKeySequenceWidget::ShortcutTypes checkAgainstShortcutTypes;
00210 
00214     QList<QAction*> checkList; // deprecated
00215 
00219     QList<KActionCollection*> checkActionCollections;
00220 
00224     QList<KAction*> stealActions;
00225 
00226     bool stealShortcuts(const QList<KAction *> &actions, const QKeySequence &seq);
00227     void wontStealShortcut(QAction *item, const QKeySequence &seq);
00228 
00229 };
00230 
00231 KKeySequenceWidgetPrivate::KKeySequenceWidgetPrivate(KKeySequenceWidget *q)
00232     : q(q)
00233      ,layout(NULL)
00234      ,keyButton(NULL)
00235      ,clearButton(NULL)
00236      ,allowModifierless(false)
00237      ,nKey(0)
00238      ,modifierKeys(0)
00239      ,isRecording(false)
00240      ,multiKeyShortcutsAllowed(true)
00241      ,componentName()
00242      ,checkAgainstShortcutTypes(KKeySequenceWidget::LocalShortcuts & KKeySequenceWidget::GlobalShortcuts)
00243      ,stealActions()
00244 {}
00245 
00246 
00247 bool KKeySequenceWidgetPrivate::stealShortcuts(
00248         const QList<KAction *> &actions,
00249         const QKeySequence &seq)
00250 {
00251 
00252     const int listSize = actions.size();
00253 
00254     QString title = i18ncp("%1 is the number of conflicts", "Shortcut Conflict", "Shortcut Conflicts", listSize);
00255 
00256     QString conflictingShortcuts;
00257     Q_FOREACH(const KAction *action, actions) {
00258         conflictingShortcuts += i18n("Shortcut '%1' for action '%2'\n",
00259                 action->shortcut().toString(QKeySequence::NativeText),
00260                 KGlobal::locale()->removeAcceleratorMarker(action->text()));
00261     }
00262     QString message = i18ncp("%1 is the number of ambigious shortcut clashes (hidden)",
00263             "The \"%2\" shortcut is ambiguous with the following shortcut.\n"
00264             "Do you want to assign an empty shortcut to this action?\n"
00265             "%3",
00266             "The \"%2\" shortcut is ambiguous with the following shortcuts.\n"
00267             "Do you want to assign an empty shortcut to these actions?\n"
00268             "%3",
00269             listSize,
00270             seq.toString(QKeySequence::NativeText),
00271             conflictingShortcuts);
00272 
00273     if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue)
00274         return false;
00275 
00276     return true;
00277 }
00278 
00279 void KKeySequenceWidgetPrivate::wontStealShortcut(QAction *item, const QKeySequence &seq)
00280 {
00281     QString title( i18n( "Shortcut conflict" ) );
00282     QString msg( i18n( "<qt>The '%1' key combination is already used by the <b>%2</b> action.<br>"
00283             "Please select a different one.</qt>", seq.toString(QKeySequence::NativeText) ,
00284             KGlobal::locale()->removeAcceleratorMarker(item->text()) ) );
00285     KMessageBox::sorry( q, msg );
00286 }
00287 
00288 
00289 KKeySequenceWidget::KKeySequenceWidget(QWidget *parent)
00290  : QWidget(parent),
00291    d(new KKeySequenceWidgetPrivate(this))
00292 {
00293     d->init();
00294     setFocusProxy( d->keyButton );
00295     connect(d->keyButton, SIGNAL(clicked()), this, SLOT(captureKeySequence()));
00296     connect(d->clearButton, SIGNAL(clicked()), this, SLOT(clearKeySequence()));
00297     connect(&d->modifierlessTimeout, SIGNAL(timeout()), this, SLOT(doneRecording()));
00298     //TODO: how to adopt style changes at runtime?
00299     /*QFont modFont = d->clearButton->font();
00300     modFont.setStyleHint(QFont::TypeWriter);
00301     d->clearButton->setFont(modFont);*/
00302     d->updateShortcutDisplay();
00303 }
00304 
00305 
00306 void KKeySequenceWidgetPrivate::init()
00307 {
00308     layout = new QHBoxLayout(q);
00309     layout->setMargin(0);
00310 
00311     keyButton = new KKeySequenceButton(this, q);
00312     keyButton->setFocusPolicy(Qt::StrongFocus);
00313     keyButton->setIcon(KIcon("configure"));
00314     keyButton->setToolTip(i18n("Click on the button, then enter the shortcut like you would in the program.\nExample for Ctrl+a: hold the Ctrl key and press a."));
00315     layout->addWidget(keyButton);
00316 
00317     clearButton = new QToolButton(q);
00318     layout->addWidget(clearButton);
00319 
00320     if (qApp->isLeftToRight())
00321         clearButton->setIcon(KIcon("edit-clear-locationbar-rtl"));
00322     else
00323         clearButton->setIcon(KIcon("edit-clear-locationbar-ltr"));
00324 }
00325 
00326 
00327 KKeySequenceWidget::~KKeySequenceWidget ()
00328 {
00329     delete d;
00330 }
00331 
00332 
00333 KKeySequenceWidget::ShortcutTypes KKeySequenceWidget::checkForConflictsAgainst() const
00334 {
00335     return d->checkAgainstShortcutTypes;
00336 }
00337 
00338 
00339 void KKeySequenceWidget::setComponentName(const QString &componentName)
00340 {
00341     d->componentName = componentName;
00342 }
00343 
00344 bool KKeySequenceWidget::multiKeyShortcutsAllowed() const
00345 {
00346     return d->multiKeyShortcutsAllowed;
00347 }
00348 
00349 
00350 void KKeySequenceWidget::setMultiKeyShortcutsAllowed(bool allowed)
00351 {
00352     d->multiKeyShortcutsAllowed = allowed;
00353 }
00354 
00355 
00356 void KKeySequenceWidget::setCheckForConflictsAgainst(ShortcutTypes types)
00357 {
00358     d->checkAgainstShortcutTypes = types;
00359 }
00360 
00361 void KKeySequenceWidget::setModifierlessAllowed(bool allow)
00362 {
00363     d->allowModifierless = allow;
00364 }
00365 
00366 
00367 bool KKeySequenceWidget::isKeySequenceAvailable(const QKeySequence &keySequence) const
00368 {
00369     if (keySequence.isEmpty())
00370         return true;
00371     return ! ( d->conflictWithLocalShortcuts(keySequence)
00372                || d->conflictWithGlobalShortcuts(keySequence)
00373                || d->conflictWithStandardShortcuts(keySequence));
00374 }
00375 
00376 
00377 bool KKeySequenceWidget::isModifierlessAllowed()
00378 {
00379     return d->allowModifierless;
00380 }
00381 
00382 
00383 void KKeySequenceWidget::setClearButtonShown(bool show)
00384 {
00385     d->clearButton->setVisible(show);
00386 }
00387 
00388 #ifndef KDE_NO_DEPRECATED
00389 void KKeySequenceWidget::setCheckActionList(const QList<QAction*> &checkList) // deprecated
00390 {
00391     d->checkList = checkList;
00392     Q_ASSERT(d->checkActionCollections.isEmpty()); // don't call this method if you call setCheckActionCollections!
00393 }
00394 #endif
00395 
00396 void KKeySequenceWidget::setCheckActionCollections(const QList<KActionCollection *>& actionCollections)
00397 {
00398     d->checkActionCollections = actionCollections;
00399 }
00400 
00401 //slot
00402 void KKeySequenceWidget::captureKeySequence()
00403 {
00404     d->startRecording();
00405 }
00406 
00407 
00408 QKeySequence KKeySequenceWidget::keySequence() const
00409 {
00410     return d->keySequence;
00411 }
00412 
00413 
00414 //slot
00415 void KKeySequenceWidget::setKeySequence(const QKeySequence &seq, Validation validate)
00416 {
00417     // oldKeySequence holds the key sequence before recording started, if setKeySequence()
00418     // is called while not recording then set oldKeySequence to the existing sequence so
00419     // that the keySequenceChanged() signal is emitted if the new and previous key
00420     // sequences are different
00421     if (!d->isRecording)
00422         d->oldKeySequence = d->keySequence;
00423 
00424     d->keySequence = seq;
00425     d->doneRecording(validate == Validate);
00426 }
00427 
00428 
00429 //slot
00430 void KKeySequenceWidget::clearKeySequence()
00431 {
00432     setKeySequence(QKeySequence());
00433 }
00434 
00435 //slot
00436 void KKeySequenceWidget::applyStealShortcut()
00437 {
00438     QSet<KActionCollection *> changedCollections;
00439 
00440     Q_FOREACH (KAction *stealAction, d->stealActions) {
00441 
00442         // Stealing a shortcut means setting it to an empty one.
00443         stealAction->setShortcut(KShortcut(), KAction::ActiveShortcut);
00444 
00445         // The following code will find the action we are about to
00446         // steal from and save it's actioncollection.
00447         KActionCollection* parentCollection = 0;
00448         foreach(KActionCollection* collection, d->checkActionCollections) {
00449             if (collection->actions().contains(stealAction)) {
00450                 parentCollection = collection;
00451                 break;
00452             }
00453         }
00454 
00455         // Remember the changed collection
00456         if (parentCollection) {
00457             changedCollections.insert(parentCollection);
00458         }
00459     }
00460 
00461     Q_FOREACH (KActionCollection *col, changedCollections) {
00462             col->writeSettings();
00463     }
00464 
00465     d->stealActions.clear();
00466 }
00467 
00468 void KKeySequenceButton::setText(const QString &text)
00469 {
00470     QPushButton::setText(text);
00471     //setFixedSize( sizeHint().width()+12, sizeHint().height()+8 );
00472 }
00473 
00474 
00475 void KKeySequenceWidgetPrivate::startRecording()
00476 {
00477     nKey = 0;
00478     modifierKeys = 0;
00479     oldKeySequence = keySequence;
00480     keySequence = QKeySequence();
00481     isRecording = true;
00482     keyButton->grabKeyboard();
00483 
00484     if (!QWidget::keyboardGrabber()) {
00485         kWarning() << "Failed to grab the keyboard! Most likely qt's nograb option is active";
00486     }
00487 
00488     keyButton->setDown(true);
00489     updateShortcutDisplay();
00490 }
00491 
00492 
00493 void KKeySequenceWidgetPrivate::doneRecording(bool validate)
00494 {
00495     modifierlessTimeout.stop();
00496     isRecording = false;
00497     keyButton->releaseKeyboard();
00498     keyButton->setDown(false);
00499     stealActions.clear();
00500 
00501     if (keySequence==oldKeySequence) {
00502         // The sequence hasn't changed
00503         updateShortcutDisplay();
00504         return;
00505     }
00506 
00507     if (validate && !q->isKeySequenceAvailable(keySequence)) {
00508         // The sequence had conflicts and the user said no to stealing it
00509         keySequence = oldKeySequence;
00510     } else {
00511         emit q->keySequenceChanged(keySequence);
00512     }
00513 
00514     updateShortcutDisplay();
00515 }
00516 
00517 
00518 bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(const QKeySequence &keySequence)
00519 {
00520 #ifdef Q_WS_WIN
00521     //on windows F12 is reserved by the debugger at all times, so we can't use it for a global shortcut
00522     if (KKeySequenceWidget::GlobalShortcuts && keySequence.toString().contains("F12")) {
00523         QString title = i18n("Reserved Shortcut");
00524         QString message = i18n("The F12 key is reserved on Windows, so cannot be used for a global shortcut.\n"
00525                                "Please choose another one.");
00526 
00527         KMessageBox::sorry(q, message, title);
00528         return false;
00529     }
00530 #endif
00531 
00532     if (!(checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts)) {
00533         return false;
00534     }
00535 
00536     // Global shortcuts are on key+modifier shortcuts. They can clash with
00537     // each of the keys of a multi key shortcut.
00538     QHash<QKeySequence, QList<KGlobalShortcutInfo> > others;
00539     for (uint i=0; i<keySequence.count(); ++i) {
00540         QKeySequence tmp(keySequence[i]);
00541 
00542         if (!KGlobalAccel::isGlobalShortcutAvailable(tmp, componentName)) {
00543                 others.insert(tmp, KGlobalAccel::getGlobalShortcutsByKey(tmp));
00544         }
00545     }
00546 
00547     if (!others.isEmpty()
00548             && !promptStealShortcutSystemwide(q, others, keySequence)) {
00549         return true;
00550     }
00551 
00552     // The user approved stealing the shortcut. We have to steal
00553     // it immediately because KAction::setGlobalShortcut() refuses
00554     // to set a global shortcut that is already used. There is no
00555     // error it just silently fails. So be nice because this is
00556     // most likely the first action that is done in the slot
00557     // listening to keySequenceChanged().
00558     for (uint i=0; i<keySequence.count(); ++i) {
00559         KGlobalAccel::stealShortcutSystemwide(keySequence[i]);
00560     }
00561     return false;
00562 }
00563 
00564 
00565 bool KKeySequenceWidgetPrivate::conflictWithLocalShortcuts(const QKeySequence &keySequence)
00566 {
00567     if (!(checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts)) {
00568         return false;
00569     }
00570 
00571     // We have actions both in the deprecated checkList and the
00572     // checkActionCollections list. Add all the actions to a single list to
00573     // be able to process them in a single loop below.
00574     // Note that this can't be done in setCheckActionCollections(), because we
00575     // keep pointers to the action collections, and between the call to
00576     // setCheckActionCollections() and this function some actions might already be
00577     // removed from the collection again.
00578     QList<QAction*> allActions;
00579     allActions += checkList;
00580     foreach(KActionCollection* collection, checkActionCollections) {
00581         allActions += collection->actions();
00582     }
00583 
00584     // Because of multikey shortcuts we can have clashes with many shortcuts.
00585     //
00586     // Example 1:
00587     //
00588     // Application currently uses 'CTRL-X,a', 'CTRL-X,f' and 'CTRL-X,CTRL-F'
00589     // and the user wants to use 'CTRL-X'. 'CTRL-X' will only trigger as
00590     // 'activatedAmbiguously()' for obvious reasons.
00591     //
00592     // Example 2:
00593     //
00594     // Application currently uses 'CTRL-X'. User wants to use 'CTRL-X,CTRL-F'.
00595     // This will shadow 'CTRL-X' for the same reason as above.
00596     //
00597     // Example 3:
00598     //
00599     // Some weird combination of Example 1 and 2 with three shortcuts using
00600     // 1/2/3 key shortcuts. I think you can imagine.
00601     QList<KAction*> conflictingActions;
00602 
00603     //find conflicting shortcuts with existing actions
00604     foreach(QAction * qaction , allActions ) {
00605         KAction *kaction=qobject_cast<KAction*>(qaction);
00606         if(kaction) {
00607             if(kaction->shortcut().conflictsWith(keySequence)) {
00608                 // A conflict with a KAction. If that action is configurable
00609                 // ask the user what to do. If not reject this keySequence.
00610                 if(kaction->isShortcutConfigurable ()) {
00611                     conflictingActions.append(kaction);
00612                 } else {
00613                     wontStealShortcut(kaction, keySequence);
00614                     return true;
00615                 }
00616             }
00617         } else {
00618             if(qaction->shortcut() == keySequence) {
00619                 // A conflict with a QAction. Don't know why :-( but we won't
00620                 // steal from that kind of actions.
00621                 wontStealShortcut(qaction, keySequence);
00622                 return true;
00623             }
00624         }
00625     }
00626 
00627     if (conflictingActions.isEmpty()) {
00628         // No conflicting shortcuts found.
00629         return false;
00630     }
00631 
00632     if(stealShortcuts(conflictingActions, keySequence)) {
00633         stealActions = conflictingActions;
00634         // Announce that the user
00635         // agreed
00636         Q_FOREACH (KAction *stealAction, stealActions) {
00637             emit q->stealShortcut(
00638                 keySequence,
00639                 stealAction);
00640         }
00641         return false;
00642     } else {
00643         return true;
00644     }
00645 }
00646 
00647 
00648 bool KKeySequenceWidgetPrivate::conflictWithStandardShortcuts(const QKeySequence &keySequence)
00649 {
00650     if (!(checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts)) {
00651         return false;
00652     }
00653 
00654     KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence);
00655     if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) {
00656         return true;
00657     }
00658     return false;
00659 }
00660 
00661 
00662 bool KKeySequenceWidgetPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq)
00663 {
00664     QString title = i18n("Conflict with Standard Application Shortcut");
00665     QString message = i18n("The '%1' key combination is also used for the standard action "
00666                            "\"%2\" that some applications use.\n"
00667                            "Do you really want to use it as a global shortcut as well?",
00668                            seq.toString(QKeySequence::NativeText), KStandardShortcut::label(std));
00669 
00670     if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) {
00671         return false;
00672     }
00673     return true;
00674 }
00675 
00676 
00677 void KKeySequenceWidgetPrivate::updateShortcutDisplay()
00678 {
00679     //empty string if no non-modifier was pressed
00680     QString s = keySequence.toString(QKeySequence::NativeText);
00681     s.replace('&', QLatin1String("&&"));
00682 
00683     if (isRecording) {
00684         if (modifierKeys) {
00685             if (!s.isEmpty()) s.append(",");
00686             if (modifierKeys & Qt::META)  s += KKeyServer::modToStringUser(Qt::META) + '+';
00687 #if defined(Q_WS_MAC)
00688             if (modifierKeys & Qt::ALT)   s += KKeyServer::modToStringUser(Qt::ALT) + '+';
00689             if (modifierKeys & Qt::CTRL)  s += KKeyServer::modToStringUser(Qt::CTRL) + '+';
00690 #elif defined(Q_WS_X11)
00691             if (modifierKeys & Qt::CTRL)  s += KKeyServer::modToStringUser(Qt::CTRL) + '+';
00692             if (modifierKeys & Qt::ALT)   s += KKeyServer::modToStringUser(Qt::ALT) + '+';
00693 #endif
00694             if (modifierKeys & Qt::SHIFT) s += KKeyServer::modToStringUser(Qt::SHIFT) + '+';
00695 
00696         } else if (nKey == 0) {
00697             s = i18nc("What the user inputs now will be taken as the new shortcut", "Input");
00698         }
00699         //make it clear that input is still going on
00700         s.append(" ...");
00701     }
00702 
00703     if (s.isEmpty()) {
00704         s = i18nc("No shortcut defined", "None");
00705     }
00706 
00707     s.prepend(' ');
00708     s.append(' ');
00709     keyButton->setText(s);
00710 }
00711 
00712 
00713 KKeySequenceButton::~KKeySequenceButton()
00714 {
00715 }
00716 
00717 
00718 //prevent Qt from special casing Tab and Backtab
00719 bool KKeySequenceButton::event (QEvent* e)
00720 {
00721     if (d->isRecording && e->type() == QEvent::KeyPress) {
00722         keyPressEvent(static_cast<QKeyEvent *>(e));
00723         return true;
00724     }
00725 
00726     // The shortcut 'alt+c' ( or any other dialog local action shortcut )
00727     // ended the recording and triggered the action associated with the
00728     // action. In case of 'alt+c' ending the dialog.  It seems that those
00729     // ShortcutOverride events get sent even if grabKeyboard() is active.
00730     if (d->isRecording && e->type() == QEvent::ShortcutOverride) {
00731         e->accept();
00732         return true;
00733     }
00734 
00735     return QPushButton::event(e);
00736 }
00737 
00738 
00739 void KKeySequenceButton::keyPressEvent(QKeyEvent *e)
00740 {
00741     int keyQt = e->key();
00742     if (keyQt == -1) {
00743         // Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key.
00744         // We cannot do anything useful with those (several keys have -1, indistinguishable)
00745         // and QKeySequence.toString() will also yield a garbage string.
00746         KMessageBox::sorry(this,
00747                 i18n("The key you just pressed is not supported by Qt."),
00748                 i18n("Unsupported Key"));
00749         return d->cancelRecording();
00750     }
00751 
00752     uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
00753 
00754     //don't have the return or space key appear as first key of the sequence when they
00755     //were pressed to start editing - catch and them and imitate their effect
00756     if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) {
00757         d->startRecording();
00758         d->modifierKeys = newModifiers;
00759         d->updateShortcutDisplay();
00760         return;
00761     }
00762 
00763     // We get events even if recording isn't active.
00764     if (!d->isRecording)
00765         return QPushButton::keyPressEvent(e);
00766 
00767     e->accept();
00768     d->modifierKeys = newModifiers;
00769 
00770 
00771     switch(keyQt) {
00772     case Qt::Key_AltGr: //or else we get unicode salad
00773         return;
00774     case Qt::Key_Shift:
00775     case Qt::Key_Control:
00776     case Qt::Key_Alt:
00777     case Qt::Key_Meta:
00778     case Qt::Key_Menu: //unused (yes, but why?)
00779         d->controlModifierlessTimout();
00780         d->updateShortcutDisplay();
00781         break;
00782     default:
00783 
00784         if (d->nKey == 0 && !(d->modifierKeys & ~Qt::SHIFT)) {
00785             // It's the first key and no modifier pressed. Check if this is
00786             // allowed
00787             if (!(KKeySequenceWidgetPrivate::isOkWhenModifierless(keyQt)
00788                             || d->allowModifierless)) {
00789                 // No it's not
00790                 return;
00791             }
00792         }
00793 
00794         // We now have a valid key press.
00795         if (keyQt) {
00796             if ((keyQt == Qt::Key_Backtab) && (d->modifierKeys & Qt::SHIFT)) {
00797                 keyQt = Qt::Key_Tab | d->modifierKeys;
00798             }
00799             else if (d->isShiftAsModifierAllowed(keyQt)) {
00800                 keyQt |= d->modifierKeys;
00801             }
00802             else
00803                 keyQt |= (d->modifierKeys & ~Qt::SHIFT);
00804 
00805             if (d->nKey == 0) {
00806                 d->keySequence = QKeySequence(keyQt);
00807             } else {
00808                 d->keySequence =
00809                   KKeySequenceWidgetPrivate::appendToSequence(d->keySequence, keyQt);
00810             }
00811 
00812             d->nKey++;
00813             if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) {
00814                 d->doneRecording();
00815                 return;
00816             }
00817             d->controlModifierlessTimout();
00818             d->updateShortcutDisplay();
00819         }
00820     }
00821 }
00822 
00823 
00824 void KKeySequenceButton::keyReleaseEvent(QKeyEvent *e)
00825 {
00826     if (e->key() == -1) {
00827         // ignore garbage, see keyPressEvent()
00828         return;
00829     }
00830 
00831     if (!d->isRecording)
00832         return QPushButton::keyReleaseEvent(e);
00833 
00834     e->accept();
00835 
00836     uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
00837 
00838     //if a modifier that belongs to the shortcut was released...
00839     if ((newModifiers & d->modifierKeys) < d->modifierKeys) {
00840         d->modifierKeys = newModifiers;
00841         d->controlModifierlessTimout();
00842         d->updateShortcutDisplay();
00843     }
00844 }
00845 
00846 
00847 //static
00848 QKeySequence KKeySequenceWidgetPrivate::appendToSequence(const QKeySequence& seq, int keyQt)
00849 {
00850     switch (seq.count()) {
00851     case 0:
00852         return QKeySequence(keyQt);
00853     case 1:
00854         return QKeySequence(seq[0], keyQt);
00855     case 2:
00856         return QKeySequence(seq[0], seq[1], keyQt);
00857     case 3:
00858         return QKeySequence(seq[0], seq[1], seq[2], keyQt);
00859     default:
00860         return seq;
00861     }
00862 }
00863 
00864 
00865 //static
00866 bool KKeySequenceWidgetPrivate::isOkWhenModifierless(int keyQt)
00867 {
00868     //this whole function is a hack, but especially the first line of code
00869     if (QKeySequence(keyQt).toString().length() == 1)
00870         return false;
00871 
00872     switch (keyQt) {
00873     case Qt::Key_Return:
00874     case Qt::Key_Space:
00875     case Qt::Key_Tab:
00876     case Qt::Key_Backtab: //does this ever happen?
00877     case Qt::Key_Backspace:
00878     case Qt::Key_Delete:
00879         return false;
00880     default:
00881         return true;
00882     }
00883 }
00884 
00885 #include "kkeysequencewidget.moc"
00886 #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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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