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"
KDE 4.7 API Reference