Index: trunk/kdelibs/kdeui/kkeydialog.cpp =================================================================== --- trunk/kdelibs/kdeui/kkeydialog.cpp (revision 103063) +++ trunk/kdelibs/kdeui/kkeydialog.cpp (revision 103064) @@ -1,1029 +1,1029 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe Copyright (C) 1997 Nicolas Hadacek Copyright (C) 1998 Matthias Ettrich 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kkeydialog.h" #include #include "kkeybutton.h" #include #define XK_XKB_KEYS #define XK_MISCELLANY #include // For x11Event() #include // For XK_... #ifdef KeyPress const int XFocusOut = FocusOut; const int XFocusIn = FocusIn; const int XKeyPress = KeyPress; const int XKeyRelease = KeyRelease; #undef KeyRelease #undef KeyPress #undef FocusOut #undef FocusIn #endif class KKeyChooserPrivate { public: QDict *globalDict; QDict *stdDict; KListView *wList; QLabel *lInfo; KKeyButton *bChange; QGroupBox *fCArea; QButtonGroup *kbGroup; KKeyEntryMap *map; QMap actionMap; bool bKeyIntercept; bool bAllowMetaKey; // If this is set, then shortcuts require a modifier: // so 'A' would not be valid, whereas 'Ctrl+A' would be. // Note, however, that this only applies to printable characters. // 'F1', 'Insert', etc., could still be used. bool bAllowLetterShortcuts; // When set, pressing the 'Default' button will select the aDefaultKeycode4, // otherwise aDefaultKeycode. bool bPreferFourModifierKeys; }; // HACK: for getting around some of Qt's lack of Meta support enum { QT_META_MOD = Qt::ALT << 1 }; // Supply Meta bit where Qt left it out. //bool KKeyChooserPrivate::g_bMetaPressed = false; /***********************************************************************/ /* KKeyButton */ /* */ /* Added by Mark Donohoe */ /* */ /***********************************************************************/ KKeyButton::KKeyButton(QWidget *parent, const char *name) : QPushButton( parent, name ) { setFocusPolicy( QWidget::StrongFocus ); editing = false; connect( this, SIGNAL(clicked()), this, SLOT(captureKey()) ); kapp->installX11EventFilter( this ); // Allow button to capture X Key Events. setKey( 0 ); } KKeyButton::~KKeyButton () { if( editing ) captureKey( false ); } void KKeyButton::setKey( uint _key ) { key = _key; QString keyStr = KAccel::keyToString( key, true ); setText( keyStr.isEmpty() ? "None" : keyStr ); } void KKeyButton::setText( const QString& text ) { QPushButton::setText( text ); setFixedSize( sizeHint().width()+12, sizeHint().height()+8 ); } void KKeyButton::captureKey( bool bCapture ) { editing = bCapture; if( editing == true ) { setFocus(); KGlobalAccel::setKeyEventsEnabled( false ); grabKeyboard(); grabMouse( IbeamCursor ); } else { releaseMouse(); releaseKeyboard(); KGlobalAccel::setKeyEventsEnabled( true ); } repaint(); } void KKeyButton::captureKey() { captureKey( true ); } bool KKeyButton::x11Event( XEvent *pEvent ) { if( editing ) { //kdDebug(125) << "x11Event: type: " << pEvent->type << " window: " << pEvent->xany.window << endl; switch( pEvent->type ) { case XKeyPress: case XKeyRelease: keyPressEventX( pEvent ); return true; case ButtonPress: captureKey( false ); setKey( key ); return true; } } return QWidget::x11Event( pEvent ); } void KKeyButton::keyPressEventX( XEvent *pEvent ) { uint keyModX = 0, keySymX; KAccel::keyEventXToKeyX( pEvent, 0, &keySymX, 0 ); //kdDebug(125) << QString( "keycode: 0x%1 state: 0x%2\n" ) // .arg( pEvent->xkey.keycode, 0, 16 ).arg( pEvent->xkey.state, 0, 16 ); 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: keyModX = KAccel::keyModXShift(); break; case XK_Control_L: case XK_Control_R: keyModX = KAccel::keyModXCtrl(); break; case XK_Alt_L: case XK_Alt_R: keyModX = KAccel::keyModXAlt(); break; case XK_Meta_L: case XK_Meta_R: keyModX = KAccel::keyModXMeta(); break; case XK_Super_L: case XK_Super_R: case XK_Hyper_L: case XK_Hyper_R: case XK_Mode_switch: break; default: uint keyCombQt = KAccel::keyEventXToKeyQt( pEvent ); if( keyCombQt && keyCombQt != Qt::Key_unknown ) { captureKey( false ); // The parent must decide whether this is a valid // key, and if so, call setKey(uint) with the new value. emit capturedKey( keyCombQt ); setKey( key ); } return; } if( pEvent->type == XKeyPress ) keyModX |= pEvent->xkey.state; else keyModX = pEvent->xkey.state & ~keyModX; QString keyModStr; if( keyModX & KAccel::keyModXMeta() ) keyModStr += "Meta+"; if( keyModX & KAccel::keyModXAlt() ) keyModStr += "Alt+"; if( keyModX & KAccel::keyModXCtrl() ) keyModStr += "Ctrl+"; if( keyModX & KAccel::keyModXShift() ) keyModStr += "Shift+"; // Display currently selected modifiers, or redisplay old key. if( !keyModStr.isEmpty() ) setText( keyModStr ); else setKey( key ); } void KKeyButton::drawButton( QPainter *painter ) { QPointArray a( 4 ); a.setPoint( 0, 0, 0) ; a.setPoint( 1, width(), 0 ); a.setPoint( 2, 0, height() ); a.setPoint( 3, 0, 0 ); QRegion r1( a ); painter->setClipRegion( r1 ); painter->setBrush( backgroundColor().light() ); painter->drawRoundRect( 0, 0, width(), height(), 20, 20); a.setPoint( 0, width(), height() ); a.setPoint( 1, width(), 0 ); a.setPoint( 2, 0, height() ); a.setPoint( 3, width(), height() ); QRegion r2( a ); painter->setClipRegion( r2 ); painter->setBrush( backgroundColor().dark() ); painter->drawRoundRect( 0, 0, width(), height(), 20, 20 ); painter->setClipping( false ); if( width() > 12 && height() > 8 ) qDrawShadePanel( painter, 6, 4, width() - 12, height() - 8, colorGroup(), true, 1, 0L ); if ( editing ) { painter->setPen( colorGroup().base() ); painter->setBrush( colorGroup().base() ); } else { painter->setPen( backgroundColor() ); painter->setBrush( backgroundColor() ); } if( width() > 14 && height() > 10 ) painter->drawRect( 7, 5, width() - 14, height() - 10 ); drawButtonLabel( painter ); painter->setPen( colorGroup().text() ); painter->setBrush( NoBrush ); if( hasFocus() || editing ) { if( width() > 16 && height() > 12 ) painter->drawRect( 8, 6, width() - 16, height() - 12 ); } } /************************************************************************/ /* KKeyDialog */ /* */ /* Originally by Nicolas Hadacek */ /* */ /* Substantially revised by Mark Donohoe */ /* */ /* And by Espen Sand 1999-10-19 */ /* (by using KDialogBase there is almost no code left ;) */ /* */ /************************************************************************/ KKeyDialog::KKeyDialog( KKeyEntryMap *aKeyMap, QWidget *parent, bool check_against_std_keys) : KDialogBase( parent, 0, true, i18n("Configure Key Bindings"), Help|Default|Ok|Cancel, Ok ) { KKeyChooser *kc = new KKeyChooser( aKeyMap, this, check_against_std_keys ); setMainWidget(kc); connect( this, SIGNAL(defaultClicked()), kc, SLOT(allDefault()) ); enableButton ( Help, false ); } KKeyDialog::~KKeyDialog() { } int KKeyDialog::configureKeys( KAccel *keys, bool save_settings, QWidget *parent ) { KKeyEntryMap map = keys->keyDict(); KKeyDialog kd( &map, parent ); int retcode = kd.exec(); if( retcode == Accepted ) { keys->setKeyDict( map ); if (save_settings) keys->writeSettings(); } return retcode; } int KKeyDialog::configureKeys( KGlobalAccel *keys, bool save_settings, QWidget *parent ) { KKeyEntryMap dict = keys->keyDict(); KKeyDialog kd( &dict, parent ); int retcode = kd.exec(); if( retcode == Accepted ) { keys->setKeyDict( dict ); if (save_settings) keys->writeSettings(); } return retcode; } int KKeyDialog::configureKeys( KActionCollection *coll, const QString& file, bool save_settings, QWidget *parent ) { KKeyEntryMap map = coll->keyMap(); KKeyDialog kd( &map, parent ); int retcode = kd.exec(); if( retcode != Accepted ) return retcode; if (!save_settings) { coll->setKeyMap( map ); return retcode; } // let's start saving this info QString raw_xml(KXMLGUIFactory::readConfigFile(file)); QDomDocument doc; doc.setContent(raw_xml); QString tagActionProp = QString::fromLatin1( "ActionProperties" ); QString tagAction = QString::fromLatin1( "Action" ); QString attrName = QString::fromLatin1( "name" ); QString attrAccel = QString::fromLatin1( "accel" ); // first, lets see if we have existing properties QDomElement elem; QDomElement it = doc.documentElement(); KXMLGUIFactory::removeDOMComments( it ); it = it.firstChild().toElement(); for ( ; !it.isNull(); it = it.nextSibling().toElement() ) if ( it.tagName() == tagActionProp ) { elem = it; break; } // if there was none, create one if ( elem.isNull() ) { elem = doc.createElement( tagActionProp ); doc.firstChild().appendChild(elem); } // now, iterate through our actions for (unsigned int i = 0; i < coll->count(); i++) { KAction *action = coll->action(i); // see if we changed KKeyEntry key = map[action->name()]; if (key.aCurrentKeyCode == key.aConfigKeyCode) continue; // now see if this element already exists QDomElement act_elem; for ( it = elem.firstChild().toElement(); !it.isNull(); it = it.nextSibling().toElement() ) { if ( it.attribute( attrName ) == action->name() ) { act_elem = it; break; } } // nope, create a new one if ( act_elem.isNull() ) { act_elem = doc.createElement( tagAction ); act_elem.setAttribute( attrName, action->name() ); } act_elem.setAttribute( attrAccel, KAccel::keyToString( key.aConfigKeyCode, false) ); elem.appendChild( act_elem ); } // finally, write out the result KXMLGUIFactory::saveConfigFile(doc, file); coll->setKeyMap( map ); return retcode; } //************************************************************************ // KKeyChooser * //************************************************************************ KKeyChooser::KKeyChooser( KKeyEntryMap *aKeyMap, KKeyMapOrder *pMapOrder, QWidget* parent, bool check_against_std_keys, bool bAllowLetterShortcuts, bool bAllowMetaKey ) : QWidget( parent ) { init( aKeyMap, pMapOrder, check_against_std_keys, bAllowLetterShortcuts, bAllowMetaKey ); } KKeyChooser::KKeyChooser( KKeyEntryMap *aKeyMap, QWidget* parent, bool check_against_std_keys, bool bAllowLetterShortcuts, bool bAllowMetaKey ) : QWidget( parent ) { init( aKeyMap, 0, check_against_std_keys, bAllowLetterShortcuts, bAllowMetaKey ); } KKeyChooser::KKeyChooser( KKeyEntryMap *aKeyMap, QWidget *parent, bool check_against_std_keys) : QWidget( parent ) { init( aKeyMap, 0, check_against_std_keys, false, true ); } void KKeyChooser::init( KKeyEntryMap *aKeyMap, KKeyMapOrder *pMapOrder, bool check_against_std_keys, bool bAllowLetterShortcuts, bool bAllowMetaKey ) { d = new KKeyChooserPrivate(); d->bKeyIntercept = false; d->map = aKeyMap; d->bAllowMetaKey = bAllowMetaKey; d->bAllowLetterShortcuts = bAllowLetterShortcuts; d->bPreferFourModifierKeys = KAccel::useFourModifierKeys(); // // TOP LAYOUT MANAGER // // The following layout is used for the dialog // LIST LABELS LAYOUT // SPLIT LIST BOX WIDGET // CHOOSE KEY GROUP BOX WIDGET // BUTTONS LAYOUT // Items are added to topLayout as they are created. // QBoxLayout *topLayout = new QVBoxLayout( this, 0, KDialog::spacingHint() ); QGridLayout *stackLayout = new QGridLayout(2, 2, 2); topLayout->addLayout( stackLayout, 10 ); stackLayout->setRowStretch( 1, 10 ); // Only list will stretch // // CREATE SPLIT LIST BOX // // Copy all currentKeyCodes to configKeyCodes // and fill up the split list box with the action/key pairs. // d->wList = new KListView( this ); d->wList->setFocus(); stackLayout->addMultiCellWidget( d->wList, 1, 1, 0, 1 ); QString wtstr = i18n("Here you can see a list of key bindings, \n" "i.e. associations between actions (e.g. 'Copy')\n" "shown in the left column and keys or combination\n" "of keys (e.g. CTRL-V) shown in the right column."); QWhatsThis::add( d->wList, wtstr ); d->wList->addColumn(i18n("Action")); d->wList->addColumn(i18n("Current Key")); // // Add all "keys" to the list // if( pMapOrder ) { d->wList->setSorting( -1 ); // HACK to avoid alphabetic ording. I'll re-write this in the // next development phase where API changes are not so sensitive. -- ellis QListViewItem *pProgramItem, *pGroupItem = 0, *pParentItem, *pItem; // Initially set all of the keys to not-configurable, and then only // let those which are to be displayed be configured (set below). // This is so that the call to KAccel::writeKeyMap() will only write // those which we are editing. for( KKeyEntryMap::Iterator it = aKeyMap->begin(); it != aKeyMap->end(); ++it ) (*it).bConfigurable = false; pParentItem = pProgramItem = pItem = new QListViewItem( d->wList, "Shortcuts" ); pParentItem->setExpandable( true ); pParentItem->setOpen( true ); pParentItem->setSelectable( false ); for( int i = 0; i < (int)pMapOrder->count(); i++ ) { QString sConfigKey = (*pMapOrder)[i]; KKeyEntryMap::Iterator it = aKeyMap->find( sConfigKey ); kdDebug(125) << "Key: " << it.key() << endl; if( it.key().contains("Program:") ) { pItem = new QListViewItem( d->wList, pProgramItem, (*it).descr ); pItem->setSelectable( false ); pItem->setExpandable( true ); pItem->setOpen( true ); if( !pProgramItem->firstChild() ) delete pProgramItem; pProgramItem = pParentItem = pItem; } else if( it.key().contains("Group:") ) { - pItem = new QListViewItem( pProgramItem, pItem, (*it).descr ); + pItem = new QListViewItem( pProgramItem, pParentItem, (*it).descr ); pItem->setSelectable( false ); pItem->setExpandable( true ); pItem->setOpen( true ); if( pGroupItem && !pGroupItem->firstChild() ) delete pGroupItem; pGroupItem = pParentItem = pItem; } else { (*it).aConfigKeyCode = (*it).aCurrentKeyCode; pItem = new QListViewItem( pParentItem, pItem, (*it).descr, KAccel::keyToString( (*it).aConfigKeyCode, true ) ); d->actionMap[pItem] = it; (*it).bConfigurable = true; } } if( !pProgramItem->firstChild() ) delete pProgramItem; if( pGroupItem && !pGroupItem->firstChild() ) delete pGroupItem; } else { for (KKeyEntryMap::Iterator it = aKeyMap->begin(); it != aKeyMap->end(); ++it) { (*it).aConfigKeyCode = (*it).aCurrentKeyCode; QListViewItem * s = new QListViewItem(d->wList, (*it).descr, KAccel::keyToString((*it).aConfigKeyCode, true)); d->actionMap[s] = it; } } connect( d->wList, SIGNAL( currentChanged( QListViewItem * ) ), SLOT( updateAction( QListViewItem * ) ) ); // // CREATE CHOOSE KEY GROUP // d->fCArea = new QGroupBox( this ); topLayout->addWidget( d->fCArea, 1 ); d->fCArea->setTitle( i18n("Choose a Key for the Selected Action") ); d->fCArea->setFrameStyle( QFrame::Box | QFrame::Sunken ); // // CHOOSE KEY GROUP LAYOUT MANAGER // /*QGridLayout *grid = new QGridLayout( d->fCArea, 6, 4, KDialog::spacingHint() ); grid->setRowStretch(0,10); grid->setRowStretch(1,10); grid->setRowStretch(2,10); grid->setRowStretch(3,10); grid->setRowStretch(4,10); grid->setRowStretch(5,10); grid->setColStretch(0,0); grid->setColStretch(1,10); grid->setColStretch(2,90); grid->setColStretch(3,0); grid->addRowSpacing(0,15); grid->addRowSpacing(5,1);*/ QGridLayout *grid = new QGridLayout( d->fCArea, 3, 4, KDialog::spacingHint() ); grid->addRowSpacing( 0, 5 ); d->kbGroup = new QButtonGroup( d->fCArea ); d->kbGroup->hide(); d->kbGroup->setExclusive( true ); QRadioButton *rb = new QRadioButton( i18n("&No Key"), d->fCArea ); d->kbGroup->insert( rb, NoKey ); rb->setEnabled( false ); //grid->addMultiCellWidget( rb, 1, 1, 1, 2 ); grid->addWidget( rb, 1, 0 ); QWhatsThis::add( rb, i18n("The selected action will not be associated with any key.") ); rb = new QRadioButton( i18n("De&fault Key"), d->fCArea ); d->kbGroup->insert( rb, DefaultKey ); rb->setEnabled( false ); //grid->addMultiCellWidget( rb, 2, 2, 1, 2 ); grid->addWidget( rb, 1, 1 ); QWhatsThis::add( rb, i18n("This will bind the default key to the selected action. Usually a reasonable choice.") ); rb = new QRadioButton( i18n("Custom &Key"), d->fCArea ); d->kbGroup->insert( rb, CustomKey ); rb->setEnabled( false ); //grid->addMultiCellWidget( rb, 3, 3, 1, 2 ); grid->addWidget( rb, 1, 2 ); QWhatsThis::add( rb, i18n("If this option is selected you can create a customized key binding for the" " selected action using the buttons below.") ); connect( d->kbGroup, SIGNAL( clicked( int ) ), SLOT( keyMode( int ) ) ); QBoxLayout *pushLayout = new QHBoxLayout( KDialog::spacingHint() ); grid->addLayout( pushLayout, 1, 3 ); d->bChange = new KKeyButton(d->fCArea, "key"); d->bChange->setEnabled( false ); connect( d->bChange, SIGNAL( capturedKey(uint) ), SLOT( capturedKey(uint) ) ); grid->addRowSpacing( 1, d->bChange->sizeHint().height() + 5 ); wtstr = i18n("Use this button to choose a new shortcut key. Once you click it, " "you can press the key-combination which you would like to be assigned " "to the currently selected action."); QWhatsThis::add( d->bChange, wtstr ); // // Add widgets to the geometry manager // pushLayout->addSpacing( KDialog::spacingHint()*2 ); pushLayout->addWidget( d->bChange ); pushLayout->addStretch( 10 ); d->lInfo = new QLabel(d->fCArea); //resize(0,0); //d->lInfo->setAlignment( AlignCenter ); //d->lInfo->setEnabled( false ); //d->lInfo->hide(); grid->addMultiCellWidget( d->lInfo, 2, 2, 0, 3 ); d->globalDict = new QDict ( 100, false ); d->globalDict->setAutoDelete( true ); readGlobalKeys(); d->stdDict = new QDict ( 100, false ); d->stdDict->setAutoDelete( true ); if (check_against_std_keys) readStdKeys(); } KKeyChooser::~KKeyChooser() { delete d->globalDict; delete d->stdDict; delete d->wList; delete d; // Make sure that we don't still have global accelerators turned off. //KGlobalAccel::setKeyEventsEnabled( true ); } void KKeyChooser::updateAction( QListViewItem *item ) { toChange( item ); } void KKeyChooser::readKeysInternal( QDict< int >* dict, const QString& group ) { dict->clear(); // Insert all keys into dict int *keyCode; KConfig pConfig; QMap tmpMap = pConfig.entryMap( group ); QMap::Iterator gIt(tmpMap.begin()); for (; gIt != tmpMap.end(); ++gIt) { if ( (*gIt).isEmpty() || *gIt == "default" ) // old code used to write just "default" continue; // which is not enough kdDebug( 125 ) << gIt.key() << " " << *gIt << endl; QString tmp = *gIt; if( tmp.startsWith( "default(" )) { int pos = tmp.findRev( ')' ); if( pos >= 0 ) // this should be really done with regexp tmp = tmp.mid( 8, pos - 8 ); } keyCode = new int; *keyCode = KAccel::stringToKey( tmp ); dict->insert( gIt.key(), keyCode); } } void KKeyChooser::readGlobalKeys() { //debug("KKeyChooser::readGlobalKeys()"); readKeysInternal( d->globalDict, QString::fromLatin1("Global Keys")); // insert all global keys, even if they appear in dictionary to be configured } void KKeyChooser::readStdKeys() { // debug("KKeyChooser::readStdKeys()"); readKeysInternal( d->stdDict, QString::fromLatin1("Keys")); // Only insert std keys which don't appear in the dictionary to be configured for (KKeyEntryMap::ConstIterator it = d->map->begin(); it != d->map->end(); ++it) if ( d->stdDict->find( it.key() ) ) d->stdDict->remove( it.key() ); } void KKeyChooser::toChange( QListViewItem *item ) { // Hack: Do this incase we still have changeKey() running. // Better would be to capture the mouse pointer so that we can't click // around while we're supposed to be entering a key. // Better yet would be a modal dialog for changeKey()! d->bKeyIntercept = false; releaseKeyboard(); if ( !item || !d->actionMap.contains( item ) ) { // if nothing is selected -> disable radio boxes d->kbGroup->find(NoKey)->setEnabled( false ); d->kbGroup->find(DefaultKey)->setEnabled( false ); d->kbGroup->find(CustomKey)->setEnabled( false ); d->bChange->setEnabled( false ); } else { /* get the entry */ KKeyEntryMap::Iterator it = d->actionMap[item]; - int keyDefault = d->bPreferFourModifierKeys ? (*it).aDefaultKeyCode4 : (*it).aDefaultKeyCode; + int keyDefault = /*d->bPreferFourModifierKeys ? (*it).aDefaultKeyCode4 :*/ (*it).aDefaultKeyCode; // Set key strings QString keyStrCfg = KAccel::keyToString( (*it).aConfigKeyCode, true ); QString keyStrDef = KAccel::keyToString( keyDefault, true ); d->bChange->setKey( (*it).aConfigKeyCode ); item->setText( 1, keyStrCfg ); d->lInfo->setText( i18n("Default Key") + QString(": %1").arg(keyStrDef.isEmpty() ? "None" : keyStrDef) ); // Select the appropriate radio button. int index = ((*it).aConfigKeyCode == 0) ? NoKey : ((*it).aConfigKeyCode == keyDefault) ? DefaultKey : CustomKey; ((QRadioButton *)d->kbGroup->find(NoKey))->setChecked( index == NoKey ); ((QRadioButton *)d->kbGroup->find(DefaultKey))->setChecked( index == DefaultKey ); ((QRadioButton *)d->kbGroup->find(CustomKey))->setChecked( index == CustomKey ); // Enable buttons if this key is configurable. // The 'Default Key' button must also have a default key. ((QRadioButton *)d->kbGroup->find(NoKey))->setEnabled( (*it).bConfigurable ); ((QRadioButton *)d->kbGroup->find(DefaultKey))->setEnabled( (*it).bConfigurable && keyDefault != 0 ); ((QRadioButton *)d->kbGroup->find(CustomKey))->setEnabled( (*it).bConfigurable ); d->bChange->setEnabled( (*it).bConfigurable ); } } void KKeyChooser::fontChange( const QFont & ) { d->fCArea->setMinimumHeight( 4*d->bChange->sizeHint().height() ); int widget_width = 0; setMinimumWidth( 20+5*(widget_width+10) ); } void KKeyChooser::keyMode( int m ) { switch( m ) { case NoKey: noKey(); break; case DefaultKey: defaultKey(); break; case CustomKey: d->bChange->captureKey(); break; } } void KKeyChooser::noKey() { // return if no key is selected QListViewItem *item = d->wList->currentItem(); if (!item) return; //kdDebug(125) << "no Key" << d->wList->currentItem()->text(0) << endl; if( d->actionMap.contains( d->wList->currentItem() ) ) (*d->actionMap[d->wList->currentItem()]).aConfigKeyCode = 0; /* update the list and the change area */ toChange(d->wList->currentItem()); emit keyChange(); } void KKeyChooser::defaultKey() { // return if no key is selected QListViewItem *item = d->wList->currentItem(); if (!item) return; /* update the list and the change area */ if( d->actionMap.contains( item ) ) { (*d->actionMap[item]).aConfigKeyCode = - (d->bPreferFourModifierKeys) ? - (*d->actionMap[item]).aDefaultKeyCode4 : + /*(d->bPreferFourModifierKeys) ? + (*d->actionMap[item]).aDefaultKeyCode4 :*/ (*d->actionMap[item]).aDefaultKeyCode; item->setText( 1, KAccel::keyToString((*d->actionMap[item]).aConfigKeyCode, true) ); } toChange(d->wList->currentItem()); emit keyChange(); } void KKeyChooser::allDefault() { allDefault( d->bPreferFourModifierKeys ); } void KKeyChooser::allDefault( bool useFourModifierKeys ) { // Change all configKeyCodes to default values kdDebug(125) << QString( "allDefault( %1 )\n" ).arg( useFourModifierKeys ); for( QMap::Iterator itit = d->actionMap.begin(); itit != d->actionMap.end(); ++itit ) { KKeyEntryMap::Iterator it = *itit; QListViewItem *at = itit.key(); //kdDebug(125) << QString( "allDefault: %1 3:%2 4:%3\n" ).arg(it.key()).arg((*it).aDefaultKeyCode).arg((*it).aDefaultKeyCode4); if ( (*it).bConfigurable ) { (*it).aCurrentKeyCode = (*it).aConfigKeyCode = - (useFourModifierKeys) ? (*it).aDefaultKeyCode4 : (*it).aDefaultKeyCode; + /*(useFourModifierKeys) ? (*it).aDefaultKeyCode4 :*/ (*it).aDefaultKeyCode; } at->setText(1, KAccel::keyToString((*it).aConfigKeyCode, true)); } emit keyChange(); } void KKeyChooser::setPreferFourModifierKeys( bool bPreferFourModifierKeys ) { d->bPreferFourModifierKeys = bPreferFourModifierKeys; } // These should be removed during the next clean-up. void KKeyChooser::shiftClicked() { } void KKeyChooser::ctrlClicked() { } void KKeyChooser::altClicked() { } void KKeyChooser::editKey() { } void KKeyChooser::editEnd() { } void KKeyChooser::changeKey() {} void KKeyChooser::capturedKey( uint key ) { if( KAccel::keyToString( key, true ).isEmpty() ) d->lInfo->setText( i18n("Undefined key") ); else setKey( key ); } void KKeyChooser::listSync() { QListViewItem *item = d->wList->firstChild(); while (item) { if( d->actionMap.contains( item ) ) { // ellis -- for labels KKeyEntryMap::Iterator it = d->actionMap[item]; if ( (*it).bConfigurable ) item->setText(1, KAccel::keyToString((*it).aConfigKeyCode, true)); } item = item->itemBelow(); } updateAction( d->wList->currentItem() ); } void KKeyChooser::setKey( int keyCode ) { QListViewItem *item = d->wList->currentItem(); if (!item || !d->actionMap.contains( item )) return; KKeyEntryMap::Iterator it = d->actionMap[item]; if( !d->bAllowMetaKey && (keyCode & (Qt::ALT<<1)) ) { QString s = i18n("The Meta key is not allowed in this context."); KMessageBox::sorry( this, s, i18n("Invalid Shortcut Key") ); return; } if( keyCode < 0x1000 && !d->bAllowLetterShortcuts ) { QString s = i18n( "In order to use the '%1' key as a shortcut,\n" "it must be combined with the\n" "Meta, Alt, Ctrl, and/or Shift keys." ).arg(QChar(keyCode)); KMessageBox::sorry( this, s, i18n("Invalid Shortcut Key") ); return; } // If key isn't already in use, if( !isKeyPresent( keyCode ) ) { // Set new key code (*it).aConfigKeyCode = keyCode; // Update display toChange(item); emit keyChange(); } } bool KKeyChooser::isKeyPresent( int kcode, bool warnuser ) { if (!kcode || !d->actionMap.contains( d->wList->currentItem() )) return false; // Search the global key codes to find if this keyCode is already used // elsewhere QDictIterator gIt( *d->globalDict ); gIt.toFirst(); while ( gIt.current() ) { if ( (*gIt.current()) == kcode && *gIt.current() != 0 ) { if (!warnuser) return true; QString actionName = gIt.currentKey(); actionName.stripWhiteSpace(); QString keyName = KAccel::keyToString( *gIt.current(), true ); QString str = i18n("The %1 key combination has already been " "allocated\n" "to the global %2 action.\n\n" "Please choose a unique key combination."). arg(keyName).arg(actionName); KMessageBox::sorry( this, str, i18n("Global key conflict")); return true; } ++gIt; } // Search the std key codes to find if this keyCode is already used // elsewhere QDictIterator sIt( *d->stdDict ); sIt.toFirst(); while ( sIt.current() ) { kdDebug(125) << "current " << sIt.currentKey() << ":" << *sIt.current() << " code " << kcode << endl; if ( *sIt.current() == kcode && *sIt.current() != 0 ) { QString actionName( (*d->map)[sIt.currentKey()].descr ); actionName.stripWhiteSpace(); QString keyName = KAccel::keyToString( *sIt.current(), true ); QString str = i18n("The %1 key combination has already " "been allocated\n" "to the standard %2 action.\n" "\n" "Please choose a unique key combination."). arg(keyName).arg(actionName); KMessageBox::sorry( this, str, i18n("Standard key conflict")); return true; } ++sIt; } // Search the aConfigKeyCodes to find if this keyCode is already used // elsewhere for (KKeyEntryMap::ConstIterator it = d->map->begin(); it != d->map->end(); ++it) { if ( it != d->actionMap[d->wList->currentItem()] && (*it).aConfigKeyCode == kcode ) { QString actionName( (*it).descr ); actionName.stripWhiteSpace(); QString keyName = KAccel::keyToString( kcode, true ); QString str = i18n("The %1 key combination has already " "been allocated\n" "to the %2 action.\n" "\n" "Please choose a unique key combination."). arg(keyName).arg(actionName); KMessageBox::sorry( this, str, i18n("Key conflict")); return true; } } return false; } QDict* KKeyChooser::globalDict() { return d->globalDict; } QDict* KKeyChooser::stdDict() { return d->stdDict; } #include "kkeydialog.moc" #include "kkeybutton.moc" Index: trunk/kdelibs/kdecore/kaccel.cpp =================================================================== --- trunk/kdelibs/kdecore/kaccel.cpp (revision 103063) +++ trunk/kdelibs/kdecore/kaccel.cpp (revision 103064) @@ -1,1160 +1,1156 @@ /* Copyright (C) 1998 Mark Donohoe Copyright (C) 1997-2000 Nicolas Hadacek Copyright (C) 1998 Matthias Ettrich 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include // For the X11-related static functions #define XK_MISCELLANY #define XK_XKB_KEYS #include #include #include #include //------------------------------------------------------------------- // BCI: KAccel doesn't have a destructor, so we need to delete it's 'd' another way. // So in this case, we'll make KAccelPrivate a child of QObject, and set it's QObject // parent to KAccel. class KAccelPrivate : public QObject { public: KKeyMapOrder aKeyMapOrder; // A list preserving the original insertItem order. KAccelPrivate( QObject *parent ) : QObject( parent ) { }; }; //------------------------------------------------------------------- KKey::KKey( const XEvent *pEvent ) { m_keyCombQt = KAccel::keyEventXToKeyQt( pEvent ); } KKey::KKey( const QKeyEvent *pEvent ) { m_keyCombQt = KAccel::keyEventQtToKeyQt( pEvent ); } KKey::KKey( const QString& keyStr ) { m_keyCombQt = KAccel::stringToKey( keyStr ); } QString KKey::toString() { return KAccel::keyToString( m_keyCombQt ); } void KKeyEntry::operator=(const KKeyEntry& e) { aCurrentKeyCode = e.aCurrentKeyCode; aDefaultKeyCode = e.aDefaultKeyCode; - aDefaultKeyCode4 = e.aDefaultKeyCode4; + //aDefaultKeyCode4 = e.aDefaultKeyCode4; aConfigKeyCode = e.aConfigKeyCode; bConfigurable = e.bConfigurable; bEnabled = e.bEnabled; aAccelId = e.aAccelId; receiver = e.receiver; member = e.member; descr = e.descr; menuId = e.menuId; menu = e.menu; - keyCodeNative = e.keyCodeNative; - keyModNative = e.keyModNative; } KKeyEntry::KKeyEntry() { aCurrentKeyCode = 0; aDefaultKeyCode = 0; - aDefaultKeyCode4 = 0; + //aDefaultKeyCode4 = 0; aConfigKeyCode = 0; bConfigurable = false; bEnabled = false; aAccelId = 0; receiver = 0; member = 0; menuId = 0; menu = 0; - keyCodeNative = 0; - keyModNative = 0; } KKeyEntry::KKeyEntry(const KKeyEntry& e) { *this = e; } KAccel::KAccel( QWidget * parent, const char *name ) : QAccel(parent, name) { aAvailableId = 1; bEnabled = true; aGroup = "Keys"; bGlobal = false; d = new KAccelPrivate( this ); // BCI: we'll need a destructor function too. } void KAccel::clear() { QAccel::clear(); aKeyMap.clear(); } void KAccel::connectItem( const QString& action, const QObject* receiver, const char *member, bool activate ) { if (action.isNull()) return; if (!aKeyMap.contains(action)) { kdWarning(125) << "cannot connect action " << action << " which is not in the object dictionary" << endl; return; } KKeyEntry entry = aKeyMap[action]; entry.receiver = receiver; entry.member = member; entry.aAccelId = aAvailableId; aKeyMap[action] = entry; // reassign aAvailableId++; // Qt does strange things if a QAccel contains a accelerator // with key code 0, so leave it out here. if (entry.aCurrentKeyCode) { QAccel::insertItem( entry.aCurrentKeyCode, entry.aAccelId ); QAccel::connectItem( entry.aAccelId, receiver, member ); } if ( !activate ) setItemEnabled( action, false ); } void KAccel::connectItem( KStdAccel::StdAccel accel, const QObject* receiver, const char *member, bool activate ) { QString action(KStdAccel::action(accel)); if (!action.isNull() && !aKeyMap.contains(action)) insertStdItem(accel); connectItem(action, receiver, member, activate); } uint KAccel::count() const { return aKeyMap.count(); } int KAccel::currentKey( const QString& action ) const { return aKeyMap[action].aCurrentKeyCode; } void KAccel::setDescription(const QString &action, const QString &description) { aKeyMap[action].descr = description; } QString KAccel::description( const QString& action ) const { return aKeyMap[action].descr; } int KAccel::defaultKey( const QString& action ) const { return aKeyMap[action].aDefaultKeyCode; } void KAccel::disconnectItem( const QString& action, const QObject* receiver, const char *member ) { if (aKeyMap.contains(action)) QAccel::disconnectItem( aKeyMap[action].aAccelId, receiver, member ); } QString KAccel::findKey( int key ) const { for (KKeyEntryMap::ConstIterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it) if ( key == (*it).aCurrentKeyCode ) return it.key(); return QString::null; } bool KAccel::insertItem( const QString& descr, const QString& action, int keyCode, bool configurable ) { return insertItem( descr, action, keyCode, keyCode, 0, 0, configurable); } bool KAccel::insertItem( const QString& descr, const QString& action, KKey defaultKeyCode3, KKey defaultKeyCode4, bool configurable ) { //kdDebug(125) << QString( "insertItem("+action+", %1, %2)\n" ).arg(defaultKeyCode3).arg(defaultKeyCode4); return insertItem( descr, action, defaultKeyCode3, defaultKeyCode4, 0, 0, configurable); } bool KAccel::insertItem( const QString& descr, const QString& action, KKey defaultKeyCode3, KKey defaultKeyCode4, int id, QPopupMenu *qmenu, bool configurable) { kdDebug(125) << QString( "insertItem("+action+", 0x%1, 0x%2)\n" ).arg(defaultKeyCode3.key(),0,16).arg(defaultKeyCode4.key(),0,16); if (aKeyMap.contains(action)) removeItem( action ); KKeyEntry entry; entry.aDefaultKeyCode = defaultKeyCode3.key(); - entry.aDefaultKeyCode4 = defaultKeyCode4.key(); - entry.aCurrentKeyCode = entry.aConfigKeyCode = useFourModifierKeys() ? defaultKeyCode4.key() : defaultKeyCode3.key(); + //entry.aDefaultKeyCode4 = defaultKeyCode4.key(); + entry.aCurrentKeyCode = /*entry.aConfigKeyCode = useFourModifierKeys() ? defaultKeyCode4.key() : */ defaultKeyCode3.key(); kdDebug(125) << "useFourModifierKeys() = " << useFourModifierKeys() << " entry.aCurrentKeyCode = " << entry.aCurrentKeyCode << endl; entry.bConfigurable = configurable; entry.descr = descr; entry.menuId = id; entry.menu = qmenu; entry.bEnabled = true; aKeyMap[action] = entry; // Hack to get ordering and labeling working in kcontrol -- this will be replaced as // soon as the 2.2beta testing phase is over. d->aKeyMapOrder[d->aKeyMapOrder.count()] = action; // Program:XXX and Group:XXX labels should be disabled. if( action.contains( ':' ) ) entry.bEnabled = false; return true; } bool KAccel::insertItem( const QString& descr, const QString& action, int keyCode, int id, QPopupMenu *qmenu, bool configurable) { return insertItem( descr, action, keyCode, keyCode, id, qmenu, configurable ); } bool KAccel::insertItem( const QString& descr, const QString& action, const QString& keyCode, bool configurable ) { int iKeyCode = stringToKey( keyCode ); return insertItem( descr, action, iKeyCode, configurable ); } bool KAccel::insertItem( const QString& descr, const QString& action, const QString& keyCode, int id, QPopupMenu *qmenu, bool configurable) { int iKeyCode = stringToKey( keyCode ); return insertItem( descr, action, iKeyCode, id, qmenu, configurable); } bool KAccel::insertItem( const QString& action, int keyCode, bool configurable ) { return insertItem(action, action, keyCode, configurable); } bool KAccel::insertItem( const QString& action, int keyCode, int id, QPopupMenu *qmenu, bool configurable) { return insertItem(action, action, keyCode, id, qmenu, configurable); } void KAccel::changeMenuAccel ( QPopupMenu *menu, int id, const QString& action ) { QString s = menu->text( id ); if ( s.isNull() ) return; if (action.isNull()) return; int i = s.find('\t'); QString k = keyToString( currentKey( action), true ); if( k.isNull() ) return; if ( i >= 0 ) s.replace( i+1, s.length()-i, k ); else { s += '\t'; s += k; } QPixmap *pp = menu->pixmap(id); if(pp && !pp->isNull()) menu->changeItem( *pp, s, id ); else menu->changeItem( s, id ); } void KAccel::changeMenuAccel( QPopupMenu *menu, int id, KStdAccel::StdAccel accel ) { changeMenuAccel(menu, id, KStdAccel::action(accel)); } bool KAccel::insertStdItem( KStdAccel::StdAccel id, const QString& descr ) { return insertItem(descr.isNull() ? KStdAccel::description(id) : descr, KStdAccel::action(id), KStdAccel::key(id), false ); } bool KAccel::isEnabled() const { return bEnabled; } bool KAccel::isItemEnabled( const QString& action ) const { return aKeyMap[action].bEnabled; } KKeyEntryMap KAccel::keyDict() const { return aKeyMap; } const KKeyMapOrder& KAccel::keyInsertOrder() const { return d->aKeyMapOrder; } KKeyMapOrder& KAccel::keyInsertOrder() { return d->aKeyMapOrder; } void KAccel::readSettings( KConfig* config ) { kdDebug(125) << "KAccel::readSettings(...)\n"; readKeyMap( aKeyMap, aGroup, config ); for (KKeyEntryMap::Iterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it) { if ( (*it).aAccelId && (*it).aCurrentKeyCode ) { kdDebug(125) << "insert " << (*it).descr << " " << (*it).bEnabled << endl; QAccel::disconnectItem( (*it).aAccelId, (*it).receiver, (*it).member ); QAccel::removeItem( (*it).aAccelId ); QAccel::insertItem( (*it).aCurrentKeyCode, (*it).aAccelId ); QAccel::connectItem( (*it).aAccelId, (*it).receiver, (*it).member); } if ( (*it).menu ) changeMenuAccel((*it).menu, (*it).menuId, it.key()); } emit keycodeChanged(); } void KAccel::readKeyMap( KKeyEntryMap &map, const QString& group, KConfigBase *config ) { QString s; KConfigBase *pConfig = config ? config : KGlobal::config(); KConfigGroupSaver cgs(pConfig, group); for (KKeyEntryMap::Iterator it = map.begin(); it != map.end(); ++it) { s = pConfig->readEntry(it.key()); if ( s.isNull() || s.startsWith( "default" )) - (*it).aConfigKeyCode = useFourModifierKeys() ? (*it).aDefaultKeyCode4 : (*it).aDefaultKeyCode; + (*it).aConfigKeyCode = /*useFourModifierKeys() ? (*it).aDefaultKeyCode4 :*/ (*it).aDefaultKeyCode; else if( s == "none" ) (*it).aConfigKeyCode = 0; else (*it).aConfigKeyCode = stringToKey( s ); (*it).aCurrentKeyCode = (*it).aConfigKeyCode; } } void KAccel::removeItem( const QString& action ) { if (!aKeyMap.contains(action)) return; KKeyEntry entry = aKeyMap[ action ]; if ( entry.aAccelId ) { QAccel::disconnectItem( entry.aAccelId, entry.receiver, entry.member); QAccel::removeItem( entry.aAccelId ); } aKeyMap.remove( action ); } void KAccel::setEnabled( bool activate ) { kdDebug(125) << "setEnabled " << activate << endl; for (KKeyEntryMap::ConstIterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it) setItemEnabled( it.key(), activate ); bEnabled = activate; } void KAccel::setItemEnabled( const QString& action, bool activate ) { QAccel::setItemEnabled( aKeyMap[action].aAccelId, activate ); aKeyMap[action].bEnabled = activate; } bool KAccel::setKeyDict( const KKeyEntryMap& nKeyDict ) { kdDebug(125) << "Disconnect and remove" << endl; // Disconnect and remove all items in pAccel for (KKeyEntryMap::ConstIterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it) { QString s; if ( (*it).aAccelId && (*it).aCurrentKeyCode ) { QAccel::disconnectItem( (*it).aAccelId, (*it).receiver, (*it).member ); QAccel::removeItem( (*it).aAccelId ); } } // Clear the dictionary aKeyMap = nKeyDict; kdDebug(125) << "Insert new items" << endl; // Insert the new items into the dictionary and reconnect if neccessary // Note also swap config and current key codes !!!!!! for (KKeyEntryMap::Iterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it) { // Note we write config key code to current key code !! (*it).aCurrentKeyCode = (*it).aConfigKeyCode; if ( (*it).aAccelId && (*it).aCurrentKeyCode ) { kdDebug(125) << "insert " << (*it).descr << " " << (*it).bEnabled << endl; QAccel::insertItem( (*it).aCurrentKeyCode, (*it).aAccelId ); QAccel::setItemEnabled( (*it).aAccelId, (*it).bEnabled ); QAccel::connectItem( (*it).aAccelId, (*it).receiver, (*it).member); } if ( (*it).menu ) { changeMenuAccel((*it).menu, (*it).menuId, it.key()); } } emit keycodeChanged(); return true; } void KAccel::setConfigGroup( const QString& group ) { aGroup = group; } void KAccel::setConfigGlobal( bool global ) { bGlobal = global; } QString KAccel::configGroup() const { return aGroup; } bool KAccel::configGlobal() const { return bGlobal; } void KAccel::writeKeyMap( const KKeyEntryMap &map, const QString &group, KConfig *config, bool global ) { KConfig *pConfig = config ? config : KGlobal::config(); KConfigGroupSaver cs(pConfig, group); for (KKeyEntryMap::ConstIterator it = map.begin(); it != map.end(); ++it) { if ( (*it).bConfigurable ) { kdDebug(125) << "writing " << KAccel::keyToString( (*it).aCurrentKeyCode, false) << " " << KAccel::keyToString( (*it).aConfigKeyCode, false) << endl; if ( ( *it ).aConfigKeyCode != ( *it ).aDefaultKeyCode ) { QString keyStr = (*it).aConfigKeyCode ? KAccel::keyToString( (*it).aConfigKeyCode, false ) : "none"; pConfig->writeEntry( it.key(), keyStr, true, global ); } else { // global ones must be written if ( global || !pConfig->readEntry( it.key() ).isNull() ) pConfig->writeEntry( it.key(), QString( "default(%1)" ) .arg( KAccel::keyToString( (*it).aDefaultKeyCode, false )), true, global ); } } } pConfig->sync(); } void KAccel::writeSettings(KConfig* config) const { writeKeyMap( aKeyMap, aGroup, config, bGlobal ); } bool KAccel::configurable( const QString &action ) const { return aKeyMap[action].bConfigurable; } void KAccel::clearItem(const QString &action) { if (!aKeyMap.contains(action)) return; KKeyEntry entry = aKeyMap[ action ]; if ( entry.aAccelId && entry.bConfigurable) { QAccel::disconnectItem( entry.aAccelId, entry.receiver, entry.member); QAccel::removeItem( entry.aAccelId ); entry.aAccelId = 0; entry.aCurrentKeyCode = 0; aKeyMap[action] = entry; // reassign if ( entry.menu ) { changeMenuAccel(entry.menu, entry.menuId, action); } } } bool KAccel::updateItem( const QString &action, int keyCode) { if (!aKeyMap.contains(action)) return false; KKeyEntry entry = aKeyMap[ action ]; if ( entry.aCurrentKeyCode==keyCode ) return true; if ( entry.aAccelId ) { QAccel::disconnectItem( entry.aAccelId, entry.receiver, entry.member); QAccel::removeItem( entry.aAccelId ); } else { entry.aAccelId = aKeyMap[action].aAccelId = aAvailableId; aAvailableId++; } aKeyMap[action].aCurrentKeyCode = keyCode; if (keyCode) { QAccel::insertItem( keyCode, entry.aAccelId ); QAccel::connectItem( entry.aAccelId, entry.receiver, entry.member ); } emit keycodeChanged(); return true; } void KAccel::removeDeletedMenu(QPopupMenu *menu) { for (KKeyEntryMap::Iterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it) if ( (*it).menu == menu ) (*it).menu = 0; } // Indicate whether to default to the 3- or 4- modifier keyboard schemes // This variable should also be moved into a class along with the // X11-related key functions below. static int g_bUseFourModifierKeys = -1; bool KAccel::useFourModifierKeys() { if( g_bUseFourModifierKeys == -1 ) { // Read in whether to use 4 modifier keys KConfigGroupSaver cgs( KGlobal::config(), "Keyboard Layout" ); bool fourMods = KGlobal::config()->readBoolEntry( "Use Four Modifier Keys", false ); g_bUseFourModifierKeys = fourMods && keyboardHasMetaKey(); } return g_bUseFourModifierKeys == 1; } void KAccel::useFourModifierKeys( bool b ) { if( g_bUseFourModifierKeys != (int)b ) { g_bUseFourModifierKeys = b && keyboardHasMetaKey(); // If we're 'turning off' the meta key or, if we're turning it on, // the keyboard must actually have a meta key. if( b && !keyboardHasMetaKey() ) kdDebug(125) << "Tried to use four modifier keys on a keyboard layout without a Meta key.\n"; } KConfigGroupSaver cgs( KGlobal::config(), "Keyboard Layout" ); KGlobal::config()->writeEntry( "Use Four Modifier Keys", g_bUseFourModifierKeys, true, true); kdDebug(125) << "bUseFourModifierKeys = " << g_bUseFourModifierKeys << endl; } bool KAccel::qtSupportsMetaKey() { static int qtSupport = -1; if( qtSupport == -1 ) { qtSupport = QAccel::stringToKey("Meta+A") & (Qt::ALT<<1); kdDebug(125) << "Qt Supports Meta Key: " << qtSupport << endl; } return qtSupport == 1; } /*****************************************************************************/ struct ModKeyXQt { static bool bInitialized; const char *keyName; uint keyModMaskQt; uint keyModMaskX; }; bool ModKeyXQt::bInitialized = false; struct TransKey { uint keySymQt; uint keySymX; }; static ModKeyXQt g_aModKeys[] = { { "Shift", Qt::SHIFT, ShiftMask }, { "CapsLock", 0, LockMask }, { "Ctrl", Qt::CTRL, ControlMask }, { "Alt", Qt::ALT, Mod1Mask }, { "NumLock", 0, Mod2Mask }, { "ModeSwitch", 0, Mod3Mask }, { "Meta", (Qt::ALT<<1), Mod4Mask }, { "ScrollLock", 0, Mod5Mask }, { 0, 0, 0 } }; static const TransKey g_aTransKeySyms[] = { { Qt::Key_Backspace, XK_BackSpace }, { Qt::Key_Backtab, XK_ISO_Left_Tab }, { Qt::Key_Enter, XK_KP_Enter }, { Qt::Key_SysReq, XK_Sys_Req }, { Qt::Key_CapsLock, XK_Caps_Lock }, { Qt::Key_NumLock, XK_Num_Lock }, { Qt::Key_ScrollLock, XK_Scroll_Lock } }; void KAccel::readModifierMapping() { XModifierKeymap* xmk = XGetModifierMapping( qt_xdisplay() ); for( int i = Mod2MapIndex; i < 8; i++ ) g_aModKeys[i].keyModMaskX = 0; // Qt assumes that Alt is always Mod1Mask, so start at Mod2Mask. for( int i = Mod2MapIndex; i < 8; i++ ) { uint keySymX = XKeycodeToKeysym( qt_xdisplay(), xmk->modifiermap[xmk->max_keypermod * i], 0 ); int j = -1; switch( keySymX ) { //case XK_Alt_L: //case XK_Alt_R: j = 3; break; // Normally Mod1Mask case XK_Num_Lock: j = 4; break; // Normally Mod2Mask case XK_Mode_switch: j = 5; break; // Normally Mod3Mask case XK_Meta_L: case XK_Meta_R: j = 6; break; // Normally Mod4Mask case XK_Scroll_Lock: j = 7; break; // Normally Mod5Mask } if( j >= 0 ) { g_aModKeys[j].keyModMaskX = (1< 0, 2 & 3 => 2 uint keySymX0 = XKeycodeToKeysym( qt_xdisplay(), keyCodeX, indexUnshifted ), keySymX1 = XKeycodeToKeysym( qt_xdisplay(), keyCodeX, indexUnshifted+1 ); QString s0 = XKeysymToString( keySymX0 ), s1 = XKeysymToString( keySymX1 ); // If shifted value is not the same as unshifted, // then we shouldn't print Shift. if( s0.lower() != s1.lower() ) { keyModQt &= ~Qt::SHIFT; keySymX = keySymX1; } } // Search for modifier flags. for( int i = MOD_KEYS-1; i >= 0; i-- ) { if( keyModQt & g_aModKeys[i].keyModMaskQt ) { keyModStr += (bi18n) ? i18n(g_aModKeys[i].keyName) : QString(g_aModKeys[i].keyName); keyModStr += "+"; } } } keyStr = "Unknown"; // Determine name of primary key. // If printable, non-space unicode character, // then display it directly instead of by name // (e.g. '!' instead of 'Exclam') // UNLESS we're not wanting internationalization. Then all // keys should be printed with their ASCII-7 name. if( bi18n && keySymQt < 0x1000 && QChar(keySymQt).isPrint() && !QChar(keySymQt).isSpace() ) keyStr = QChar(keySymQt); else { for( int i = 0; i < NB_KEYS; i++ ) { if( keySymQt == (uint) KKEYS[i].code ) { keyStr = KKEYS[i].name; break; } } } } return !keyStr.isEmpty() ? (keyModStr + keyStr) : QString::null; } int KAccel::stringToKey(const QString& key) { QString keyStr = key; if( key == "default" ) // old code used to write just "default" return 0; // which is not enough if( key.startsWith( "default(" )) { int pos = key.findRev( ')' ); if( pos >= 0 ) // this should be really done with regexp keyStr = key.mid( 8, pos - 8 ); } kdDebug(125) << QString("stringToKey("+key+") = %1\n").arg(stringToKey( keyStr, 0, 0, 0 ), 0, 16); return stringToKey( keyStr, 0, 0, 0 ); } // Return value is Qt key code. uint KAccel::stringToKey( const QString& keyStr, unsigned char *pKeyCodeX, uint *pKeySymX, uint *pKeyModX ) { uint keySymX = 0; unsigned char keyCodeX = 0; uint keyModX = 0; uint keyCombQt = 0; QString sKeySym; QChar c; // Initialize if( pKeySymX ) *pKeySymX = 0; if( pKeyCodeX ) *pKeyCodeX = 0; if( pKeyModX ) *pKeyModX = 0; if( keyStr.isNull() || keyStr.isEmpty() ) return 0; int iOffset = 0, iOffsetToken; do { int i; // Find next token. iOffsetToken = keyStr.find( '+', iOffset ); // If no more '+'s are found, set to end of string. if( iOffsetToken < 0 ) iOffsetToken = keyStr.length(); // Allow a '+' to be the keysym if it's the last character. else if( iOffsetToken == iOffset && iOffset + 1 == (int)keyStr.length() ) iOffsetToken++; sKeySym = keyStr.mid( iOffset, iOffsetToken - iOffset ).stripWhiteSpace(); iOffset = iOffsetToken + 1; // Check if this is a modifier key (Shift, Ctrl, Alt, Meta). for( i = 0; i < MOD_KEYS; i++ ) { if( g_aModKeys[i].keyModMaskQt && stricmp( sKeySym.ascii(), g_aModKeys[i].keyName ) == 0 ) { // If there is no X mod flag defined for this key, // then abort. Ex: Meta+F1, but X hasn't assigned Meta. if( g_aModKeys[i].keyModMaskX == 0 ) return 0; keyCombQt |= g_aModKeys[i].keyModMaskQt; keyModX |= g_aModKeys[i].keyModMaskX; break; } } // If this was not a modifier key, // search for 'normal' key. if( i == MOD_KEYS ) { // Abort if already found primary key. if( !c.isNull() || keySymX ) { c = QChar::null; keySymX = keyModX = keyCombQt = 0; break; } //if( keySymX ) { kdWarning(125) << "keystrToKey: Tried to set more than one key in key code." << endl; return 0; } if( sKeySym.length() == 1 ) c = sKeySym[0]; else { // Search for Qt keycode for( i = 0; i < NB_KEYS; i++ ) { if( stricmp( sKeySym.ascii(), KKEYS[i].name ) == 0 ) { keyCombQt |= KKEYS[i].code; keyQtToKeyX( KKEYS[i].code, 0, &keySymX, 0 ); if( KKEYS[i].code < 0x1000 && QChar(KKEYS[i].code).isLetter() ) c = KKEYS[i].code; break; } } //if( i == NB_KEYS ) { kdWarning(125) << "keystrToKey: Unknown key name " << sKeySym << endl; return 0; } if( i == NB_KEYS ) { c = QChar::null; keySymX = keyModX = keyCombQt = 0; break; } } } } while( (uint)iOffsetToken < keyStr.length() ); if( !c.isNull() ) { if( c.isLetter() && !(keyModX & ShiftMask) ) c = c.lower(); keySymX = c.unicode(); // For some reason, Qt always wants 'a-z' as 'A-Z'. if( c >= 'a' && c <= 'z' ) c = c.upper(); keyCombQt |= c.unicode(); } if( keySymX ) { // Find X key code (code of key sent from keyboard) keyCodeX = XKeysymToKeycode( qt_xdisplay(), keySymX ); // If 'Shift' has been explicitly give, i.e. 'Shift+1', if( keyModX & ShiftMask ) { int index = keySymXIndex( keySymX ); // But symbol given is unshifted, i.e. '1' if( index == 0 || index == 2 ) { keySymX = XKeycodeToKeysym( qt_xdisplay(), keyCodeX, index+1 ); keyCombQt = keySymXToKeyQt( keySymX, keyModX ); } } // If keySym requires Shift or ModeSwitch to activate, // then add the flags. keySymXMods( keySymX, &keyCombQt, &keyModX ); } if( pKeySymX ) *pKeySymX = keySymX; if( pKeyCodeX ) *pKeyCodeX = keyCodeX; if( pKeyModX ) *pKeyModX = keyModX; return keyCombQt; } uint KAccel::keyCodeXToKeySymX( uchar keyCodeX, uint keyModX ) { // I don't know where it's documented, but Mode_shift sometimes sets the 13th bit in 'state'. int index = ((keyModX & ShiftMask) ? 1 : 0) + ((keyModX & (0x2000 | keyModXModeSwitch())) ? 2 : 0); return XKeycodeToKeysym( qt_xdisplay(), keyCodeX, index ); } void KAccel::keyEventXToKeyX( const XEvent *pEvent, uchar *pKeyCodeX, uint *pKeySymX, uint *pKeyModX ) { if( pKeyCodeX ) *pKeyCodeX = pEvent->xkey.keycode; if( pKeySymX ) *pKeySymX = keyCodeXToKeySymX( pEvent->xkey.keycode, pEvent->xkey.state ); if( pKeyModX ) *pKeyModX = pEvent->xkey.state; } uint KAccel::keyEventXToKeyQt( const XEvent *pEvent ) { uint keySymX, keyModX; keyEventXToKeyX( pEvent, 0, &keySymX, &keyModX ); return keySymXToKeyQt( keySymX, keyModX ); } int KAccel::keySymXIndex( uint keySym ) { unsigned char keyCode = XKeysymToKeycode( qt_xdisplay(), keySym ); if( keyCode ) { for( int i = 0; i < 4; i++ ) { uint keySym2 = XKeycodeToKeysym( qt_xdisplay(), keyCode, i ); if( keySym == keySym2 ) return i; } } return -1; } void KAccel::keySymXMods( uint keySym, uint *pKeyModQt, uint *pKeyModX ) { uint keyModQt = 0, keyModX = 0; int i = keySymXIndex( keySym ); if( i == 1 || i == 3 ) { keyModQt |= Qt::SHIFT; keyModX |= ShiftMask; } if( i == 2 || i == 3 ) { keyModX |= keyModXModeSwitch(); } if( pKeyModQt ) *pKeyModQt |= keyModQt; if( pKeyModX ) *pKeyModX |= keyModX; } uint KAccel::keyCodeXToKeyQt( uchar keyCodeX, uint keyModX ) { return keySymXToKeyQt( keyCodeXToKeySymX( keyCodeX, keyModX ), keyModX ); } uint KAccel::keySymXToKeyQt( uint keySymX, uint keyModX ) { uint keyCombQt = 0; // Qt's own key definitions begin at 0x1000 if( keySymX < 0x1000 ) { // For some reason, Qt wants 'a-z' converted to 'A-Z' if( keySymX >= 'a' && keySymX <= 'z' ) keyCombQt = toupper( keySymX ); else keyCombQt = keySymX; } if( !keyCombQt ) { // Find name of key, and assign its code to keyCombQt. const char *psKeySym = XKeysymToString( keySymX ); for( int i = 0; i < NB_KEYS; i++ ) { if( stricmp( psKeySym, KKEYS[i].name ) == 0 ) { keyCombQt = KKEYS[i].code; break; } } } if( !keyCombQt ) { for( uint i = 0; i < sizeof(g_aTransKeySyms)/sizeof(TransKey); i++ ) { if( keySymX == g_aTransKeySyms[i].keySymX ) { keyCombQt = g_aTransKeySyms[i].keySymQt; break; } } } if( keyCombQt ) { // Get Qt modifier flags for( int i = 0; i < MOD_KEYS; i++ ) { if( keyModX & g_aModKeys[i].keyModMaskX ) keyCombQt |= g_aModKeys[i].keyModMaskQt; } } return keyCombQt; } void KAccel::keyQtToKeyX( uint keyCombQt, unsigned char *pKeyCodeX, uint *pKeySymX, uint *pKeyModX ) { uint keySymQt; uint keySymX = 0; unsigned char keyCodeX = 0; uint keyModX = 0; const char *psKeySym = 0; // Get code of just the primary key keySymQt = keyCombQt & 0xffff; // If unicode value beneath 0x1000 (special Qt codes begin thereafter), if( keySymQt < 0x1000 ) { // For reasons unbeknownst to me, Qt converts 'a-z' to 'A-Z'. // So convert it back to lowercase if SHIFT isn't held down. if( keySymQt >= Qt::Key_A && keySymQt <= Qt::Key_Z && !(keyCombQt & Qt::SHIFT) ) keySymQt = tolower( keySymQt ); keySymX = keySymQt; } // Else, special key (e.g. Delete, F1, etc.) else { for( int i = 0; i < NB_KEYS; i++ ) { if( keySymQt == (uint) KKEYS[i].code ) { psKeySym = KKEYS[i].name; //kdDebug(125) << " symbol found: \"" << psKeySym << "\"" << endl; break; } } // Get X key symbol. Only works if Qt name is same as X name. if( psKeySym ) { QString sKeySym = psKeySym; // Check for lower-case equalent first because most // X11 names are all lower-case. keySymX = XStringToKeysym( sKeySym.lower().ascii() ); if( keySymX == 0 ) keySymX = XStringToKeysym( psKeySym ); } if( keySymX == 0 ) { for( uint i = 0; i < sizeof(g_aTransKeySyms)/sizeof(TransKey); i++ ) { if( keySymQt == g_aTransKeySyms[i].keySymQt ) { keySymX = g_aTransKeySyms[i].keySymX; break; } } } } if( keySymX != 0 ) { // Get X keyboard code keyCodeX = XKeysymToKeycode( qt_xdisplay(), keySymX ); // Add ModeSwitch modifier bit, if necessary keySymXMods( keySymX, 0, &keyModX ); // Get X modifier flags for( int i = 0; i < MOD_KEYS; i++ ) { if( keyCombQt & g_aModKeys[i].keyModMaskQt ) { if( g_aModKeys[i].keyModMaskX ) keyModX |= g_aModKeys[i].keyModMaskX; // Qt key calls for a modifier which the current // X modifier map doesn't support. else { keySymX = 0; keyCodeX = 0; keyModX = 0; break; } } } } if( pKeySymX ) *pKeySymX = keySymX; if( pKeyCodeX ) *pKeyCodeX = keyCodeX; if( pKeyModX ) *pKeyModX = keyModX; } uint KAccel::keyEventQtToKeyQt( const QKeyEvent* pke ) { uint keyCombQt; // Set the modifier bits. keyCombQt = (pke->state() & Qt::KeyButtonMask) * (Qt::SHIFT / Qt::ShiftButton); if( pke->key() ) keyCombQt |= pke->key(); // If key() == 0, then it may be a compose character, so we need to // look at text() instead. else { QChar c = pke->text()[0]; // Looks like Qt allows unicode character up to 0x0fff. if( pke->text().length() == 1 && c.unicode() < 0x1000 ) keyCombQt |= c.unicode(); else keyCombQt |= Qt::Key_unknown; } return keyCombQt; } QString KAccel::keyCodeXToString( uchar keyCodeX, uint keyModX, bool bi18n ) { return keyToString( keyCodeXToKeyQt( keyCodeX, keyModX ), bi18n ); } QString KAccel::keySymXToString( uint keySymX, uint keyModX, bool bi18n ) { return keyToString( keySymXToKeyQt( keySymX, keyModX ), bi18n ); } uint KAccel::keyModXShift() { return ShiftMask; } uint KAccel::keyModXLock() { return LockMask; } uint KAccel::keyModXCtrl() { return ControlMask; } uint KAccel::keyModXAlt() { return g_aModKeys[ModAltIndex].keyModMaskX; } uint KAccel::keyModXNumLock() { if( !ModKeyXQt::bInitialized ) KAccel::readModifierMapping(); return g_aModKeys[ModNumLockIndex].keyModMaskX; } uint KAccel::keyModXModeSwitch() { if( !ModKeyXQt::bInitialized ) KAccel::readModifierMapping(); return g_aModKeys[ModModeSwitchIndex].keyModMaskX; } uint KAccel::keyModXMeta() { if( !ModKeyXQt::bInitialized ) KAccel::readModifierMapping(); return g_aModKeys[ModMetaIndex].keyModMaskX; } uint KAccel::keyModXScrollLock() { if( !ModKeyXQt::bInitialized ) KAccel::readModifierMapping(); return g_aModKeys[ModScrollLockIndex].keyModMaskX; } uint KAccel::accelModMaskQt() { return Qt::SHIFT | Qt::CTRL | Qt::ALT | (Qt::ALT<<1); } uint KAccel::accelModMaskX() { return ShiftMask | ControlMask | keyModXAlt() | keyModXMeta(); } bool KAccel::keyboardHasMetaKey() { if( !ModKeyXQt::bInitialized ) KAccel::readModifierMapping(); return keyModXMeta() != 0; } #include "kaccel.moc" Index: trunk/kdelibs/kdecore/kaccel.h =================================================================== --- trunk/kdelibs/kdecore/kaccel.h (revision 103063) +++ trunk/kdelibs/kdecore/kaccel.h (revision 103064) @@ -1,631 +1,630 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Mark Donohoe Copyright (C) 1997-2000 Nicolas Hadacek Copyright (C) 1998 Matthias Ettrich 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _KACCEL_H #define _KACCEL_H #include #include #include #include class QPopupMenu; class KConfig; class KConfigBase; class QObject; class QWidget; class KAccelPrivate; class KKey { protected: uint m_keyCombQt; public: KKey() { m_keyCombQt = 0; } KKey( const KKey& k ) { m_keyCombQt = k.m_keyCombQt; } KKey( uint keyCombQt ) { m_keyCombQt = keyCombQt; } KKey( const XEvent * ); KKey( const QKeyEvent * ); KKey( const QString& ); KKey& operator =( KKey k ) { m_keyCombQt = k.m_keyCombQt; return *this; } KKey& operator =( uint keyCombQt ) { m_keyCombQt = keyCombQt; return *this; } uint key() const { return m_keyCombQt; } uint sym() const { return m_keyCombQt & 0xffff; } uint mod() const { return m_keyCombQt & ~0xffff; } uint state() const { return mod() >> 18; } QString toString(); }; /** * Accelerator information, similar to an action. * * It is used internally by @ref KAccel. * @internal */ struct KKeyEntry { public: int aCurrentKeyCode; int aDefaultKeyCode; // For keyboards with no meta key - int aDefaultKeyCode4; // For keyboards with meta key (4 modifiers) + //int aDefaultKeyCode4; // For keyboards with meta key (4 modifiers) int aConfigKeyCode; bool bConfigurable; bool bEnabled; int aAccelId; const QObject *receiver; const char *member; QString descr; int menuId; QPopupMenu *menu; - uint keyCodeNative, keyModNative; // For storing the X11 codes (for global shortcuts) void operator=(const KKeyEntry& e); KKeyEntry(); KKeyEntry(const KKeyEntry& e); }; typedef QMap KKeyEntryMap; typedef QMap KKeyMapOrder; /** * Handle keyboard accelerators. * * Allow an user to configure * key bindings through application configuration files or through the * @ref KKeyChooser GUI. * * A @ref KAccel contains a list of accelerator items. Each accelerator item * consists of an action name and a keyboard code combined with modifiers * (Shift, Ctrl and Alt.) * * For example, "Ctrl+P" could be a shortcut for printing a document. The key * codes are listed in ckey.h. "Print" could be the action name for printing. * The action name identifies the key binding in configuration files and the * @ref KKeyChooser GUI. * * When pressed, an accelerator key calls the slot to which it has been * connected. Accelerator items can be connected so that a key will activate * two different slots. * * A KAccel object handles key events sent to its parent widget and to all * children of this parent widget. * * Key binding reconfiguration during run time can be prevented by specifying * that an accelerator item is not configurable when it is inserted. A special * group of non-configurable key bindings are known as the * standard accelerators. * * The standard accelerators appear repeatedly in applications for * standard document actions such as printing and saving. Convenience methods are * available to insert and connect these accelerators which are configurable on * a desktop-wide basis. * * It is possible for a user to choose to have no key associated with * an action. * * The translated first argument for @ref insertItem() is used only * in the configuration dialog. *
  * KAccel *a = new KAccel( myWindow );
  * // Insert an action "Scroll Up" which is associated with the "Up" key:
  * a->insertItem( i18n("Scroll up"), "Scroll Up", "Up" );
  * // Insert an action "Scroll Down" which is not associated with any key:
  * a->insertItem( i18n("Scroll down"), "Scroll Down", 0);
  * a->connectItem( "Scroll up", myWindow, SLOT( scrollUp() ) );
  * // a->insertStdItem( KStdAccel::Print ); //not necessary, since it
  *	// is done automatially with the
  *	// connect below!
  * a->connectItem(KStdAccel::Print, myWindow, SLOT( printDoc() ) );
  *
  * a->readSettings();
  *
