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

KDEUI

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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.5
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