• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

kkeysequencewidget.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002     Copyright (C) 1998 Mark Donohoe <donohoe@kde.org>
00003     Copyright (C) 2001 Ellis Whitehead <ellis@kde.org>
00004     Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019     Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "kkeysequencewidget.h"
00023 #include "kkeysequencewidget_p.h"
00024 
00025 #include "kkeyserver.h"
00026 #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>   // For x11Event()
00048 #include <X11/keysymdef.h> // For XK_...
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 //static const char* psTemp[] = {
00063 //  I18N_NOOP("Primary"), I18N_NOOP("Alternate"), I18N_NOOP("Multi-Key")
00064 //};
00065 #endif
00066 
00067 /***********************************************************************/
00068 /* KKeySequenceWidget                                                  */
00069 /*                                                                     */
00070 /* Initially added by Mark Donohoe <donohoe@kde.org>                   */
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 //private slot
00087     void doneRecording(bool validate = true);
00088 
00089 //members
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; // deprecated
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     //TODO: how to adopt style changes at runtime?
00153     /*QFont modFont = d->clearButton->font();
00154     modFont.setStyleHint(QFont::TypeWriter);
00155     d->clearButton->setFont(modFont);*/
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) // deprecated
00207 {
00208     d->checkList = checkList;
00209     Q_ASSERT(d->checkActionCollections.isEmpty()); // don't call this method if you call setCheckActionCollections!
00210 }
00211 
00212 void KKeySequenceWidget::setCheckActionCollections(const QList<KActionCollection *>& actionCollections)
00213 {
00214     d->checkActionCollections = actionCollections;
00215 }
00216 
00217 //slot
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 //slot
00231 void KKeySequenceWidget::setKeySequence(const QKeySequence &seq, Validation validate)
00232 {
00233     // oldKeySequence holds the key sequence before recording started, if setKeySequence()
00234     // is called while not recording then set oldKeySequence to the existing sequence so
00235     // that the keySequenceChanged() signal is emitted if the new and previous key
00236     // sequences are different
00237     if (!d->isRecording)
00238         d->oldKeySequence = d->keySequence;
00239 
00240     d->keySequence = seq;
00241     d->doneRecording(validate == Validate);
00242 }
00243 
00244 
00245 //slot
00246 void KKeySequenceWidget::clearKeySequence()
00247 {
00248     setKeySequence(QKeySequence());
00249 }
00250 
00251 //slot
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                 // Find the collection where stealAction belongs
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     //setFixedSize( sizeHint().width()+12, sizeHint().height()+8 );
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     // We have actions both in the deprecated checkList and the
00306     // checkActionCollections list. Add all the actions to a single list to
00307     // be able to process them in a single loop below.
00308     // Note that this can't be done in setCheckActionCollections(), because we
00309     // keep pointers to the action collections, and between the call to
00310     // setCheckActionCollections() and this function some actions might already be
00311     // removed from the collection again.
00312     QList<QAction*> allActions;
00313     allActions += checkList;
00314     foreach(KActionCollection* collection, checkActionCollections) {
00315         allActions += collection->actions();
00316     }
00317 
00318     // We don't check for global shortcut when the allActions list is empty
00319     // because we are not certain that apply will be called.
00320     if (keySequence != oldKeySequence && validate && !allActions.isEmpty()) {
00321         //find conflicting shortcuts with existing actions
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         //check for conflicts with other applications' global shortcuts
00345         QStringList conflicting = KGlobalAccel::findActionNameSystemwide(keySequence);
00346         if (!conflicting.isEmpty()) {
00347             if (!KGlobalAccel::promptStealShortcutSystemwide(0/*TODO:right?*/, 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     //empty string if no non-modifier was pressed
00367     QString s = keySequence.toString(QKeySequence::NativeText);
00368     s.replace('&', QLatin1String("&&"));
00369 
00370     if (isRecording) {
00371         // Display modifiers for the first key in the QKeySequence
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         //make it clear that input is still going on
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 //prevent Qt from special casing Tab and Backtab
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     // mjansen - 29.03.08
00413     // The shortcut 'alt+c' ( or any other dialog local action shortcut )
00414     // ended the recording and triggered the action associated with the
00415     // action. In case of 'alt+c' ending the dialog.  It seems that those
00416     // ShortcutOverride events get sent even if grabKeyboard() is active.
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         // Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key.
00431         // We cannot do anything useful with those (several keys have -1, indistinguishable)
00432         // and QKeySequence.toString() will also yield a garbage string.
00433         return;
00434     }
00435     uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
00436 
00437     //don't have the return or space key appear as first key of the sequence when they
00438     //were pressed to start editing - catch and them and imitate their effect
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: //or else we get unicode salad
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: //unused (yes, but why?)
00462         // If we are editing the first key in the sequence,
00463         // display modifier keys which are held down
00464         if(d->nKey == 0)
00465             d->updateShortcutDisplay();
00466         break;
00467     default:
00468         //Shift is not a modifier in the sense of Ctrl/Alt/WinKey
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         // ignore garbage, see keyPressEvent()
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     //if a modifier that belongs to the shortcut was released...
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 //static
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 //static
00542 bool KKeySequenceWidgetPrivate::isOkWhenModifierless(int keyQt)
00543 {
00544     //this whole function is a hack, but especially the first line of code
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: //does this ever happen?
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"

KDEUI

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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