kshortcutdialog.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2002,2003 Ellis Whitehead <ellis@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "kshortcutdialog.h" 00021 00022 #include <tqvariant.h> 00023 00024 #ifdef Q_WS_X11 00025 #define XK_XKB_KEYS 00026 #define XK_MISCELLANY 00027 #include <X11/Xlib.h> // For x11Event() 00028 #include <X11/keysymdef.h> // For XK_... 00029 00030 #ifdef KeyPress 00031 const int XKeyPress = KeyPress; 00032 const int XKeyRelease = KeyRelease; 00033 const int XFocusOut = FocusOut; 00034 const int XFocusIn = FocusIn; 00035 #undef KeyRelease 00036 #undef KeyPress 00037 #undef FocusOut 00038 #undef FocusIn 00039 #endif 00040 #elif defined(Q_WS_WIN) 00041 # include <kkeyserver.h> 00042 #endif 00043 00044 #include <kshortcutdialog_simple.h> 00045 #include <kshortcutdialog_advanced.h> 00046 00047 #include <tqbuttongroup.h> 00048 #include <tqcheckbox.h> 00049 #include <tqframe.h> 00050 #include <tqlayout.h> 00051 #include <tqradiobutton.h> 00052 #include <tqtimer.h> 00053 #include <tqvbox.h> 00054 00055 #include <kapplication.h> 00056 #include <kconfig.h> 00057 #include <kdebug.h> 00058 #include <kglobal.h> 00059 #include <kiconloader.h> 00060 #include <kkeynative.h> 00061 #include <klocale.h> 00062 #include <kstdguiitem.h> 00063 #include <kpushbutton.h> 00064 00065 bool KShortcutDialog::s_showMore = false; 00066 00067 KShortcutDialog::KShortcutDialog( const KShortcut& shortcut, bool bQtShortcut, TQWidget* parent, const char* name ) 00068 : KDialogBase( parent, name, true, i18n("Configure Shortcut"), 00069 KDialogBase::Details|KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Cancel, true ) 00070 { 00071 setButtonText(Details, i18n("Advanced")); 00072 m_stack = new TQVBox(this); 00073 m_stack->setMinimumWidth(360); 00074 m_stack->setSpacing(0); 00075 m_stack->setMargin(0); 00076 setMainWidget(m_stack); 00077 00078 m_simple = new KShortcutDialogSimple(m_stack); 00079 00080 m_adv = new KShortcutDialogAdvanced(m_stack); 00081 m_adv->hide(); 00082 00083 m_bQtShortcut = bQtShortcut; 00084 00085 m_iSeq = 0; 00086 m_iKey = 0; 00087 m_ptxtCurrent = 0; 00088 m_bRecording = false; 00089 m_mod = 0; 00090 00091 m_simple->m_btnClearShortcut->setPixmap( SmallIcon( "locationbar_erase" ) ); 00092 m_adv->m_btnClearPrimary->setPixmap( SmallIcon( "locationbar_erase" ) ); 00093 m_adv->m_btnClearAlternate->setPixmap( SmallIcon( "locationbar_erase" ) ); 00094 connect(m_simple->m_btnClearShortcut, TQT_SIGNAL(clicked()), 00095 this, TQT_SLOT(slotClearShortcut())); 00096 connect(m_adv->m_btnClearPrimary, TQT_SIGNAL(clicked()), 00097 this, TQT_SLOT(slotClearPrimary())); 00098 connect(m_adv->m_btnClearAlternate, TQT_SIGNAL(clicked()), 00099 this, TQT_SLOT(slotClearAlternate())); 00100 00101 connect(m_adv->m_txtPrimary, TQT_SIGNAL(clicked()), 00102 m_adv->m_btnPrimary, TQT_SLOT(animateClick())); 00103 connect(m_adv->m_txtAlternate, TQT_SIGNAL(clicked()), 00104 m_adv->m_btnAlternate, TQT_SLOT(animateClick())); 00105 connect(m_adv->m_btnPrimary, TQT_SIGNAL(clicked()), 00106 this, TQT_SLOT(slotSelectPrimary())); 00107 connect(m_adv->m_btnAlternate, TQT_SIGNAL(clicked()), 00108 this, TQT_SLOT(slotSelectAlternate())); 00109 00110 KGuiItem ok = KStdGuiItem::ok(); 00111 ok.setText( i18n( "OK" ) ); 00112 setButtonOK( ok ); 00113 00114 KGuiItem cancel = KStdGuiItem::cancel(); 00115 cancel.setText( i18n( "Cancel" ) ); 00116 setButtonCancel( cancel ); 00117 00118 setShortcut( shortcut ); 00119 resize( 0, 0 ); 00120 00121 s_showMore = KConfigGroup(KGlobal::config(), "General").readBoolEntry("ShowAlternativeShortcutConfig", s_showMore); 00122 updateDetails(); 00123 00124 #ifdef Q_WS_X11 00125 kapp->installX11EventFilter( this ); // Allow button to capture X Key Events. 00126 #endif 00127 } 00128 00129 KShortcutDialog::~KShortcutDialog() 00130 { 00131 KConfigGroup group(KGlobal::config(), "General"); 00132 group.writeEntry("ShowAlternativeShortcutConfig", s_showMore); 00133 } 00134 00135 void KShortcutDialog::setShortcut( const KShortcut & shortcut ) 00136 { 00137 m_shortcut = shortcut; 00138 updateShortcutDisplay(); 00139 } 00140 00141 void KShortcutDialog::updateShortcutDisplay() 00142 { 00143 TQString s[2] = { m_shortcut.seq(0).toString(), m_shortcut.seq(1).toString() }; 00144 00145 if( m_bRecording ) { 00146 m_ptxtCurrent->setDefault( true ); 00147 m_ptxtCurrent->setFocus(); 00148 00149 // Display modifiers for the first key in the KKeySequence 00150 if( m_iKey == 0 ) { 00151 if( m_mod ) { 00152 TQString keyModStr; 00153 if( m_mod & KKey::WIN ) keyModStr += KKey::modFlagLabel(KKey::WIN) + "+"; 00154 if( m_mod & KKey::ALT ) keyModStr += KKey::modFlagLabel(KKey::ALT) + "+"; 00155 if( m_mod & KKey::CTRL ) keyModStr += KKey::modFlagLabel(KKey::CTRL) + "+"; 00156 if( m_mod & KKey::SHIFT ) keyModStr += KKey::modFlagLabel(KKey::SHIFT) + "+"; 00157 s[m_iSeq] = keyModStr; 00158 } 00159 } 00160 // When in the middle of entering multi-key shortcuts, 00161 // add a "," to the end of the displayed shortcut. 00162 else 00163 s[m_iSeq] += ","; 00164 } 00165 else { 00166 m_adv->m_txtPrimary->setDefault( false ); 00167 m_adv->m_txtAlternate->setDefault( false ); 00168 this->setFocus(); 00169 } 00170 00171 s[0].replace('&', TQString::fromLatin1("&&")); 00172 s[1].replace('&', TQString::fromLatin1("&&")); 00173 00174 m_simple->m_txtShortcut->setText( s[0] ); 00175 m_adv->m_txtPrimary->setText( s[0] ); 00176 m_adv->m_txtAlternate->setText( s[1] ); 00177 00178 // Determine the enable state of the 'Less' button 00179 bool bLessOk; 00180 // If there is no shortcut defined, 00181 if( m_shortcut.count() == 0 ) 00182 bLessOk = true; 00183 // If there is a single shortcut defined, and it is not a multi-key shortcut, 00184 else if( m_shortcut.count() == 1 && m_shortcut.seq(0).count() <= 1 ) 00185 bLessOk = true; 00186 // Otherwise, we have an alternate shortcut or multi-key shortcut(s). 00187 else 00188 bLessOk = false; 00189 enableButton(Details, bLessOk); 00190 } 00191 00192 void KShortcutDialog::slotDetails() 00193 { 00194 s_showMore = (m_adv->isHidden()); 00195 updateDetails(); 00196 } 00197 00198 void KShortcutDialog::updateDetails() 00199 { 00200 bool showAdvanced = s_showMore || (m_shortcut.count() > 1); 00201 setDetails(showAdvanced); 00202 m_bRecording = false; 00203 m_iSeq = 0; 00204 m_iKey = 0; 00205 00206 if (showAdvanced) 00207 { 00208 m_simple->hide(); 00209 m_adv->show(); 00210 m_adv->m_btnPrimary->setChecked( true ); 00211 slotSelectPrimary(); 00212 } 00213 else 00214 { 00215 m_ptxtCurrent = m_simple->m_txtShortcut; 00216 m_adv->hide(); 00217 m_simple->show(); 00218 m_simple->m_txtShortcut->setDefault( true ); 00219 m_simple->m_txtShortcut->setFocus(); 00220 m_adv->m_btnMultiKey->setChecked( false ); 00221 } 00222 kapp->processEvents(); 00223 adjustSize(); 00224 } 00225 00226 void KShortcutDialog::slotSelectPrimary() 00227 { 00228 m_bRecording = false; 00229 m_iSeq = 0; 00230 m_iKey = 0; 00231 m_ptxtCurrent = m_adv->m_txtPrimary; 00232 m_ptxtCurrent->setDefault(true); 00233 m_ptxtCurrent->setFocus(); 00234 updateShortcutDisplay(); 00235 } 00236 00237 void KShortcutDialog::slotSelectAlternate() 00238 { 00239 m_bRecording = false; 00240 m_iSeq = 1; 00241 m_iKey = 0; 00242 m_ptxtCurrent = m_adv->m_txtAlternate; 00243 m_ptxtCurrent->setDefault(true); 00244 m_ptxtCurrent->setFocus(); 00245 updateShortcutDisplay(); 00246 } 00247 00248 void KShortcutDialog::slotClearShortcut() 00249 { 00250 m_shortcut.setSeq( 0, KKeySequence() ); 00251 updateShortcutDisplay(); 00252 } 00253 00254 void KShortcutDialog::slotClearPrimary() 00255 { 00256 m_shortcut.setSeq( 0, KKeySequence() ); 00257 m_adv->m_btnPrimary->setChecked( true ); 00258 slotSelectPrimary(); 00259 } 00260 00261 void KShortcutDialog::slotClearAlternate() 00262 { 00263 if( m_shortcut.count() == 2 ) 00264 m_shortcut.init( m_shortcut.seq(0) ); 00265 m_adv->m_btnAlternate->setChecked( true ); 00266 slotSelectAlternate(); 00267 } 00268 00269 void KShortcutDialog::slotMultiKeyMode( bool bOn ) 00270 { 00271 // If turning off multi-key mode during a recording, 00272 if( !bOn && m_bRecording ) { 00273 m_bRecording = false; 00274 m_iKey = 0; 00275 updateShortcutDisplay(); 00276 } 00277 } 00278 00279 #ifdef Q_WS_X11 00280 /* we don't use the generic Qt code on X11 because it allows us 00281 to grab the keyboard so that all keypresses are seen 00282 */ 00283 bool KShortcutDialog::x11Event( XEvent *pEvent ) 00284 { 00285 switch( pEvent->type ) { 00286 case XKeyPress: 00287 x11KeyPressEvent( pEvent ); 00288 return true; 00289 case XKeyRelease: 00290 x11KeyReleaseEvent( pEvent ); 00291 return true; 00292 case XFocusIn: 00293 { 00294 XFocusInEvent *fie = (XFocusInEvent*)pEvent; 00295 if (fie->mode != NotifyGrab && fie->mode != NotifyUngrab) { 00296 grabKeyboard(); 00297 } 00298 } 00299 break; 00300 case XFocusOut: 00301 { 00302 XFocusOutEvent *foe = (XFocusOutEvent*)pEvent; 00303 if (foe->mode != NotifyGrab && foe->mode != NotifyUngrab) { 00304 releaseKeyboard(); 00305 } 00306 } 00307 break; 00308 default: 00309 //kdDebug(125) << "x11Event->type = " << pEvent->type << endl; 00310 break; 00311 } 00312 return KDialogBase::x11Event( pEvent ); 00313 } 00314 00315 static uint getModsFromModX( uint keyModX ) 00316 { 00317 uint mod = 0; 00318 if( keyModX & KKeyNative::modX(KKey::SHIFT) ) mod += KKey::SHIFT; 00319 if( keyModX & KKeyNative::modX(KKey::CTRL) ) mod += KKey::CTRL; 00320 if( keyModX & KKeyNative::modX(KKey::ALT) ) mod += KKey::ALT; 00321 if( keyModX & KKeyNative::modX(KKey::WIN) ) mod += KKey::WIN; 00322 return mod; 00323 } 00324 00325 static bool convertSymXToMod( uint keySymX, uint* pmod ) 00326 { 00327 switch( keySymX ) { 00328 // Don't allow setting a modifier key as an accelerator. 00329 // Also, don't release the focus yet. We'll wait until 00330 // we get a 'normal' key. 00331 case XK_Shift_L: case XK_Shift_R: *pmod = KKey::SHIFT; break; 00332 case XK_Control_L: case XK_Control_R: *pmod = KKey::CTRL; break; 00333 case XK_Alt_L: case XK_Alt_R: *pmod = KKey::ALT; break; 00334 // FIXME: check whether the Meta or Super key are for the Win modifier 00335 case XK_Meta_L: case XK_Meta_R: 00336 case XK_Super_L: case XK_Super_R: *pmod = KKey::WIN; break; 00337 case XK_Hyper_L: case XK_Hyper_R: 00338 case XK_Mode_switch: 00339 case XK_Num_Lock: 00340 case XK_Caps_Lock: 00341 break; 00342 default: 00343 return false; 00344 } 00345 return true; 00346 } 00347 00348 void KShortcutDialog::x11KeyPressEvent( XEvent* pEvent ) 00349 { 00350 KKeyNative keyNative( pEvent ); 00351 uint keyModX = keyNative.mod(); 00352 uint keySymX = keyNative.sym(); 00353 00354 m_mod = getModsFromModX( keyModX ); 00355 00356 if( keySymX ) { 00357 m_bRecording = true; 00358 00359 uint mod = 0; 00360 if( convertSymXToMod( keySymX, &mod ) ) { 00361 if( mod ) 00362 m_mod |= mod; 00363 } 00364 else 00365 keyPressed( KKey(keyNative) ); 00366 } 00367 updateShortcutDisplay(); 00368 } 00369 00370 void KShortcutDialog::x11KeyReleaseEvent( XEvent* pEvent ) 00371 { 00372 // We're only interested in the release of modifier keys, 00373 // and then only when it's for the first key in a sequence. 00374 if( m_bRecording && m_iKey == 0 ) { 00375 KKeyNative keyNative( pEvent ); 00376 uint keyModX = keyNative.mod(); 00377 uint keySymX = keyNative.sym(); 00378 00379 m_mod = getModsFromModX( keyModX ); 00380 00381 uint mod = 0; 00382 if( convertSymXToMod( keySymX, &mod ) && mod ) { 00383 m_mod &= ~mod; 00384 if( !m_mod ) 00385 m_bRecording = false; 00386 } 00387 updateShortcutDisplay(); 00388 } 00389 } 00390 #elif defined(Q_WS_WIN) 00391 void KShortcutDialog::keyPressEvent( TQKeyEvent * e ) 00392 { 00393 kdDebug() << e->text() << " " << (int)e->text()[0].latin1()<< " " << (int)e->ascii() << endl; 00394 //if key is a letter, it must be stored as lowercase 00395 int keyQt = TQChar( e->key() & 0xff ).isLetter() ? 00396 (TQChar( e->key() & 0xff ).lower().latin1() | (e->key() & 0xffff00) ) 00397 : e->key(); 00398 int modQt = KKeyServer::qtButtonStateToMod( e->state() ); 00399 KKeyNative keyNative( KKey(keyQt, modQt) ); 00400 m_mod = keyNative.mod(); 00401 uint keySym = keyNative.sym(); 00402 00403 switch( keySym ) { 00404 case Key_Shift: 00405 m_mod |= KKey::SHIFT; 00406 m_bRecording = true; 00407 break; 00408 case Key_Control: 00409 m_mod |= KKey::CTRL; 00410 m_bRecording = true; 00411 break; 00412 case Key_Alt: 00413 m_mod |= KKey::ALT; 00414 m_bRecording = true; 00415 break; 00416 case Key_Menu: 00417 case Key_Meta: //unused 00418 break; 00419 default: 00420 if( keyNative.sym() == Key_Return && m_iKey > 0 ) { 00421 accept(); 00422 return; 00423 } 00424 //accept 00425 if (keyNative.sym()) { 00426 KKey key = keyNative; 00427 key.simplify(); 00428 KKeySequence seq; 00429 if( m_iKey == 0 ) 00430 seq = key; 00431 else { 00432 seq = m_shortcut.seq( m_iSeq ); 00433 seq.setKey( m_iKey, key ); 00434 } 00435 m_shortcut.setSeq( m_iSeq, seq ); 00436 00437 if(m_adv->m_btnMultiKey->isChecked()) 00438 m_iKey++; 00439 00440 m_bRecording = true; 00441 00442 updateShortcutDisplay(); 00443 00444 if( !m_adv->m_btnMultiKey->isChecked() ) 00445 TQTimer::singleShot(500, this, TQT_SLOT(accept())); 00446 } 00447 return; 00448 } 00449 00450 // If we are editing the first key in the sequence, 00451 // display modifier keys which are held down 00452 if( m_iKey == 0 ) { 00453 updateShortcutDisplay(); 00454 } 00455 } 00456 00457 bool KShortcutDialog::event ( TQEvent * e ) 00458 { 00459 if (e->type()==TQEvent::KeyRelease) { 00460 int modQt = KKeyServer::qtButtonStateToMod( static_cast<TQKeyEvent*>(e)->state() ); 00461 KKeyNative keyNative( KKey(static_cast<TQKeyEvent*>(e)->key(), modQt) ); 00462 uint keySym = keyNative.sym(); 00463 00464 bool change = true; 00465 switch( keySym ) { 00466 case Key_Shift: 00467 if (m_mod & KKey::SHIFT) 00468 m_mod ^= KKey::SHIFT; 00469 break; 00470 case Key_Control: 00471 if (m_mod & KKey::CTRL) 00472 m_mod ^= KKey::CTRL; 00473 break; 00474 case Key_Alt: 00475 if (m_mod & KKey::ALT) 00476 m_mod ^= KKey::ALT; 00477 break; 00478 default: 00479 change = false; 00480 } 00481 if (change) 00482 updateShortcutDisplay(); 00483 } 00484 return KDialogBase::event(e); 00485 } 00486 #endif 00487 00488 void KShortcutDialog::keyPressed( KKey key ) 00489 { 00490 kdDebug(125) << "keyPressed: " << key.toString() << endl; 00491 00492 key.simplify(); 00493 if( m_bQtShortcut ) { 00494 key = key.keyCodeQt(); 00495 if( key.isNull() ) { 00496 // TODO: message box about key not able to be used as application shortcut 00497 } 00498 } 00499 00500 KKeySequence seq; 00501 if( m_iKey == 0 ) 00502 seq = key; 00503 else { 00504 // Remove modifiers 00505 key.init( key.sym(), 0 ); 00506 seq = m_shortcut.seq( m_iSeq ); 00507 seq.setKey( m_iKey, key ); 00508 } 00509 00510 m_shortcut.setSeq( m_iSeq, seq ); 00511 00512 m_mod = 0; 00513 if( m_adv->m_btnMultiKey->isChecked() && m_iKey < KKeySequence::MAX_KEYS - 1 ) 00514 m_iKey++; 00515 else { 00516 m_iKey = 0; 00517 m_bRecording = false; 00518 } 00519 00520 updateShortcutDisplay(); 00521 00522 if( !m_adv->m_btnMultiKey->isChecked() ) 00523 TQTimer::singleShot(500, this, TQT_SLOT(accept())); 00524 } 00525 00526 #include "kshortcutdialog.moc"