diff options
Diffstat (limited to 'kdeui/kshortcutdialog.cpp')
-rw-r--r-- | kdeui/kshortcutdialog.cpp | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/kdeui/kshortcutdialog.cpp b/kdeui/kshortcutdialog.cpp new file mode 100644 index 000000000..05a6c705f --- /dev/null +++ b/kdeui/kshortcutdialog.cpp @@ -0,0 +1,529 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002,2003 Ellis Whitehead <ellis@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kshortcutdialog.h" + +#include <qvariant.h> + +#ifdef Q_WS_X11 + #define XK_XKB_KEYS + #define XK_MISCELLANY + #include <X11/Xlib.h> // For x11Event() + #include <X11/keysymdef.h> // For XK_... + + #ifdef KeyPress + const int XKeyPress = KeyPress; + const int XKeyRelease = KeyRelease; + const int XFocusOut = FocusOut; + const int XFocusIn = FocusIn; + #undef KeyRelease + #undef KeyPress + #undef FocusOut + #undef FocusIn + #endif +#elif defined(Q_WS_WIN) +# include <kkeyserver.h> +#endif + +#include <kshortcutdialog_simple.h> +#include <kshortcutdialog_advanced.h> + +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qframe.h> +#include <qlayout.h> +#include <qradiobutton.h> +#include <qtimer.h> +#include <qvbox.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kkeynative.h> +#include <klocale.h> +#include <kstdguiitem.h> +#include <kpushbutton.h> + +bool KShortcutDialog::s_showMore = false; + +KShortcutDialog::KShortcutDialog( const KShortcut& shortcut, bool bQtShortcut, QWidget* parent, const char* name ) +: KDialogBase( parent, name, true, i18n("Configure Shortcut"), + KDialogBase::Details|KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Cancel, true ) +{ + setButtonText(Details, i18n("Advanced")); + m_stack = new QVBox(this); + m_stack->setMinimumWidth(360); + m_stack->setSpacing(0); + m_stack->setMargin(0); + setMainWidget(m_stack); + + m_simple = new KShortcutDialogSimple(m_stack); + + m_adv = new KShortcutDialogAdvanced(m_stack); + m_adv->hide(); + + m_bQtShortcut = bQtShortcut; + + m_bGrab = false; + m_iSeq = 0; + m_iKey = 0; + m_ptxtCurrent = 0; + m_bRecording = false; + m_mod = 0; + + m_simple->m_btnClearShortcut->setPixmap( SmallIcon( "locationbar_erase" ) ); + m_adv->m_btnClearPrimary->setPixmap( SmallIcon( "locationbar_erase" ) ); + m_adv->m_btnClearAlternate->setPixmap( SmallIcon( "locationbar_erase" ) ); + connect(m_simple->m_btnClearShortcut, SIGNAL(clicked()), + this, SLOT(slotClearShortcut())); + connect(m_adv->m_btnClearPrimary, SIGNAL(clicked()), + this, SLOT(slotClearPrimary())); + connect(m_adv->m_btnClearAlternate, SIGNAL(clicked()), + this, SLOT(slotClearAlternate())); + + connect(m_adv->m_txtPrimary, SIGNAL(clicked()), + m_adv->m_btnPrimary, SLOT(animateClick())); + connect(m_adv->m_txtAlternate, SIGNAL(clicked()), + m_adv->m_btnAlternate, SLOT(animateClick())); + connect(m_adv->m_btnPrimary, SIGNAL(clicked()), + this, SLOT(slotSelectPrimary())); + connect(m_adv->m_btnAlternate, SIGNAL(clicked()), + this, SLOT(slotSelectAlternate())); + + KGuiItem ok = KStdGuiItem::ok(); + ok.setText( i18n( "OK" ) ); + setButtonOK( ok ); + + KGuiItem cancel = KStdGuiItem::cancel(); + cancel.setText( i18n( "Cancel" ) ); + setButtonCancel( cancel ); + + setShortcut( shortcut ); + resize( 0, 0 ); + + s_showMore = KConfigGroup(KGlobal::config(), "General").readBoolEntry("ShowAlternativeShortcutConfig", s_showMore); + updateDetails(); + + #ifdef Q_WS_X11 + kapp->installX11EventFilter( this ); // Allow button to capture X Key Events. + #endif +} + +KShortcutDialog::~KShortcutDialog() +{ + KConfigGroup group(KGlobal::config(), "General"); + group.writeEntry("ShowAlternativeShortcutConfig", s_showMore); +} + +void KShortcutDialog::setShortcut( const KShortcut & shortcut ) +{ + m_shortcut = shortcut; + updateShortcutDisplay(); +} + +void KShortcutDialog::updateShortcutDisplay() +{ + QString s[2] = { m_shortcut.seq(0).toString(), m_shortcut.seq(1).toString() }; + + if( m_bRecording ) { + m_ptxtCurrent->setDefault( true ); + m_ptxtCurrent->setFocus(); + + // Display modifiers for the first key in the KKeySequence + if( m_iKey == 0 ) { + if( m_mod ) { + QString keyModStr; + if( m_mod & KKey::WIN ) keyModStr += KKey::modFlagLabel(KKey::WIN) + "+"; + if( m_mod & KKey::ALT ) keyModStr += KKey::modFlagLabel(KKey::ALT) + "+"; + if( m_mod & KKey::CTRL ) keyModStr += KKey::modFlagLabel(KKey::CTRL) + "+"; + if( m_mod & KKey::SHIFT ) keyModStr += KKey::modFlagLabel(KKey::SHIFT) + "+"; + s[m_iSeq] = keyModStr; + } + } + // When in the middle of entering multi-key shortcuts, + // add a "," to the end of the displayed shortcut. + else + s[m_iSeq] += ","; + } + else { + m_adv->m_txtPrimary->setDefault( false ); + m_adv->m_txtAlternate->setDefault( false ); + this->setFocus(); + } + + s[0].replace('&', QString::fromLatin1("&&")); + s[1].replace('&', QString::fromLatin1("&&")); + + m_simple->m_txtShortcut->setText( s[0] ); + m_adv->m_txtPrimary->setText( s[0] ); + m_adv->m_txtAlternate->setText( s[1] ); + + // Determine the enable state of the 'Less' button + bool bLessOk; + // If there is no shortcut defined, + if( m_shortcut.count() == 0 ) + bLessOk = true; + // If there is a single shortcut defined, and it is not a multi-key shortcut, + else if( m_shortcut.count() == 1 && m_shortcut.seq(0).count() <= 1 ) + bLessOk = true; + // Otherwise, we have an alternate shortcut or multi-key shortcut(s). + else + bLessOk = false; + enableButton(Details, bLessOk); +} + +void KShortcutDialog::slotDetails() +{ + s_showMore = (m_adv->isHidden()); + updateDetails(); +} + +void KShortcutDialog::updateDetails() +{ + bool showAdvanced = s_showMore || (m_shortcut.count() > 1); + setDetails(showAdvanced); + m_bRecording = false; + m_iSeq = 0; + m_iKey = 0; + + if (showAdvanced) + { + m_simple->hide(); + m_adv->show(); + m_adv->m_btnPrimary->setChecked( true ); + slotSelectPrimary(); + } + else + { + m_ptxtCurrent = m_simple->m_txtShortcut; + m_adv->hide(); + m_simple->show(); + m_simple->m_txtShortcut->setDefault( true ); + m_simple->m_txtShortcut->setFocus(); + m_adv->m_btnMultiKey->setChecked( false ); + } + kapp->processEvents(); + adjustSize(); +} + +void KShortcutDialog::slotSelectPrimary() +{ + m_bRecording = false; + m_iSeq = 0; + m_iKey = 0; + m_ptxtCurrent = m_adv->m_txtPrimary; + m_ptxtCurrent->setDefault(true); + m_ptxtCurrent->setFocus(); + updateShortcutDisplay(); +} + +void KShortcutDialog::slotSelectAlternate() +{ + m_bRecording = false; + m_iSeq = 1; + m_iKey = 0; + m_ptxtCurrent = m_adv->m_txtAlternate; + m_ptxtCurrent->setDefault(true); + m_ptxtCurrent->setFocus(); + updateShortcutDisplay(); +} + +void KShortcutDialog::slotClearShortcut() +{ + m_shortcut.setSeq( 0, KKeySequence() ); + updateShortcutDisplay(); +} + +void KShortcutDialog::slotClearPrimary() +{ + m_shortcut.setSeq( 0, KKeySequence() ); + m_adv->m_btnPrimary->setChecked( true ); + slotSelectPrimary(); +} + +void KShortcutDialog::slotClearAlternate() +{ + if( m_shortcut.count() == 2 ) + m_shortcut.init( m_shortcut.seq(0) ); + m_adv->m_btnAlternate->setChecked( true ); + slotSelectAlternate(); +} + +void KShortcutDialog::slotMultiKeyMode( bool bOn ) +{ + // If turning off multi-key mode during a recording, + if( !bOn && m_bRecording ) { + m_bRecording = false; + m_iKey = 0; + updateShortcutDisplay(); + } +} + +#ifdef Q_WS_X11 +/* we don't use the generic Qt code on X11 because it allows us + to grab the keyboard so that all keypresses are seen + */ +bool KShortcutDialog::x11Event( XEvent *pEvent ) +{ + switch( pEvent->type ) { + case XKeyPress: + x11KeyPressEvent( pEvent ); + return true; + case XKeyRelease: + x11KeyReleaseEvent( pEvent ); + return true; + case XFocusIn: + if (!m_bGrab) { + //kdDebug(125) << "FocusIn and Grab!" << endl; + grabKeyboard(); + m_bGrab = true; + } + //else + // kdDebug(125) << "FocusIn" << endl; + break; + case XFocusOut: + if (m_bGrab) { + //kdDebug(125) << "FocusOut and Ungrab!" << endl; + releaseKeyboard(); + m_bGrab = false; + } + //else + // kdDebug(125) << "FocusOut" << endl; + break; + default: + //kdDebug(125) << "x11Event->type = " << pEvent->type << endl; + break; + } + return KDialogBase::x11Event( pEvent ); +} + +static uint getModsFromModX( uint keyModX ) +{ + uint mod = 0; + if( keyModX & KKeyNative::modX(KKey::SHIFT) ) mod += KKey::SHIFT; + if( keyModX & KKeyNative::modX(KKey::CTRL) ) mod += KKey::CTRL; + if( keyModX & KKeyNative::modX(KKey::ALT) ) mod += KKey::ALT; + if( keyModX & KKeyNative::modX(KKey::WIN) ) mod += KKey::WIN; + return mod; +} + +static bool convertSymXToMod( uint keySymX, uint* pmod ) +{ + switch( keySymX ) { + // Don't allow setting a modifier key as an accelerator. + // Also, don't release the focus yet. We'll wait until + // we get a 'normal' key. + case XK_Shift_L: case XK_Shift_R: *pmod = KKey::SHIFT; break; + case XK_Control_L: case XK_Control_R: *pmod = KKey::CTRL; break; + case XK_Alt_L: case XK_Alt_R: *pmod = KKey::ALT; break; + // FIXME: check whether the Meta or Super key are for the Win modifier + case XK_Meta_L: case XK_Meta_R: + case XK_Super_L: case XK_Super_R: *pmod = KKey::WIN; break; + case XK_Hyper_L: case XK_Hyper_R: + case XK_Mode_switch: + case XK_Num_Lock: + case XK_Caps_Lock: + break; + default: + return false; + } + return true; +} + +void KShortcutDialog::x11KeyPressEvent( XEvent* pEvent ) +{ + KKeyNative keyNative( pEvent ); + uint keyModX = keyNative.mod(); + uint keySymX = keyNative.sym(); + + m_mod = getModsFromModX( keyModX ); + + if( keySymX ) { + m_bRecording = true; + + uint mod = 0; + if( convertSymXToMod( keySymX, &mod ) ) { + if( mod ) + m_mod |= mod; + } + else + keyPressed( KKey(keyNative) ); + } + updateShortcutDisplay(); +} + +void KShortcutDialog::x11KeyReleaseEvent( XEvent* pEvent ) +{ + // We're only interested in the release of modifier keys, + // and then only when it's for the first key in a sequence. + if( m_bRecording && m_iKey == 0 ) { + KKeyNative keyNative( pEvent ); + uint keyModX = keyNative.mod(); + uint keySymX = keyNative.sym(); + + m_mod = getModsFromModX( keyModX ); + + uint mod = 0; + if( convertSymXToMod( keySymX, &mod ) && mod ) { + m_mod &= ~mod; + if( !m_mod ) + m_bRecording = false; + } + updateShortcutDisplay(); + } +} +#elif defined(Q_WS_WIN) +void KShortcutDialog::keyPressEvent( QKeyEvent * e ) +{ + kdDebug() << e->text() << " " << (int)e->text()[0].latin1()<< " " << (int)e->ascii() << endl; + //if key is a letter, it must be stored as lowercase + int keyQt = QChar( e->key() & 0xff ).isLetter() ? + (QChar( e->key() & 0xff ).lower().latin1() | (e->key() & 0xffff00) ) + : e->key(); + int modQt = KKeyServer::qtButtonStateToMod( e->state() ); + KKeyNative keyNative( KKey(keyQt, modQt) ); + m_mod = keyNative.mod(); + uint keySym = keyNative.sym(); + + switch( keySym ) { + case Key_Shift: + m_mod |= KKey::SHIFT; + m_bRecording = true; + break; + case Key_Control: + m_mod |= KKey::CTRL; + m_bRecording = true; + break; + case Key_Alt: + m_mod |= KKey::ALT; + m_bRecording = true; + break; + case Key_Menu: + case Key_Meta: //unused + break; + default: + if( keyNative.sym() == Key_Return && m_iKey > 0 ) { + accept(); + return; + } + //accept + if (keyNative.sym()) { + KKey key = keyNative; + key.simplify(); + KKeySequence seq; + if( m_iKey == 0 ) + seq = key; + else { + seq = m_shortcut.seq( m_iSeq ); + seq.setKey( m_iKey, key ); + } + m_shortcut.setSeq( m_iSeq, seq ); + + if(m_adv->m_btnMultiKey->isChecked()) + m_iKey++; + + m_bRecording = true; + + updateShortcutDisplay(); + + if( !m_adv->m_btnMultiKey->isChecked() ) + QTimer::singleShot(500, this, SLOT(accept())); + } + return; + } + + // If we are editing the first key in the sequence, + // display modifier keys which are held down + if( m_iKey == 0 ) { + updateShortcutDisplay(); + } +} + +bool KShortcutDialog::event ( QEvent * e ) +{ + if (e->type()==QEvent::KeyRelease) { + int modQt = KKeyServer::qtButtonStateToMod( static_cast<QKeyEvent*>(e)->state() ); + KKeyNative keyNative( KKey(static_cast<QKeyEvent*>(e)->key(), modQt) ); + uint keySym = keyNative.sym(); + + bool change = true; + switch( keySym ) { + case Key_Shift: + if (m_mod & KKey::SHIFT) + m_mod ^= KKey::SHIFT; + break; + case Key_Control: + if (m_mod & KKey::CTRL) + m_mod ^= KKey::CTRL; + break; + case Key_Alt: + if (m_mod & KKey::ALT) + m_mod ^= KKey::ALT; + break; + default: + change = false; + } + if (change) + updateShortcutDisplay(); + } + return KDialogBase::event(e); +} +#endif + +void KShortcutDialog::keyPressed( KKey key ) +{ + kdDebug(125) << "keyPressed: " << key.toString() << endl; + + key.simplify(); + if( m_bQtShortcut ) { + key = key.keyCodeQt(); + if( key.isNull() ) { + // TODO: message box about key not able to be used as application shortcut + } + } + + KKeySequence seq; + if( m_iKey == 0 ) + seq = key; + else { + // Remove modifiers + key.init( key.sym(), 0 ); + seq = m_shortcut.seq( m_iSeq ); + seq.setKey( m_iKey, key ); + } + + m_shortcut.setSeq( m_iSeq, seq ); + + m_mod = 0; + if( m_adv->m_btnMultiKey->isChecked() && m_iKey < KKeySequence::MAX_KEYS - 1 ) + m_iKey++; + else { + m_iKey = 0; + m_bRecording = false; + } + + updateShortcutDisplay(); + + if( !m_adv->m_btnMultiKey->isChecked() ) + QTimer::singleShot(500, this, SLOT(accept())); +} + +#include "kshortcutdialog.moc" |