summaryrefslogtreecommitdiffstats
path: root/kdeui/kshortcutdialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdeui/kshortcutdialog.cpp')
-rw-r--r--kdeui/kshortcutdialog.cpp529
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"