00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "kkeysequencewidget.h"
00023 #include "kkeysequencewidget_p.h"
00024
00025 #include "kkeyserver.h"
00026 #include "kiconloader.h"
00027
00028 #include <QKeyEvent>
00029 #include <QTimer>
00030 #include <QHBoxLayout>
00031 #include <QToolButton>
00032 #include <QApplication>
00033
00034 #include <kdebug.h>
00035 #include <kglobalaccel.h>
00036 #include <kicon.h>
00037 #include <klocale.h>
00038 #include <kmessagebox.h>
00039 #include <kshortcut.h>
00040 #include <kaction.h>
00041 #include <kactioncollection.h>
00042
00043 #if 0
00044 #ifdef Q_WS_X11
00045 #define XK_XKB_KEYS
00046 #define XK_MISCELLANY
00047 #include <X11/Xlib.h>
00048 #include <X11/keysymdef.h>
00049
00050 #ifdef KeyPress
00051 const int XFocusOut = FocusOut;
00052 const int XFocusIn = FocusIn;
00053 const int XKeyPress = KeyPress;
00054 const int XKeyRelease = KeyRelease;
00055 #undef KeyRelease
00056 #undef KeyPress
00057 #undef FocusOut
00058 #undef FocusIn
00059 #endif // KeyPress
00060 #endif // Q_WS_X11*/
00061
00062
00063
00064
00065 #endif
00066
00067
00068
00069
00070
00071
00072
00073
00074 class KKeySequenceWidgetPrivate
00075 {
00076 public:
00077 KKeySequenceWidgetPrivate(KKeySequenceWidget *q): q(q) {}
00078
00079 void init();
00080
00081 static QKeySequence appendToSequence(const QKeySequence& seq, int keyQt);
00082 static bool isOkWhenModifierless(int keyQt);
00083
00084 void updateShortcutDisplay();
00085 void startRecording();
00086
00087 void doneRecording(bool validate = true);
00088
00089
00090 KKeySequenceWidget *const q;
00091 QHBoxLayout *layout;
00092 KKeySequenceButton *keyButton;
00093 QToolButton *clearButton;
00094
00095 QKeySequence keySequence;
00096 QKeySequence oldKeySequence;
00097 QTimer modifierlessTimeout;
00098 bool allowModifierless;
00099 uint nKey;
00100 uint modifierKeys;
00101 bool isRecording;
00102
00106 QList<QAction*> checkList;
00110 QList<KActionCollection*> checkActionCollections;
00111
00115 KAction* stealAction;
00116
00117 bool stealShortcut(QAction *item, const QKeySequence &seq);
00118 void wontStealShortcut(QAction *item, const QKeySequence &seq);
00119
00120 };
00121
00122 bool KKeySequenceWidgetPrivate::stealShortcut(QAction *item, const QKeySequence &seq)
00123 {
00124 QString title = i18n("Key Conflict");
00125 QString message = i18n("The '%1' key combination has already been allocated to the \"%2\" action.\n"
00126 "Do you want to reassign it from that action to the current one?", seq.toString(QKeySequence::NativeText), item->text().remove('&'));
00127
00128 if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue)
00129 return false;
00130
00131 return true;
00132 }
00133
00134 void KKeySequenceWidgetPrivate::wontStealShortcut(QAction *item, const QKeySequence &seq)
00135 {
00136 QString title( i18n( "Shortcut conflict" ) );
00137 QString msg( i18n( "<qt>The '%1' key combination is already used by the <b>%2</b> action.<br>"
00138 "Please select a different one.</qt>", seq.toString(QKeySequence::NativeText) , item->text().remove('&') ) );
00139 KMessageBox::sorry( q, msg );
00140 }
00141
00142
00143
00144 KKeySequenceWidget::KKeySequenceWidget(QWidget *parent)
00145 : QWidget(parent),
00146 d(new KKeySequenceWidgetPrivate(this))
00147 {
00148 d->init();
00149 connect(d->keyButton, SIGNAL(clicked()), this, SLOT(captureKeySequence()));
00150 connect(d->clearButton, SIGNAL(clicked()), this, SLOT(clearKeySequence()));
00151 connect(&d->modifierlessTimeout, SIGNAL(timeout()), this, SLOT(doneRecording()));
00152
00153
00154
00155
00156 d->updateShortcutDisplay();
00157 }
00158
00159
00160 void KKeySequenceWidgetPrivate::init()
00161 {
00162 stealAction = 0;
00163 isRecording = false;
00164 allowModifierless = false;
00165
00166 layout = new QHBoxLayout(q);
00167
00168 keyButton = new KKeySequenceButton(this, q);
00169 keyButton->setFocusPolicy(Qt::StrongFocus);
00170 keyButton->setIcon(KIcon("configure"));
00171 layout->addWidget(keyButton);
00172
00173 clearButton = new QToolButton(q);
00174 layout->addWidget(clearButton);
00175
00176 if (qApp->isLeftToRight())
00177 clearButton->setIcon(KIcon("edit-clear-locationbar-rtl"));
00178 else
00179 clearButton->setIcon(KIcon("edit-clear-locationbar-ltr"));
00180 }
00181
00182
00183 KKeySequenceWidget::~KKeySequenceWidget ()
00184 {
00185 delete d;
00186 }
00187
00188
00189 void KKeySequenceWidget::setModifierlessAllowed(bool allow)
00190 {
00191 d->allowModifierless = allow;
00192 }
00193
00194
00195 bool KKeySequenceWidget::isModifierlessAllowed()
00196 {
00197 return d->allowModifierless;
00198 }
00199
00200
00201 void KKeySequenceWidget::setClearButtonShown(bool show)
00202 {
00203 d->clearButton->setVisible(show);
00204 }
00205
00206 void KKeySequenceWidget::setCheckActionList(const QList<QAction*> &checkList)
00207 {
00208 d->checkList = checkList;
00209 Q_ASSERT(d->checkActionCollections.isEmpty());
00210 }
00211
00212 void KKeySequenceWidget::setCheckActionCollections(const QList<KActionCollection *>& actionCollections)
00213 {
00214 d->checkActionCollections = actionCollections;
00215 }
00216
00217
00218 void KKeySequenceWidget::captureKeySequence()
00219 {
00220 d->startRecording();
00221 }
00222
00223
00224 QKeySequence KKeySequenceWidget::keySequence() const
00225 {
00226 return d->keySequence;
00227 }
00228
00229
00230
00231 void KKeySequenceWidget::setKeySequence(const QKeySequence &seq, Validation validate)
00232 {
00233
00234
00235
00236
00237 if (!d->isRecording)
00238 d->oldKeySequence = d->keySequence;
00239
00240 d->keySequence = seq;
00241 d->doneRecording(validate == Validate);
00242 }
00243
00244
00245
00246 void KKeySequenceWidget::clearKeySequence()
00247 {
00248 setKeySequence(QKeySequence());
00249 }
00250
00251
00252 void KKeySequenceWidget::applyStealShortcut()
00253 {
00254 if(d->stealAction) {
00255 KShortcut cut=d->stealAction->shortcut();
00256 cut.remove(d->keySequence);
00257 d->stealAction->setShortcut(cut, KAction::ActiveShortcut);
00258
00259 KActionCollection* parentCollection = 0;
00260 foreach(KActionCollection* collection, d->checkActionCollections) {
00261 if (collection->actions().contains(d->stealAction)) {
00262 parentCollection = collection;
00263 break;
00264 }
00265 }
00266 if (parentCollection)
00267 parentCollection->writeSettings();
00268 }
00269 KGlobalAccel::stealShortcutSystemwide(d->keySequence);
00270 }
00271
00272 void KKeySequenceButton::setText(const QString &text)
00273 {
00274 QPushButton::setText(text);
00275
00276 }
00277
00278
00279 void KKeySequenceWidgetPrivate::startRecording()
00280 {
00281 nKey = 0;
00282 modifierKeys = 0;
00283 oldKeySequence = keySequence;
00284 keySequence = QKeySequence();
00285 isRecording = true;
00286 keyButton->grabKeyboard();
00287
00288 if (!QWidget::keyboardGrabber()) {
00289 kDebug() << "Failed to grab the keyboard! Most likely qt's nograb option is active";
00290 }
00291
00292 keyButton->setDown(true);
00293 updateShortcutDisplay();
00294 }
00295
00296
00297 void KKeySequenceWidgetPrivate::doneRecording(bool validate)
00298 {
00299 KAction *steal=0;
00300 modifierlessTimeout.stop();
00301 isRecording = false;
00302 keyButton->releaseKeyboard();
00303 keyButton->setDown(false);
00304
00305
00306
00307
00308
00309
00310
00311
00312 QList<QAction*> allActions;
00313 allActions += checkList;
00314 foreach(KActionCollection* collection, checkActionCollections) {
00315 allActions += collection->actions();
00316 }
00317
00318
00319
00320 if (keySequence != oldKeySequence && validate && !allActions.isEmpty()) {
00321
00322 foreach(QAction * qaction , allActions )
00323 {
00324 KAction *kaction=qobject_cast<KAction*>(qaction);
00325 if(kaction) {
00326 if(kaction->shortcut().contains(keySequence)) {
00327 if(kaction->isShortcutConfigurable ()) {
00328 if(!stealShortcut(kaction, keySequence))
00329 goto reset;
00330 steal = kaction;
00331 break;
00332 } else {
00333 wontStealShortcut(kaction, keySequence);
00334 goto reset;
00335 }
00336 }
00337 } else {
00338 if(qaction->shortcut() == keySequence) {
00339 wontStealShortcut(qaction, keySequence);
00340 goto reset;
00341 }
00342 }
00343 }
00344
00345 QStringList conflicting = KGlobalAccel::findActionNameSystemwide(keySequence);
00346 if (!conflicting.isEmpty()) {
00347 if (!KGlobalAccel::promptStealShortcutSystemwide(0, conflicting, keySequence))
00348 goto reset;
00349 }
00350 }
00351 stealAction = steal;
00352 updateShortcutDisplay();
00353 if (keySequence != oldKeySequence)
00354 emit q->keySequenceChanged(keySequence);
00355 return;
00356
00357 reset:
00358 keySequence = oldKeySequence;
00359 updateShortcutDisplay();
00360 return;
00361 }
00362
00363
00364 void KKeySequenceWidgetPrivate::updateShortcutDisplay()
00365 {
00366
00367 QString s = keySequence.toString(QKeySequence::NativeText);
00368 s.replace('&', QLatin1String("&&"));
00369
00370 if (isRecording) {
00371
00372 if (nKey == 0) {
00373 if (modifierKeys) {
00374 if (modifierKeys & Qt::META) s += KKeyServer::modToStringUser(Qt::META) + '+';
00375 #if defined(Q_WS_MAC)
00376 if (modifierKeys & Qt::ALT) s += KKeyServer::modToStringUser(Qt::ALT) + '+';
00377 if (modifierKeys & Qt::CTRL) s += KKeyServer::modToStringUser(Qt::CTRL) + '+';
00378 #elif defined(Q_WS_X11)
00379 if (modifierKeys & Qt::CTRL) s += KKeyServer::modToStringUser(Qt::CTRL) + '+';
00380 if (modifierKeys & Qt::ALT) s += KKeyServer::modToStringUser(Qt::ALT) + '+';
00381 #endif
00382 if (modifierKeys & Qt::SHIFT) s += KKeyServer::modToStringUser(Qt::SHIFT) + '+';
00383 } else
00384 s = i18nc("What the user inputs now will be taken as the new shortcut", "Input");
00385 }
00386
00387 s.append(" ...");
00388 }
00389
00390 if (s.isEmpty())
00391 s = i18nc("No shortcut defined", "None");
00392
00393 s.prepend(' ');
00394 s.append(' ');
00395 keyButton->setText(s);
00396 }
00397
00398
00399 KKeySequenceButton::~KKeySequenceButton()
00400 {
00401 }
00402
00403
00404
00405 bool KKeySequenceButton::event (QEvent* e)
00406 {
00407 if (d->isRecording && e->type() == QEvent::KeyPress) {
00408 keyPressEvent(static_cast<QKeyEvent *>(e));
00409 return true;
00410 }
00411
00412
00413
00414
00415
00416
00417 if (d->isRecording && e->type() == QEvent::ShortcutOverride) {
00418 e->accept();
00419 return true;
00420 }
00421
00422 return QPushButton::event(e);
00423 }
00424
00425
00426 void KKeySequenceButton::keyPressEvent(QKeyEvent *e)
00427 {
00428 int keyQt = e->key();
00429 if (keyQt == -1) {
00430
00431
00432
00433 return;
00434 }
00435 uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
00436
00437
00438
00439 if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) {
00440 d->startRecording();
00441 d->modifierKeys = newModifiers;
00442 d->updateShortcutDisplay();
00443 return;
00444 }
00445
00446 if (!d->isRecording)
00447 return QPushButton::keyPressEvent(e);
00448
00449 e->accept();
00450
00451 if (d->nKey == 0)
00452 d->modifierKeys = newModifiers;
00453
00454 switch(keyQt) {
00455 case Qt::Key_AltGr:
00456 return;
00457 case Qt::Key_Shift:
00458 case Qt::Key_Control:
00459 case Qt::Key_Alt:
00460 case Qt::Key_Meta:
00461 case Qt::Key_Menu:
00462
00463
00464 if(d->nKey == 0)
00465 d->updateShortcutDisplay();
00466 break;
00467 default:
00468
00469 if (!(d->modifierKeys & ~Qt::SHIFT)) {
00470 if (!KKeySequenceWidgetPrivate::isOkWhenModifierless(keyQt) && !d->allowModifierless) {
00471 return;
00472 } else {
00473 d->modifierlessTimeout.start(600);
00474 }
00475 }
00476
00477 if (keyQt) {
00478 if (d->nKey == 0) {
00479 d->keySequence =
00480 KKeySequenceWidgetPrivate::appendToSequence(d->keySequence,
00481 keyQt | d->modifierKeys);
00482 } else {
00483 d->keySequence =
00484 KKeySequenceWidgetPrivate::appendToSequence(d->keySequence, keyQt);
00485 }
00486
00487 d->nKey++;
00488 if (d->nKey >= 4) {
00489 d->doneRecording();
00490 return;
00491 }
00492 d->updateShortcutDisplay();
00493 }
00494 }
00495 }
00496
00497
00498 void KKeySequenceButton::keyReleaseEvent(QKeyEvent *e)
00499 {
00500 if (e->key() == -1) {
00501
00502 return;
00503 }
00504
00505 if (!d->isRecording)
00506 return QPushButton::keyReleaseEvent(e);
00507
00508 e->accept();
00509
00510 uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
00511
00512
00513 if ((newModifiers & d->modifierKeys) < d->modifierKeys) {
00514 if (d->nKey == 0) {
00515 d->modifierKeys = newModifiers;
00516 d->updateShortcutDisplay();
00517 } else
00518 d->doneRecording();
00519 }
00520 }
00521
00522
00523
00524 QKeySequence KKeySequenceWidgetPrivate::appendToSequence(const QKeySequence& seq, int keyQt)
00525 {
00526 switch (seq.count()) {
00527 case 0:
00528 return QKeySequence(keyQt);
00529 case 1:
00530 return QKeySequence(seq[0], keyQt);
00531 case 2:
00532 return QKeySequence(seq[0], seq[1], keyQt);
00533 case 3:
00534 return QKeySequence(seq[0], seq[1], seq[2], keyQt);
00535 default:
00536 return seq;
00537 }
00538 }
00539
00540
00541
00542 bool KKeySequenceWidgetPrivate::isOkWhenModifierless(int keyQt)
00543 {
00544
00545 if (QKeySequence(keyQt).toString().length() == 1)
00546 return false;
00547
00548 switch (keyQt) {
00549 case Qt::Key_Return:
00550 case Qt::Key_Space:
00551 case Qt::Key_Tab:
00552 case Qt::Key_Backtab:
00553 case Qt::Key_Backspace:
00554 case Qt::Key_Delete:
00555 return false;
00556 default:
00557 return true;
00558 }
00559 }
00560
00561 #include "kkeysequencewidget.moc"
00562 #include "kkeysequencewidget_p.moc"