* * If a shortcut has a menu entry as well, you could insert them like * this. The example is again the @ref KStdAccel::Print from above. * *
  * int id;
  * id = popup->insertItem("&Print",this, SLOT(printDoc()));
  * a->changeMenuAccel(popup, id, KStdAccel::Print );
  * 
* * If you want a somewhat "exotic" name for your standard print action, like * id = popup->insertItem(i18n("Print &Document"),this, SLOT(printDoc())); * it might be a good idea to insert the standard action before as * a->insertStdItem( KStdAccel::Print, i18n("Print Document") ) * as well, so that the user can easily find the corresponding function. * * This technique works for other actions as well. Your "scroll up" function * in a menu could be done with * *
  *    id = popup->insertItem(i18n"Scroll &up",this, SLOT(scrollUp()));
  *    a->changeMenuAccel(popup, id, "Scroll Up" );
  * 
* * Please keep the order right: First insert all functions in the * acceleratior, then call a -> @ref readSettings() and @em then build your * menu structure. * * @short Configurable key binding support. * @version $Id$ */ class KAccel : public QAccel { Q_OBJECT public: /** * Create a KAccel object with a parent widget and a name. */ KAccel( QWidget * parent, const char *name = 0 ); /** * Remove all accelerator items. */ void clear(); /** * Connect an accelerator item to a slot/signal in another object. * * Arguments: * * @param action The accelerator item action name. * @param receiver The object to receive a signal. * @param member A slot or signal in the receiver. * @param activate Indicates whether the accelerator item should be * enabled immediately. */ void connectItem( const QString& action, const QObject* receiver, const char *member, bool activate = true ); /** * Same as the preceding @ref connectItem(), but used for standard * accelerators. * * If the standard accelerator was not inserted so far, it * will be inserted automatically. */ void connectItem( KStdAccel::StdAccel accel, const QObject* receiver, const char *member, bool activate = true ); /** * Retrieve the number of accelerator items. */ uint count() const; /** * Retrieve the key code of the accelerator item with the action name * @p action, or zero if either the action name cannot be * found or the current key is set to no key. */ int currentKey( const QString& action ) const; /** * Retrieve the description of the accelerator item with the * action name @p action, or @ref QString::null if the action name cannot * be found. Useful for menus. */ QString description( const QString& action ) const; void setDescription(const QString &action, const QString &description); /** * Retrieve the default key code of the accelerator item with * the action name * @p action, or zero if the action name cannot be found. */ int defaultKey( const QString& action) const; /** * Disconnect an accelerator item from a function in another object. */ void disconnectItem( const QString& action, const QObject* receiver, const char *member ); /** * Rerieve the identifier of the accelerator item with the keycode @p key, * or @ref QString::null if the item cannot be found. */ QString findKey( int key ) const; /** * Insert an accelerator item. * * If an action already exists the old association and connections * will be removed. * * @param descr The localized name of the action, useful in * menus or the keybinding editor. * @param action The internal accelerator item action name. It * is supposed to be the same for all languages. * @param defaultKeyCode A key code to be used as the default * for the action. Set it to 0 for no default key (It still * may be configured later.) * @param configurable Indicates whether a user can configure * the key binding using the @ref KKeyChooser GUI and whether the * key will be written back to configuration files when * @ref writeSettings() is called. * @return @p true if successful. */ bool insertItem( const QString& descr, const QString& action, int defaultKeyCode, bool configurable = true ); bool insertItem( const QString& descr, const QString& action, KKey defaultKeyCode3, KKey defaultKeyCode4, bool configurable = true ); /** * Insert an accelerator item. * * If an action already exists the old association and connections * will be removed. * * @param descr The localized name of the action, useful in * menus or the keybinding editor. * @param action The internal accelerator item action name. It * is supposed to be the same for all languages. * @param defaultKeyCode A key code to be used as the default * for the action. Set it to 0 for no default key (It still * may be configured later.) * @param id Menu index of menu items associated with this action. * @param qmenu Menu containing items associated with this action. * @param configurable Indicates whether a user can configure * the key binding using the @ref KKeyChooser GUI and whether the * key will be written back to configuration files when * @ref writeSettings() is called. * @return @p true if successful. * */ bool insertItem( const QString& descr, const QString& action, int defaultKeyCode, int id, QPopupMenu *qmenu, bool configurable = true ); bool insertItem( const QString& descr, const QString& action, KKey defaultKeyCode3, KKey defaultKeyCode4, int id, QPopupMenu *qmenu, bool configurable = true ); /** * Insert an accelerator item. * * If an action already exists the old association and connections * will be removed. * * @param descr The localized name of the action, useful in * menus or the keybinding editor. * @param action The internal accelerator item action name. It * is supposed to be the same for all languages. * @param defaultKeyCode A key plus a combination of Shift, Ctrl * and Alt to be used as the default for the action. * @param id Menu index of menu items associated with this action. * @param qmenu Menu containing items associated with this action. * @param configurable Indicates whether a user can configure * the key binding using the @ref KKeyChooser GUI and whether the * key will be written back to configuration files when * @ref writeSettings() is called. * @return @p true if successful. * */ bool insertItem( const QString& descr, const QString& action, const QString& defaultKeyCode, bool configurable = true ); /** * Insert an accelerator item. * * If an action already exists the old association and connections * will be removed.. * * @param descr The localized name of the action, useful in * menus or the keybinding editor. * @param action The internal accelerator item action name. It * is supposed to be the same for all languages. * @param defaultKeyCode A key plus a combination of Shift, Ctrl * and Alt to be used as the default for the action. * @param configurable Indicates whether a user can configure * the key binding using the @ref KKeyChooser GUI and whether the * key will be written back to configuration files when * @ref writeSettings() is called. * @return @p true if successful. * */ bool insertItem( const QString& descr, const QString& action, const QString& defaultKeyCode, int id, QPopupMenu *qmenu, bool configurable = true ); /** * Insert a standard accelerator item. * * If an action already exists the old association and connections * will be removed. * param id One of the following: @p Open, * @p New, @p Close, @p Save, @p Print, @p Quit, @p Cut, @p Copy, @p Paste, @p Undo, @p Redo, * @p Find, @p Replace, @p Insert, @p Home, @p End, @p Prior, @p Next, or @pHelp. * param descr You can optionally also assign a description to * the standard item which may be used a in a popup menu. */ bool insertStdItem( KStdAccel::StdAccel id, const QString& descr = QString::null ); /** * Convenience function form of @ref insertItem() * without the need to specify a localized * function name for the user. * * This is useful if the accelerator * is used internally only, without appearing in a menu or a * keybinding editor. */ bool insertItem( const QString& action, int defaultKeyCode, bool configurable = true ); /** * Convenience function for of @ref insertItem() without the need * to specify a localized * function name for the user. * * This is useful if the accelerator * is only used internally, without appearing in a menu or a * keybinding editor. */ bool insertItem( const QString& action, int defaultKeyCode, int id, QPopupMenu *qmenu, bool configurable = true ); /** * Remove the accelerator item with the action name action. */ void removeItem( const QString& action ); /** * Shortcuts should be visible in the menu * structure of an application. * * Use this function for that * purpose. Note that the action must have been inserted * before! */ void changeMenuAccel ( QPopupMenu *menu, int id, const QString& action ); /** * Same as @ref changeMenuAccel() but for standard accelerators. */ void changeMenuAccel ( QPopupMenu *menu, int id, KStdAccel::StdAccel accel ); /** * Set the dictionary of accelerator action names and @ref KKeyEntry * objects to @p nKeyDict. * * Note that only a shallow copy is made so * that items will be lost when the @ref KKeyEntry objects are deleted. */ bool setKeyDict( const KKeyEntryMap& nKeyDict ); /** * Retrieve the dictionary of accelerator action names and * @ref KKeyEntry * objects. Note that only a shallow copy is returned so that * items will be lost when the @ref KKeyEntry objects are deleted. */ KKeyEntryMap keyDict() const; // Hack: to be replaced after the 2.2beta testing phase. -- ellis const KKeyMapOrder& keyInsertOrder() const; KKeyMapOrder& keyInsertOrder(); /** * Read all key associations from @p config, or (if @p config * is zero) from the application's configuration file * @ref KGlobal::config(). * * The group in which the configuration is stored can be * set with @ref setConfigGroup(). */ void readSettings(KConfig* config = 0); /// @internal static void readKeyMap( KKeyEntryMap &aKeyMap, const QString &group, KConfigBase *config = 0 ); /** * Write the current configurable associations to @p config, * or (if @p config is zero) to the application's * configuration file. */ void writeSettings(KConfig* config = 0) const; /** * More flexible version of @see writeSettings. You can specify your * own key map. ( writeSettings calls this function internally ) * @internal **/ static void writeKeyMap( const KKeyEntryMap &aKeyMap, const QString& group, KConfig *config = 0, bool global = false ); /** * Set the group in the configuration file in which the * accelerator settings are stored. * * By default, this is "Keys". */ void setConfigGroup( const QString& group ); /** * Retrieve the name of the group in which accelerator * settings are stored. **/ QString configGroup() const; /** * If @p global is true, KAccel writes to the global * configuration file, instead of the application configuration file. **/ void setConfigGlobal( bool global ); /** * Will KAccel write to the global configuration file (instead of * the application configuration file)? **/ bool configGlobal() const; /** * Enable all accelerators if activate is true, or disable it if * activate is false. * * Individual keys can also be enabled or disabled. */ void setEnabled( bool activate ); /** * Are accelerators enabled? **/ bool isEnabled() const; /** * Enable or disable an accelerator item. * * @param action The accelerator item action name. * @param activate Specifies whether the item should be enabled or * disabled. */ void setItemEnabled( const QString& action, bool activate ); /** * Check whether a specific accelerator, @p action, is enabled. **/ bool isItemEnabled( const QString& action ) const; /** * Returns @p true if keyentry can be modified. */ bool configurable( const QString &action ) const; /** * Change the keycode for an accelerator. */ bool updateItem( const QString &action, int keyCode); /** * Remove the keycode for an accelerator. **/ void clearItem(const QString &action); /** * Clear any pointers to a menu. **/ void removeDeletedMenu(QPopupMenu *menu); // When bUseFourModifierKeys is on (setting: Global|Keyboard|Use Four Modifier Keys = true | false) // calls to insertItem will set the current key to aDefaultKeyCode4. static bool useFourModifierKeys(); static void useFourModifierKeys( bool b ); static bool qtSupportsMetaKey(); /** * Retrieve the key code corresponding to the string @p sKey or * zero if the string is not recognized. * * The string must be something like "Shift+A", * "F1+Ctrl+Alt" or "Backspace" for example. That is, the string * must consist of a key name plus a combination of * the modifiers Shift, Ctrl and Alt. * * N.B.: @p sKey must @em not be @ref i18n()'d! */ static int stringToKey( const QString& sKey ); /** * Retrieve a string corresponding to the key code @p keyCode, * which is empty if * @p keyCode is not recognized or zero. */ static QString keyToString( int keyCode, bool i18_n = FALSE ); // X11-Related Functions // I want to move these functions out of KAccel and into their own // class ASAP. // Naming Proceedure: // -CodeX the index of the physical key pressed (keyboard dependent) // -Sym- key symbol. Either unicode (like 'A') or special key (like delete) // -Mod- contains bits for modifier flags // -X Formatted for/by the X sever // -Qt Formatted for/by Qt // keyQt Qt shortcut key value containing both Qt Sym and Qt Mod. // keyEvent- An X or Qt key event // Example: // keyCodeXToKeyQt() converts the X11 key code & mod into a Qt shortcut key enum ModKeysIndex { ModShiftIndex, ModCapsLockIndex, ModCtrlIndex, ModAltIndex, ModNumLockIndex, ModModeSwitchIndex, ModMetaIndex, ModScrollLockIndex, MOD_KEYS }; static void readModifierMapping(); static uint stringToKey( const QString& keyStr, uchar *pKeyCodeX, uint *pKeySymX, uint *pKeyModX ); static uint keyCodeXToKeySymX( uchar keyCodeX, uint keyModX ); static void keyEventXToKeyX( const XEvent *pEvent, uchar *pKeyCodeX, uint *pKeySymX, uint *pKeyModX ); static uint keyEventXToKeyQt( const XEvent *pEvent ); static int keySymXIndex( uint keySym ); static void keySymXMods( uint keySym, uint *pKeyModQt, uint *pKeyModX ); static uint keyCodeXToKeyQt( uchar keyCodeX, uint keyModX ); static uint keySymXToKeyQt( uint keySymX, uint keyModX ); static void keyQtToKeyX( uint keyCombQt, uchar *pKeyCodeX, uint *pKeySymX, uint *pKeyModX ); static uint keyEventQtToKeyQt( const QKeyEvent* ); static QString keyCodeXToString( uchar keyCodeX, uint keyModX, bool bi18n ); static QString keySymXToString( uint keySymX, uint keyModX, bool bi18n ); // Return the keyModX containing just the bit set for the given modifier. static uint keyModXShift(); // ShiftMask static uint keyModXLock(); // LockMask static uint keyModXCtrl(); // ControlMask static uint keyModXAlt(); // Normally Mod1Mask static uint keyModXNumLock(); // Normally Mod2Mask static uint keyModXModeSwitch(); // Normally Mod3Mask static uint keyModXMeta(); // Normally Mod4Mask static uint keyModXScrollLock(); // Normally Mod5Mask // Return the keyMod mask containing the bits set for the modifiers // which may be used in accelerator shortcuts. static uint accelModMaskQt(); // Normally Qt::SHIFT | Qt::CTRL | Qt::ALT | (Qt::ALT<<1) static uint accelModMaskX(); // Normally ShiftMask | ControlMask | Mod1Mask | Mod3Mask // Returns true if X has the Meta key assigned to a modifier bit static bool keyboardHasMetaKey(); signals: void keycodeChanged(); protected: int aAvailableId; KKeyEntryMap aKeyMap; bool bEnabled; bool bGlobal; QString aGroup; private: KAccelPrivate *d; }; #endif Index: trunk/kdelibs/kdecore/kglobalaccel.h =================================================================== --- trunk/kdelibs/kdecore/kglobalaccel.h (revision 103063) +++ trunk/kdelibs/kdecore/kglobalaccel.h (revision 103064) @@ -1,326 +1,326 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Mark Donohoe Copyright (C) 1997 Nicolas Hadacek Copyright (C) 1998 Matthias Ettrich 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _KGLOBALACCEL_H_ #define _KGLOBALACCEL_H_ #include #include #include "kaccel.h" class KGlobalAccelPrivate; /** * Returns the X key modifier for the key code keyCode. */ uint keyToXMod( int keyCode ); /** * Returns the X key sym for the key code keyCode. */ uint keyToXSym( int keyCode ); /** * The KGlobalAccel class handles global keyboard accelerators, allowing a * user to configure key bindings through application configuration files or * through the KKeyChooser GUI. * * A KGlobalAccel contains a list of accelerator items. Each accelerator item * consists of an action name and a keyboard code combined with modifiers * (SHIFT, CTRL and ALT.) * * For example, "CTRL+SHIFT+M" could be a shortcut for popping-up a menu of * monitor setting choices anywhere on the desktop. The key codes are listed * in kckey.cpp. "Monitor settings" could be the action name for this * accelerator. The action name indentifies the key binding in configuration * files and the KKeyChooser GUI. * * When pressed,an accelerator key calls the slot to which it has been * connected. Accelerator items can be connected so that a key will activate * two different slots. * * Key binding configuration during run time can be prevented by specifying * that an accelerator item is not configurable when it is inserted. * * It is possible for a user to choose to have no key associated with an action. * * * The translated first argument for insertItem is only used in the * configuration dialog. * * ... * * ga = new KGlobalAccel(); * ga->insertItem( i18n("Monitor settings"), "Monitor settings", "CTRL+SHIFT+M" ); * ga->connectItem( "Monitor settings", myObject, SLOT( popupMenu() ) ); * * ga->readSettings(); * */ class KGlobalAccel : public QObject { Q_OBJECT friend class KGlobalAccelPrivate; public: /** * Creates a KGlobalAccel object. */ KGlobalAccel( bool _do_not_grab = false); /** Creates a KGlobalAccel object with a parent and a name. The parent has the only effect that the KGlobalAccel object will be automatically destroyed in the parent's destructor, thus releaseing the keys. */ KGlobalAccel(QWidget * parent, const char *name = 0, bool _do_not_grab = false); /** * Destroys the accelerator object.and ungrabs any allocated key bindings. */ ~KGlobalAccel(); /** * Removes all accelerator items. */ void clear(); - + /** * Connect an accelerator item to a slot/signal in another object. * * Arguments: * * @param action is the accelerator item action name. * @param receiver is the object to receive a signal * @param member is a slot or signal in the receiver * @param activate indicates whether the accelrator item should be * enabled immediately */ void connectItem( const QString& action, const QObject* receiver, const char *member, bool activate = true ); /** * Returns the number of accelerator items. */ uint count() const; /** * Returns the key code of the accelerator item with the action name * action, or zero if either the action name cannot be found or the current * key is set to no key. */ int currentKey( const QString& action ) const; /** * Returns the default key code of the accelerator item with the action name * action, or zero if the action name cannot be found. */ int defaultKey( const QString& action) const; /** * Disconnects an accelerator item from a function in another object. */ void disconnectItem( const QString& action, const QObject* receiver, const char *member ); /** * Returns that identifier of the accelerator item with the keycode key, * or zero if the item cannot be found. */ QString findKey( int key ) const; /** * Inserts an accelerator item and returns false if the key code * defaultKeyCode is not valid. * * Arguments: * * @param action is the accelerator item action name. * @param defaultKeyCode is a key code to be used as the default for the action. * @param configurable indicates whether a user can configure the key * binding using the KKeyChooser GUI and whether the key will be written * back to configuration files on calling writeSettings. * * If an action already exists the old association and connections will be * removed.. * */ bool insertItem( const QString& descr, const QString& action, int defaultKeyCode, bool configurable = true ); bool insertItem( const QString& descr, const QString& action, KKey defaultKeyCode3, KKey defaultKeyCode4, bool configurable = true ); /** * Inserts an accelerator item and returns false if the key code * defaultKeyCode is not valid. * * Arguments: * * @param action is the accelerator item action name. * @param defaultKeyCode is a key plus a combination of SHIFT, CTRL * and ALT to be used as the default for the action. * @param configurable indicates whether a user can configure * the key * binding using the KKeyChooser GUI and whether the key * will be written back to configuration files on calling * writeSettings. * * If an action already exists the old association and connections * will be removed.. */ bool insertItem( const QString& descr, const QString& action, const QString& defaultKeyCode, bool configurable = true ); //bool insertItem( const QString& descr, const QString& action, // const QString& defaultKeyCode3, const QString& defaultKeyCode4, // bool configurable = true ); bool isEnabled() const; bool isItemEnabled( const QString& action ) const; /** * Returns the dictionary of accelerator action names and KKeyEntry * objects. Note that only a shallow copy is returned so that * items will be lost when the KKeyEntry objects are deleted. */ KKeyEntryMap keyDict() const; /** * Read all key associations from @p config, or (if @p config * is zero) from the application's configuration file * @ref KGlobal::config(). * * The group in which the configuration is stored can be * set with @ref setConfigGroup(). */ void readSettings(KConfig* config); // BCI merge with the one above void readSettings(); /** * Removes the accelerator item with the action name action. */ void removeItem( const QString& action ); void setConfigGroup( const QString& group ); QString configGroup() const; /** * Enables the accelerator if activate is true, or disables it if * activate is false... * * Individual keys can also be enabled or disabled. */ void setEnabled( bool activate ); /** * Enables or disables an accelerator item. * * Arguments: * * @param action is the accelerator item action name. * @param activate specifies whether the item should be enabled or * disabled. */ void setItemEnabled( const QString& action, bool activate ); /** * Sets the dictionary of accelerator action names and KKeyEntry * objects to nKeyMap. Note that only a shallow copy is made so that items will be * lost when the KKeyEntry objects are deleted. */ bool setKeyDict( const KKeyEntryMap& nKeyMap ); /** * Write the current configurable associations to @p config, * or (if @p config is zero) to the application's * configuration file. */ void writeSettings(KConfig* config) const; // BCI merge with the one above void writeSettings() const; /** * Enables or disables raw mode on an accelerator item. * * Raw mode means that KGlobalAccel will not ungrab the key * before emitting the signals. In raw mode, this is the * responsibility of the caller. * * Do not use this until you really really really know what you * are doing. * * Arguments: * * @param action is the accelerator item action name. * @param activate specifies whether raw mode for the * item should be enabled or disabled. */ void setItemRawModeEnabled( const QString& action, bool activate ); protected: // Attempts to make a passive X server grab/ungrab of the specified key. // Return true if successful. // Modifications with NumLock, CapsLock, ScrollLock, and ModeSwitch are // also grabbed. - bool grabKey( KKeyEntry *pKeyEntry, bool bGrab ); + bool grabKey( const QString& action, bool bGrab ); /** * Filters X11 events ev for key bindings in the accelerator dictionary. * If a match is found the activated activated is emitted and the function * returns true. Return false if the event is not processed. * * This is public for compatibility only. You do not need to call it. */ bool x11EventFilter(const XEvent *); signals: void activated(); void activated( int ); protected: int aAvailableId; KKeyEntryMap aKeyMap; bool bEnabled; QString aGroup; bool do_not_grab; KGlobalAccelPrivate* d; public: // Setting this to false shuts off processing of KeyPress events in // x11EventFilter(). It will still be called, but won't act on them. // This is a more effecient means for briefly suspending processing // than setEnabled(false) ... setEnabled(true). static void setKeyEventsEnabled( bool enabled ); static bool areKeyEventsEnabled(); }; #endif // _KGLOBALACCEL_H_ Index: trunk/kdelibs/kdecore/kglobalaccel.cpp =================================================================== --- trunk/kdelibs/kdecore/kglobalaccel.cpp (revision 103063) +++ trunk/kdelibs/kdecore/kglobalaccel.cpp (revision 103064) @@ -1,592 +1,612 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe , Jani Jaakkola (jjaakkol@cs.helsinki.fi), Nicolas Hadacek Matthias Ettrich (ettrich@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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "kglobalaccel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KeyPress const int XKeyPress = KeyPress; #undef KeyPress #endif // NOTE ABOUT CONFIGURATION CHANGES // Test if keys enabled because these keys have made X server grabs +struct KKeyNative { + uint keyCode, keyMod; // For storing the X11 codes (for global shortcuts) + + KKeyNative() : keyCode(0), keyMod(0) { } +}; +typedef QMap KKeyNativeMap; + class KGlobalAccelPrivate : public QWidget { public: KGlobalAccelPrivate( KGlobalAccel* a, bool do_not_grab ) :QWidget(), accel( a ), rawModeList( 0 ) { if ( !do_not_grab ) kapp->installX11EventFilter( this ); } ~KGlobalAccelPrivate() { delete rawModeList; } protected: bool x11Event( XEvent * e ) { return accel->x11EventFilter( e ); } private: KGlobalAccel* accel; public: QStringList* rawModeList; static bool g_bKeyEventsEnabled; + KKeyNativeMap keyNativeMap; }; bool KGlobalAccelPrivate::g_bKeyEventsEnabled = true; KGlobalAccel::KGlobalAccel(bool _do_not_grab) : QObject() { aAvailableId = 1; bEnabled = true; aGroup = "Global Keys"; do_not_grab =_do_not_grab; d = 0; d = new KGlobalAccelPrivate( this, do_not_grab ); } KGlobalAccel::KGlobalAccel(QWidget * parent, const char *name, bool _do_not_grab) : QObject(parent, name) { aAvailableId = 1; bEnabled = true; aGroup = "Global Keys"; do_not_grab =_do_not_grab; d = 0; d = new KGlobalAccelPrivate( this, do_not_grab ); } KGlobalAccel::~KGlobalAccel() { setEnabled( false ); delete d; } void KGlobalAccel::clear() { setEnabled( false ); aKeyMap.clear(); } void KGlobalAccel::connectItem( const QString& action, const QObject* receiver, const char *member, bool activate ) { if (!aKeyMap.contains(action)) { kdDebug() << QString::fromLatin1("KGlobalAccel : Cannot connect action %1 " "which is not in the object dictionary\n").arg(action); return; } KKeyEntry entry = aKeyMap[ action ]; entry.receiver = receiver; entry.member = member; entry.aAccelId = aAvailableId; aKeyMap.replace(action, entry); aAvailableId++; setItemEnabled( action, activate ); } uint KGlobalAccel::count() const { return aKeyMap.count(); } int KGlobalAccel::currentKey( const QString& action ) const { KKeyEntry entry = aKeyMap[ action ]; return entry.aCurrentKeyCode; } int KGlobalAccel::defaultKey( const QString& action ) const { KKeyEntry entry = aKeyMap[ action ]; return entry.aDefaultKeyCode; } void KGlobalAccel::disconnectItem( const QString& action, const QObject* /*receiver*/, const char */*member*/ ) { // TODO KKeyEntry entry = aKeyMap[ action ]; } QString KGlobalAccel::findKey( int key ) const { for (KKeyEntryMap::ConstIterator aKeyIt = aKeyMap.begin(); aKeyIt != aKeyMap.end(); ++aKeyIt) if ( key == (*aKeyIt).aCurrentKeyCode ) return aKeyIt.key(); return QString::null; } bool KGlobalAccel::insertItem( const QString& descr, const QString& action, - KKey defaultKeyCode3, KKey defaultKeyCode4, + KKey defaultKeyCode3, KKey /*defaultKeyCode4*/, bool configurable ) { if (aKeyMap.contains(action)) removeItem( action ); KKeyEntry entry; entry.aDefaultKeyCode = defaultKeyCode3.key(); - entry.aDefaultKeyCode4 = defaultKeyCode4.key(); - entry.aCurrentKeyCode = entry.aConfigKeyCode = KAccel::useFourModifierKeys() ? defaultKeyCode4.key() : defaultKeyCode3.key(); + //entry.aDefaultKeyCode4 = defaultKeyCode4.key(); + entry.aCurrentKeyCode = /*try.aConfigKeyCode = KAccel::useFourModifierKeys() ? defaultKeyCode4.key() : */ defaultKeyCode3.key(); entry.bConfigurable = configurable; entry.bEnabled = false; entry.aAccelId = 0; entry.receiver = 0; entry.member = 0; entry.descr = descr; aKeyMap[action] = entry; return true; } bool KGlobalAccel::insertItem( const QString& descr, const QString& action, int keyCode, bool configurable ) { return insertItem( descr, action, keyCode, keyCode, configurable ); } bool KGlobalAccel::insertItem( const QString& descr, const QString& action, const QString& keyCode, bool configurable ) { return insertItem(descr, action, KKey(keyCode).key(), configurable); } bool KGlobalAccel::isEnabled() const { return bEnabled; } bool KGlobalAccel::isItemEnabled( const QString& action ) const { return aKeyMap[action].bEnabled; } KKeyEntryMap KGlobalAccel::keyDict() const { return aKeyMap; } void KGlobalAccel::readSettings(KConfig* config) { kdDebug(125) << "KGlobalAccel::readSettings()\n"; - QArray aKeysToGrab( aKeyMap.count() ); + QMap aKeysToGrab; int cKeysToGrab = 0; KConfigBase *pConfig = config ? config : KGlobal::config(); KConfigGroupSaver cgs( pConfig, aGroup ); // Read settings from config file, and if it differs // from the current setting, release the key and save the // new key for grabbing afterwards. for( KKeyEntryMap::Iterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it ) { QString keyStr = pConfig->readEntry( it.key() ); uint keyQt; uchar keyCodeX; uint keyModX; + KKeyNative keyNative; if ( keyStr.isEmpty() || keyStr.startsWith( "default" )) - keyQt = KAccel::useFourModifierKeys() ? (*it).aDefaultKeyCode4 : (*it).aDefaultKeyCode; + keyQt = /*KAccel::useFourModifierKeys() ? (*it).aDefaultKeyCode4 :*/ (*it).aDefaultKeyCode; else keyQt = KAccel::stringToKey( keyStr ); + if( d->keyNativeMap.contains( it.key() ) ) + keyNative = d->keyNativeMap[ it.key() ]; + + // Get X keycodes for current X keymap. KAccel::keyQtToKeyX( keyQt, &keyCodeX, 0, &keyModX ); kdDebug(125) << QString( it.key()+" = "+keyStr+" key: 0x%1 curKey: 0x%2 enabled: %3\n" ) .arg( keyQt, 0, 16 ). arg( (*it).aCurrentKeyCode, 0, 16 ).arg( (*it).bEnabled ); // If the X codes have changed, - if( (*it).bEnabled && (keyCodeX != (*it).keyCodeNative || keyModX != (*it).keyModNative) && keyCodeX != 0 ) { - grabKey( &(*it), false ); - aKeysToGrab[cKeysToGrab++] = &(*it); + if( (*it).bEnabled && (keyCodeX != keyNative.keyCode || keyModX != keyNative.keyMod) ) { + if( keyNative.keyCode ) + grabKey( it.key(), false ); + aKeysToGrab[cKeysToGrab++] = it.key(); } (*it).aConfigKeyCode = (*it).aCurrentKeyCode = keyQt; } // Grab the changed keys. for( int i = 0; i < cKeysToGrab; i++ ) grabKey( aKeysToGrab[i], true ); } void KGlobalAccel::readSettings() { readSettings( NULL ); } void KGlobalAccel::removeItem( const QString& action ) { aKeyMap.remove(action); } void KGlobalAccel::setConfigGroup( const QString& group ) { aGroup = group; } QString KGlobalAccel::configGroup() const { return aGroup; } void KGlobalAccel::setKeyEventsEnabled( bool enabled ) { KGlobalAccelPrivate::g_bKeyEventsEnabled = enabled; } bool KGlobalAccel::areKeyEventsEnabled() { return KGlobalAccelPrivate::g_bKeyEventsEnabled; } void KGlobalAccel::setEnabled( bool activate ) { kdDebug(125) << QString( "KGlobalAccel::setEnabled( %1 )\n" ).arg( activate ); for (KKeyEntryMap::ConstIterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it) setItemEnabled( it.key(), activate ); bEnabled = activate; } void KGlobalAccel::setItemEnabled( const QString& action, bool activate ) { if ( !aKeyMap.contains(action) ) { kdDebug() << QString::fromLatin1("KGlobalAccel : cannot enable action %1 " "which is not in the object dictionary\n").arg(action); return; } KKeyEntry& entry = aKeyMap[action]; - if( entry.bEnabled == activate ) - return; - aKeyMap[action].bEnabled = activate; - - if ( entry.aCurrentKeyCode == 0 ) return; - - if ( entry.bEnabled ) - grabKey( &entry, true ); - else - grabKey( &entry, false ); - + if ( entry.bEnabled != activate ) { + aKeyMap[action].bEnabled = activate; + if ( entry.aCurrentKeyCode ) + grabKey( action, activate ); + } } bool KGlobalAccel::setKeyDict( const KKeyEntryMap& nKeyMap ) { for (KKeyEntryMap::ConstIterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it) { // ungrab all connected and enabled keys QString s; if ( (*it).bEnabled ) - grabKey( (KKeyEntry*)&(*it), false ); + grabKey( it.key(), false ); } // Clear the dictionary aKeyMap.clear(); + d->keyNativeMap.clear(); // Insert the new items into the dictionary and reconnect if neccessary // Note also swap config and current key codes !!!!!! for (KKeyEntryMap::ConstIterator it = nKeyMap.begin(); it != nKeyMap.end(); ++it) { KKeyEntry entry = *it; // Not we write config key code to current key code !! entry.aCurrentKeyCode = (*it).aConfigKeyCode; aKeyMap[it.key()] = entry; if ( entry.bEnabled ) - grabKey( &entry, true ); + grabKey( it.key(), true ); } return true; } void KGlobalAccel::writeSettings(KConfig* config) const { KAccel::writeKeyMap( aKeyMap, aGroup, config ); } void KGlobalAccel::writeSettings() const { KAccel::writeKeyMap( aKeyMap, aGroup, NULL ); } bool grabFailed; extern "C" { static int XGrabErrorHandler( Display *, XErrorEvent *e ) { if ( e->error_code != BadAccess ) { kdWarning() << "grabKey: got X error " << e->type << " instead of BadAccess\n"; } grabFailed = true; return 0; } } static uint g_keyModMaskXAccel = 0; static uint g_keyModMaskXAlwaysOff = 0; static uint g_keyModMaskXOnOrOff = 0; static void calculateGrabMasks() { KAccel::readModifierMapping(); g_keyModMaskXAccel = KAccel::accelModMaskX(); g_keyModMaskXAlwaysOff = ~( KAccel::keyModXShift() | KAccel::keyModXLock() | KAccel::keyModXCtrl() | KAccel::keyModXAlt() | KAccel::keyModXNumLock() | KAccel::keyModXModeSwitch() | KAccel::keyModXMeta() | KAccel::keyModXScrollLock() ); g_keyModMaskXOnOrOff = KAccel::keyModXLock() | KAccel::keyModXNumLock() | KAccel::keyModXScrollLock(); // X11 seems to treat the ModeSwitch bit differently than the others -- // namely, it won't grab anything if it's set, but both switched and // unswiched keys if it's not. // So we always need to XGrabKey with the bit set to 0. g_keyModMaskXAlwaysOff |= KAccel::keyModXModeSwitch(); } -bool KGlobalAccel::grabKey( KKeyEntry *pKeyEntry, bool bGrab ) +bool KGlobalAccel::grabKey( const QString &action, bool bGrab ) { - if (do_not_grab) - return true; - if( !pKeyEntry ) + if( action.isEmpty() ) return false; + if( bGrab ) { + if( do_not_grab ) + return true; + if( !aKeyMap.contains( action ) ) + return false; + } else if( !d->keyNativeMap.contains( action ) ) { + kdDebug(125) << "Tried to ungrab an action (" << action <<") which is not is d->keyNativeMap." << endl; + return false; + } // Make sure that grab masks have been initialized. if( g_keyModMaskXOnOrOff == 0 ) calculateGrabMasks(); // Get the X equivalents. - KKey key = pKeyEntry->aCurrentKeyCode; + KKey key; + KKeyNative keyNative; uchar keyCodeX; uint keyModX; if( bGrab ) { + key = aKeyMap[action].aCurrentKeyCode; KAccel::keyQtToKeyX( key.key(), &keyCodeX, 0, &keyModX ); - pKeyEntry->keyCodeNative = keyCodeX; - pKeyEntry->keyModNative = keyModX; + keyNative.keyCode = keyCodeX; + keyNative.keyMod = keyModX; + d->keyNativeMap[action] = keyNative; } else { - keyCodeX = pKeyEntry->keyCodeNative; - keyModX = pKeyEntry->keyModNative; + keyCodeX = d->keyNativeMap[action].keyCode; + keyModX = d->keyNativeMap[action].keyMod; } keyModX &= g_keyModMaskXAccel; // Get rid of any non-relevant bits in mod #ifndef __osf__ // this crashes under Tru64 so ..... kdDebug(125) << QString( "grabKey( key: 0x%1, bGrab: %2 ): %3 keyCodeX: %4 keyModX: %5\n" ) - .arg( key.key(), 0, 16 ).arg( bGrab ).arg( bGrab ? key.toString() : "" ) + .arg( key.key(), 0, 16 ).arg( bGrab ).arg( action ) .arg( keyCodeX, 0, 16 ).arg( keyModX, 0, 16 ); #endif if( !keyCodeX ) return false; // We want to catch only our own errors grabFailed = false; XSync(qt_xdisplay(),0); XErrorHandler savedErrorHandler=XSetErrorHandler(XGrabErrorHandler); // We'll have to grab 8 key modifier combinations in order to cover all // combinations of CapsLock, NumLock, ScrollLock. // Does anyone with more X-savvy know how to set a mask on qt_xrootwin so that // the irrelevant bits are always ignored and we can just make one XGrabKey // call per accelerator? uint keyModMaskX = ~g_keyModMaskXOnOrOff; for( uint irrelevantBitsMask = 0; irrelevantBitsMask <= 0xff; irrelevantBitsMask++ ) { if( (irrelevantBitsMask & keyModMaskX) == 0 ) { kdDebug(125) << QString( "code: 0x%1 state: 0x%2 | 0x%3\n" ) .arg( keyCodeX, 0, 16 ).arg( keyModX, 0, 16 ).arg( irrelevantBitsMask, 0, 16 ); if( bGrab ) { XGrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask, qt_xrootwin(), True, GrabModeAsync, GrabModeSync ); // If grab failed, then ungrab any previously successful grabs. if( grabFailed ) { kdDebug(125) << "grab failed!\n"; - pKeyEntry->keyCodeNative = 0; - pKeyEntry->keyModNative = 0; + d->keyNativeMap.remove( action ); for( uint m = 0; m < irrelevantBitsMask; m++ ) { if( m & keyModMaskX == 0 ) XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | m, qt_xrootwin() ); } break; } - } else + } else { + d->keyNativeMap.remove( action ); XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask, qt_xrootwin() ); + } } } XSync(qt_xdisplay(),0); XSetErrorHandler(savedErrorHandler); return !grabFailed; } bool KGlobalAccel::x11EventFilter( const XEvent *event_ ) { uint keyModX, keyModX2; uint keySymX, keySymX2; if ( event_->type == MappingNotify ) { kdDebug(125) << "Caught MappingNotify" << endl; // Do XUngrabKey()s. setEnabled( false ); // Maybe the X modifier map has been changed. calculateGrabMasks(); // Do new XGrabKey()s. setEnabled( true ); return true; } if ( aKeyMap.isEmpty() ) return false; if ( event_->type != XKeyPress ) return false; if ( !KGlobalAccelPrivate::g_bKeyEventsEnabled ) return false; KAccel::keyEventXToKeyX( event_, 0, &keySymX, &keyModX ); keyModX &= g_keyModMaskXAccel; kdDebug(125) << "x11EventFilter: seek " << KAccel::keySymXToString( keySymX, keyModX, false ) << QString( " keyCodeX: %1 state: %2 keySym: %3 keyMod: %4\n" ) .arg( event_->xkey.keycode, 0, 16 ).arg( event_->xkey.state, 0, 16 ).arg( keySymX, 0, 16 ).arg( keyModX, 0, 16 ); // Search for which accelerator activated this event: KKeyEntry entry; QString sConfigKey; for (KKeyEntryMap::ConstIterator it = aKeyMap.begin(); it != aKeyMap.end(); ++it) { KAccel::keyQtToKeyX( (*it).aCurrentKeyCode, 0, &keySymX2, &keyModX2 ); //kdDebug() << "x11EventFilter: inspecting " << KAccel::keyToString( (*it).aCurrentKeyCode ) // << QString( " keySym: %1 keyMod: %2\n" ).arg( keySymX2, 0, 16 ).arg( keyModX2, 0, 16 ); if ( keySymX == keySymX2 && keyModX == (keyModX2 & g_keyModMaskXAccel) ) { entry = *it; sConfigKey = it.key(); break; } } if ( !QWidget::keyboardGrabber() ) { kdDebug(125) << "received action " << sConfigKey << endl; if ( !d->rawModeList || !d->rawModeList->contains( sConfigKey ) ) { XUngrabKeyboard(qt_xdisplay(), event_->xkey.time ); } else { kdDebug(125) << "in raw mode !" << endl; } if ( !entry.receiver || !entry.bEnabled ) { kdDebug(125) << "KGlobalAccel::x11EventFilter(): Key has been grabbed (" << KAccel::keySymXToString( keySymX, keyModX, false ) << ") which doesn't have an associated action or was disabled.\n"; return false; } else { QRegExp r1( "([ ]*int[ ]*)" ), r2( " [0-9]+$" ); if( r1.match( entry.member ) >= 0 && r2.match( sConfigKey ) >= 0 ) { int n = sConfigKey.mid( sConfigKey.findRev(' ')+1 ).toInt(); kdDebug(125) << "Calling " << entry.member << " int = " << n << endl; connect( this, SIGNAL( activated( int ) ), entry.receiver, entry.member); emit activated( n ); disconnect( this, SIGNAL( activated( int ) ), entry.receiver, entry.member ); } else { connect( this, SIGNAL( activated() ), entry.receiver, entry.member); emit activated(); disconnect( this, SIGNAL( activated() ), entry.receiver, entry.member ); } } } return true; } void KGlobalAccel::setItemRawModeEnabled( const QString& action, bool activate ) { if ( !d->rawModeList ) d->rawModeList = new QStringList; if ( activate ) { if ( !d->rawModeList->contains( action ) ) d->rawModeList->append( action ); } else { d->rawModeList->remove( action ); } } /*****************************************************************************/ uint keyToXMod( int keyCode ) { uint mod = 0; if ( keyCode == 0 ) return mod; if ( keyCode & Qt::SHIFT ) mod |= ShiftMask; if ( keyCode & Qt::CTRL ) mod |= ControlMask; if ( keyCode & Qt::ALT ) mod |= Mod1Mask; if ( keyCode & (Qt::ALT<<1) ) mod |= Mod4Mask; return mod; } uint keyToXSym( int keyCode ) { uint keySymX; KAccel::keyQtToKeyX( keyCode, 0, &keySymX, 0 ); return keySymX; } #include "kglobalaccel.moc"