kdeui Library API Documentation

kkeydialog.cpp

00001 /* This file is part of the KDE libraries
00002     Copyright (C) 1998 Mark Donohoe <donohoe@kde.org>
00003     Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org>
00004     Copyright (C) 1998 Matthias Ettrich <ettrich@kde.org>
00005     Copyright (C) 2001 Ellis Whitehead <ellis@kde.org>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to
00019     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00020     Boston, MA 02111-1307, USA.
00021 */
00022 
00023 #include "kkeydialog.h"
00024 #include "kkeybutton.h"
00025 
00026 #include <string.h>
00027 
00028 #include <qbuttongroup.h>
00029 #include <qlabel.h>
00030 #include <qlayout.h>
00031 #include <qdrawutil.h>
00032 #include <qpainter.h>
00033 #include <qradiobutton.h>
00034 #include <qregexp.h>
00035 #include <qwhatsthis.h>
00036 
00037 #include <kaccel.h>
00038 #include <kaction.h>
00039 #include <kaccelaction.h>
00040 #include <kactionshortcutlist.h>
00041 #include <kapplication.h>
00042 #include <kconfig.h>
00043 #include <kdebug.h>
00044 #include <kglobal.h>
00045 #include <kglobalaccel.h>
00046 #include <klocale.h>
00047 #include <kmessagebox.h>
00048 #include <kshortcut.h>
00049 #include <kshortcutlist.h>
00050 #include <kxmlguifactory.h>
00051 #include <kaboutdata.h>
00052 #include <kstaticdeleter.h>
00053 
00054 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00055 #define XK_XKB_KEYS
00056 #define XK_MISCELLANY
00057 #include <X11/Xlib.h>   // For x11Event() // schroder
00058 #include <X11/keysymdef.h> // For XK_... // schroder
00059 
00060 #ifdef KeyPress
00061 const int XFocusOut = FocusOut;
00062 const int XFocusIn = FocusIn;
00063 const int XKeyPress = KeyPress;
00064 const int XKeyRelease = KeyRelease;
00065 #undef KeyRelease
00066 #undef KeyPress
00067 #undef FocusOut
00068 #undef FocusIn
00069 #endif // KEYPRESS
00070 #endif // Q_WX_X11 && ! K_WS_QTONLY
00071 
00072 //---------------------------------------------------------------------
00073 // KKeyChooserItem
00074 //---------------------------------------------------------------------
00075 
00076 class KKeyChooserItem : public KListViewItem
00077 {
00078  public:
00079     KKeyChooserItem( KListView* parent, QListViewItem* after, KShortcutList* pList, uint iAction );
00080     KKeyChooserItem( QListViewItem* parent, QListViewItem* after, KShortcutList* pList, uint iAction );
00081 
00082     QString actionName() const;
00083     const KShortcut& shortcut() const;
00084     bool isConfigurable() const
00085         { return m_pList->isConfigurable( m_iAction ); }
00086     const KShortcut& shortcutDefault() const
00087         { return m_pList->shortcutDefault( m_iAction ); }
00088 
00089     void setShortcut( const KShortcut& cut );
00090     void commitChanges();
00091 
00092     virtual QString text( int iCol ) const;
00093     virtual int compare( QListViewItem*, int iCol, bool bAscending ) const;
00094 
00095  protected:
00096     KShortcutList* m_pList;
00097     uint m_iAction;
00098     bool m_bModified;
00099     KShortcut m_cut;
00100 };
00101 
00102 //---------------------------------------------------------------------
00103 // KKeyChooserPrivate
00104 //---------------------------------------------------------------------
00105 
00106 class KKeyChooserPrivate
00107 {
00108  public:
00109     QValueList<KShortcutList*> rgpLists;
00110     QValueList<KShortcutList*> rgpListsAllocated;
00111 
00112     KListView *pList;
00113     QLabel *lInfo;
00114     KKeyButton *pbtnShortcut;
00115     QGroupBox *fCArea;
00116     QButtonGroup *kbGroup;
00117 
00118     QMap<QString, KShortcut> mapGlobals;
00119 
00120     // If this is set, then shortcuts require a modifier:
00121     //  so 'A' would not be valid, whereas 'Ctrl+A' would be.
00122     // Note, however, that this only applies to printable characters.
00123     //  'F1', 'Insert', etc., could still be used.
00124     bool bAllowLetterShortcuts;
00125     // When set, pressing the 'Default' button will select the aDefaultKeycode4,
00126     //  otherwise aDefaultKeycode.
00127     bool bPreferFourModifierKeys;
00128 };
00129 
00130 //---------------------------------------------------------------------
00131 // KKeyChooser
00132 //---------------------------------------------------------------------
00133 
00134 KKeyChooser::KKeyChooser( QWidget* parent, ActionType type, bool bAllowLetterShortcuts )
00135 : QWidget( parent )
00136 {
00137     initGUI( type, bAllowLetterShortcuts );
00138 }
00139 
00140 KKeyChooser::KKeyChooser( KActionCollection* coll, QWidget* parent, bool bAllowLetterShortcuts )
00141 : QWidget( parent )
00142 {
00143     initGUI( Application, bAllowLetterShortcuts );
00144     insert( coll );
00145 }
00146 
00147 KKeyChooser::KKeyChooser( KAccel* pAccel, QWidget* parent, bool bAllowLetterShortcuts )
00148 : QWidget( parent )
00149 {
00150     initGUI( Application, bAllowLetterShortcuts );
00151     insert( pAccel );
00152 }
00153 
00154 KKeyChooser::KKeyChooser( KGlobalAccel* pAccel, QWidget* parent )
00155 : QWidget( parent )
00156 {
00157     initGUI( ApplicationGlobal, false );
00158     insert( pAccel );
00159 }
00160 
00161 KKeyChooser::KKeyChooser( KShortcutList* pList, QWidget* parent, ActionType type, bool bAllowLetterShortcuts )
00162 : QWidget( parent )
00163 {
00164     initGUI( type, bAllowLetterShortcuts );
00165     insert( pList );
00166 }
00167 
00168 KKeyChooser::KKeyChooser( KAccel* actions, QWidget* parent,
00169             bool bCheckAgainstStdKeys,
00170             bool bAllowLetterShortcuts,
00171             bool bAllowWinKey )
00172 : QWidget( parent )
00173 {
00174     ActionType type;
00175     if( bAllowWinKey )
00176         type = (bCheckAgainstStdKeys) ? ApplicationGlobal : Global;
00177     else
00178         type = Application;
00179 
00180     initGUI( type, bAllowLetterShortcuts );
00181     insert( actions );
00182 }
00183 
00184 KKeyChooser::KKeyChooser( KGlobalAccel* actions, QWidget* parent,
00185             bool bCheckAgainstStdKeys,
00186             bool bAllowLetterShortcuts,
00187             bool /*bAllowWinKey*/ )
00188 : QWidget( parent )
00189 {
00190     ActionType type = (bCheckAgainstStdKeys) ? ApplicationGlobal : Global;
00191 
00192     initGUI( type, bAllowLetterShortcuts );
00193     insert( actions );
00194 }
00195 
00196 // list of all existing KKeyChooser's
00197 // Used when checking global shortcut for a possible conflict
00198 // (just checking against kdeglobals isn't enough, the shortcuts
00199 // might have changed in KKeyChooser and not being saved yet).
00200 // Also used when reassigning a shortcut from one chooser to another.
00201 static QValueList< KKeyChooser* >* allChoosers = NULL;
00202 static KStaticDeleter< QValueList< KKeyChooser* > > allChoosersDeleter;
00203 
00204 KKeyChooser::~KKeyChooser()
00205 {
00206         allChoosers->remove( this );
00207     // Delete allocated KShortcutLists
00208     for( uint i = 0; i < d->rgpListsAllocated.count(); i++ )
00209         delete d->rgpListsAllocated[i];
00210     delete d;
00211 }
00212 
00213 bool KKeyChooser::insert( KActionCollection *pColl)
00214 {
00215     return insert(pColl, QString::null);
00216 }
00217 
00218 bool KKeyChooser::insert( KActionCollection* pColl, const QString &title )
00219 {
00220     QString str = title;
00221     if ( title.isEmpty() && pColl->instance()
00222          && pColl->instance()->aboutData() )
00223         str = pColl->instance()->aboutData()->programName();
00224 
00225     KShortcutList* pList = new KActionShortcutList( pColl );
00226     d->rgpListsAllocated.append( pList );
00227     d->rgpLists.append( pList );
00228     buildListView(d->rgpLists.count() - 1, str);
00229     return true;
00230 }
00231 
00232 bool KKeyChooser::insert( KAccel* pAccel )
00233 {
00234     KShortcutList* pList = new KAccelShortcutList( pAccel );
00235     d->rgpListsAllocated.append( pList );
00236     return insert( pList );
00237 }
00238 
00239 bool KKeyChooser::insert( KGlobalAccel* pAccel )
00240 {
00241     KShortcutList* pList = new KAccelShortcutList( pAccel );
00242     d->rgpListsAllocated.append( pList );
00243     return insert( pList );
00244 }
00245 
00246 bool KKeyChooser::insert( KShortcutList* pList )
00247 {
00248     d->rgpLists.append( pList );
00249     buildListView( d->rgpLists.count() - 1, QString::null );
00250     return true;
00251 }
00252 
00253 void KKeyChooser::commitChanges()
00254 {
00255     kdDebug(125) << "KKeyChooser::commitChanges()" << endl;
00256 
00257     QListViewItemIterator it( d->pList );
00258     for( ; it.current(); ++it ) {
00259         KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(it.current());
00260         if( pItem )
00261             pItem->commitChanges();
00262     }
00263 }
00264 
00265 void KKeyChooser::save()
00266 {
00267     commitChanges();
00268     for( uint i = 0; i < d->rgpLists.count(); i++ )
00269         d->rgpLists[i]->save();
00270 }
00271 
00272 void KKeyChooser::initGUI( ActionType type, bool bAllowLetterShortcuts )
00273 {
00274   d = new KKeyChooserPrivate();
00275 
00276   m_type = type;
00277   d->bAllowLetterShortcuts = bAllowLetterShortcuts;
00278 
00279   d->bPreferFourModifierKeys = KGlobalAccel::useFourModifierKeys();
00280 
00281   //
00282   // TOP LAYOUT MANAGER
00283   //
00284   // The following layout is used for the dialog
00285   //            LIST LABELS LAYOUT
00286   //            SPLIT LIST BOX WIDGET
00287   //            CHOOSE KEY GROUP BOX WIDGET
00288   //            BUTTONS LAYOUT
00289   // Items are added to topLayout as they are created.
00290   //
00291 
00292   QBoxLayout *topLayout = new QVBoxLayout( this, 0, KDialog::spacingHint() );
00293 
00294   QGridLayout *stackLayout = new QGridLayout(2, 2, 2);
00295   topLayout->addLayout( stackLayout, 10 );
00296   stackLayout->setRowStretch( 1, 10 ); // Only list will stretch
00297 
00298   //
00299   // CREATE SPLIT LIST BOX
00300   //
00301   // fill up the split list box with the action/key pairs.
00302   //
00303   d->pList = new KListView( this );
00304   d->pList->setFocus();
00305 
00306   stackLayout->addMultiCellWidget( d->pList, 1, 1, 0, 1 );
00307   QString wtstr = i18n("Here you can see a list of key bindings, "
00308                        "i.e. associations between actions (e.g. 'Copy') "
00309                        "shown in the left column and keys or combination "
00310                        "of keys (e.g. Ctrl+V) shown in the right column.");
00311 
00312   QWhatsThis::add( d->pList, wtstr );
00313 
00314   d->pList->setAllColumnsShowFocus( true );
00315   d->pList->addColumn(i18n("Action"));
00316   d->pList->addColumn(i18n("Shortcut"));
00317   d->pList->addColumn(i18n("Alternate"));
00318 
00319   connect( d->pList, SIGNAL(currentChanged(QListViewItem*)),
00320            SLOT(slotListItemSelected(QListViewItem*)) );
00321 
00322   // handle double clicking an item
00323   connect ( d->pList, SIGNAL ( doubleClicked ( QListViewItem *, const QPoint &, int ) ),
00324                        SLOT ( captureCurrentItem()) );
00325   connect ( d->pList, SIGNAL ( spacePressed( QListViewItem* )), SLOT( captureCurrentItem()));
00326   //
00327   // CREATE CHOOSE KEY GROUP
00328   //
00329   d->fCArea = new QGroupBox( this );
00330   topLayout->addWidget( d->fCArea, 1 );
00331 
00332   d->fCArea->setTitle( i18n("Shortcut for Selected Action") );
00333   d->fCArea->setFrameStyle( QFrame::Box | QFrame::Sunken );
00334 
00335   //
00336   // CHOOSE KEY GROUP LAYOUT MANAGER
00337   //
00338   QGridLayout *grid = new QGridLayout( d->fCArea, 3, 4, KDialog::spacingHint() );
00339   grid->addRowSpacing( 0, fontMetrics().lineSpacing() );
00340 
00341   d->kbGroup = new QButtonGroup( d->fCArea );
00342   d->kbGroup->hide();
00343   d->kbGroup->setExclusive( true );
00344 
00345   m_prbNone = new QRadioButton( i18n("no key", "&None"), d->fCArea );
00346   d->kbGroup->insert( m_prbNone, NoKey );
00347   m_prbNone->setEnabled( false );
00348   //grid->addMultiCellWidget( rb, 1, 1, 1, 2 );
00349   grid->addWidget( m_prbNone, 1, 0 );
00350   QWhatsThis::add( m_prbNone, i18n("The selected action will not be associated with any key.") );
00351   connect( m_prbNone, SIGNAL(clicked()), SLOT(slotNoKey()) );
00352 
00353   m_prbDef = new QRadioButton( i18n("default key", "De&fault"), d->fCArea );
00354   d->kbGroup->insert( m_prbDef, DefaultKey );
00355   m_prbDef->setEnabled( false );
00356   //grid->addMultiCellWidget( rb, 2, 2, 1, 2 );
00357   grid->addWidget( m_prbDef, 1, 1 );
00358   QWhatsThis::add( m_prbDef, i18n("This will bind the default key to the selected action. Usually a reasonable choice.") );
00359   connect( m_prbDef, SIGNAL(clicked()), SLOT(slotDefaultKey()) );
00360 
00361   m_prbCustom = new QRadioButton( i18n("C&ustom"), d->fCArea );
00362   d->kbGroup->insert( m_prbCustom, CustomKey );
00363   m_prbCustom->setEnabled( false );
00364   //grid->addMultiCellWidget( rb, 3, 3, 1, 2 );
00365   grid->addWidget( m_prbCustom, 1, 2 );
00366   QWhatsThis::add( m_prbCustom, i18n("If this option is selected you can create a customized key binding for the"
00367     " selected action using the buttons below.") );
00368   connect( m_prbCustom, SIGNAL(clicked()), SLOT(slotCustomKey()) );
00369 
00370   //connect( d->kbGroup, SIGNAL( clicked( int ) ), SLOT( keyMode( int ) ) );
00371 
00372   QBoxLayout *pushLayout = new QHBoxLayout( KDialog::spacingHint() );
00373   grid->addLayout( pushLayout, 1, 3 );
00374 
00375   d->pbtnShortcut = new KKeyButton(d->fCArea, "key");
00376   d->pbtnShortcut->setEnabled( false );
00377   connect( d->pbtnShortcut, SIGNAL(capturedShortcut(const KShortcut&)), SLOT(capturedShortcut(const KShortcut&)) );
00378   grid->addRowSpacing( 1, d->pbtnShortcut->sizeHint().height() + 5 );
00379 
00380   wtstr = i18n("Use this button to choose a new shortcut key. Once you click it, "
00381         "you can press the key-combination which you would like to be assigned "
00382         "to the currently selected action.");
00383   QWhatsThis::add( d->pbtnShortcut, wtstr );
00384 
00385   //
00386   // Add widgets to the geometry manager
00387   //
00388   pushLayout->addSpacing( KDialog::spacingHint()*2 );
00389   pushLayout->addWidget( d->pbtnShortcut );
00390   pushLayout->addStretch( 10 );
00391 
00392   d->lInfo = new QLabel(d->fCArea);
00393   //resize(0,0);
00394   //d->lInfo->setAlignment( AlignCenter );
00395   //d->lInfo->setEnabled( false );
00396   //d->lInfo->hide();
00397   grid->addMultiCellWidget( d->lInfo, 2, 2, 0, 3 );
00398 
00399   //d->globalDict = new QDict<int> ( 100, false );
00400   //d->globalDict->setAutoDelete( true );
00401   readGlobalKeys();
00402   //d->stdDict = new QDict<int> ( 100, false );
00403   //d->stdDict->setAutoDelete( true );
00404   //if (type == Application || type == ApplicationGlobal)
00405   //  readStdKeys();
00406   connect( kapp, SIGNAL( settingsChanged( int )), SLOT( slotSettingsChanged( int )));
00407   if( allChoosers == NULL )
00408         allChoosers = allChoosersDeleter.setObject( allChoosers, new QValueList< KKeyChooser* > );
00409   allChoosers->append( this );
00410 }
00411 
00412 // Add all shortcuts to the list
00413 void KKeyChooser::buildListView( uint iList, const QString &title )
00414 {
00415     KShortcutList* pList = d->rgpLists[iList];
00416 
00417         if( m_type == Global || m_type == ApplicationGlobal )
00418         d->pList->setSorting( -1 );
00419     KListViewItem *pProgramItem, *pGroupItem = 0, *pParentItem, *pItem;
00420 
00421     QString str = (title.isEmpty() ? i18n("Shortcuts") : title);
00422     pParentItem = pProgramItem = pItem = new KListViewItem( d->pList, str );
00423     pParentItem->setExpandable( true );
00424     pParentItem->setOpen( true );
00425     pParentItem->setSelectable( false );
00426     uint nSize = pList->count();
00427     for( uint iAction = 0; iAction < nSize; iAction++ ) {
00428         QString sName = pList->name(iAction);
00429         kdDebug(125) << "Key: " << sName << endl;
00430         if( sName.startsWith( "Program:" ) ) {
00431             pItem = new KListViewItem( d->pList, pProgramItem, pList->label(iAction) );
00432             pItem->setSelectable( false );
00433             pItem->setExpandable( true );
00434             pItem->setOpen( true );
00435             if( !pProgramItem->firstChild() )
00436                 delete pProgramItem;
00437             pProgramItem = pParentItem = pItem;
00438         } else if( sName.startsWith( "Group:" ) ) {
00439             pItem = new KListViewItem( pProgramItem, pParentItem, pList->label(iAction) );
00440             pItem->setSelectable( false );
00441             pItem->setExpandable( true );
00442             pItem->setOpen( true );
00443             if( pGroupItem && !pGroupItem->firstChild() )
00444                 delete pGroupItem;
00445             pGroupItem = pParentItem = pItem;
00446         } else if( !sName.isEmpty() && pList->isConfigurable(iAction) )
00447             pItem = new KKeyChooserItem( pParentItem, pItem, pList, iAction );
00448     }
00449     if( !pProgramItem->firstChild() )
00450         delete pProgramItem;
00451     if( pGroupItem && !pGroupItem->firstChild() )
00452         delete pGroupItem;
00453 }
00454 
00455 void KKeyChooser::updateButtons()
00456 {
00457     // Hack: Do this incase we still have changeKey() running.
00458     //  Better would be to capture the mouse pointer so that we can't click
00459     //   around while we're supposed to be entering a key.
00460     //  Better yet would be a modal dialog for changeKey()!
00461     releaseKeyboard();
00462     KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( d->pList->currentItem() );
00463 
00464     if ( !pItem ) {
00465         // if nothing is selected -> disable radio boxes
00466         m_prbNone->setEnabled( false );
00467         m_prbDef->setEnabled( false );
00468         m_prbCustom->setEnabled( false );
00469         d->pbtnShortcut->setEnabled( false );
00470         d->pbtnShortcut->setShortcut( KShortcut(), false );
00471     } else {
00472         bool bConfigurable = pItem->isConfigurable();
00473         bool bQtShortcut = (m_type == Application || m_type == Standard);
00474         const KShortcut& cutDef = pItem->shortcutDefault();
00475 
00476         // Set key strings
00477         QString keyStrCfg = pItem->shortcut().toString();
00478         QString keyStrDef = cutDef.toString();
00479 
00480         d->pbtnShortcut->setShortcut( pItem->shortcut(), bQtShortcut );
00481         //item->setText( 1, keyStrCfg );
00482         pItem->repaint();
00483         d->lInfo->setText( i18n("Default key:") + QString(" %1").arg(keyStrDef.isEmpty() ? i18n("None") : keyStrDef) );
00484 
00485         // Select the appropriate radio button.
00486         int index = (pItem->shortcut().isNull()) ? NoKey
00487                 : (pItem->shortcut() == cutDef) ? DefaultKey
00488                 : CustomKey;
00489         m_prbNone->setChecked( index == NoKey );
00490         m_prbDef->setChecked( index == DefaultKey );
00491         m_prbCustom->setChecked( index == CustomKey );
00492 
00493         // Enable buttons if this key is configurable.
00494         // The 'Default Key' button must also have a default key.
00495         m_prbNone->setEnabled( bConfigurable );
00496         m_prbDef->setEnabled( bConfigurable && cutDef.count() != 0 );
00497         m_prbCustom->setEnabled( bConfigurable );
00498         d->pbtnShortcut->setEnabled( bConfigurable );
00499     }
00500 }
00501 
00502 void KKeyChooser::slotNoKey()
00503 {
00504     // return if no key is selected
00505     KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( d->pList->currentItem() );
00506     if( pItem ) {
00507         //kdDebug(125) << "no Key" << d->pList->currentItem()->text(0) << endl;
00508         pItem->setShortcut( KShortcut() );
00509         updateButtons();
00510         emit keyChange();
00511     }
00512 }
00513 
00514 void KKeyChooser::slotDefaultKey()
00515 {
00516     // return if no key is selected
00517     KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( d->pList->currentItem() );
00518     if( pItem ) // don't set it directly, check for conflicts
00519         setShortcut( pItem->shortcutDefault() );
00520 }
00521 
00522 void KKeyChooser::slotCustomKey()
00523 {
00524     d->pbtnShortcut->captureShortcut();
00525 }
00526 
00527 void KKeyChooser::readGlobalKeys()
00528 {
00529         d->mapGlobals.clear();
00530         if( m_type == Global )
00531             return; // they will be checked normally, because we're configuring them
00532         readGlobalKeys( d->mapGlobals );
00533 }
00534 
00535 void KKeyChooser::readGlobalKeys( QMap< QString, KShortcut >& map )
00536 {
00537     QMap<QString, QString> mapEntry = KGlobal::config()->entryMap( "Global Shortcuts" );
00538     QMap<QString, QString>::Iterator it( mapEntry.begin() );
00539     for( uint i = 0; it != mapEntry.end(); ++it, i++ )
00540         map[it.key()] = KShortcut(*it);
00541 }
00542 
00543 void KKeyChooser::slotSettingsChanged( int category )
00544 {
00545     if( category == KApplication::SETTINGS_SHORTCUTS )
00546         readGlobalKeys(); // reread
00547 }
00548 
00549 void KKeyChooser::fontChange( const QFont & )
00550 {
00551         d->fCArea->setMinimumHeight( 4*d->pbtnShortcut->sizeHint().height() );
00552 
00553         int widget_width = 0;
00554 
00555         setMinimumWidth( 20+5*(widget_width+10) );
00556 }
00557 
00558 // KDE4 IMHO this shouldn't be here at all - it cannot check whether the default
00559 // shortcut don't conflict with some already changed ones (e.g. global shortcuts).
00560 // Also, I personally find reseting all shortcuts to default (i.e. hardcoded in the app)
00561 // ones after pressing the 'Default' button rather a misfeature.
00562 void KKeyChooser::allDefault()
00563 {
00564     kdDebug(125) << "KKeyChooser::allDefault()" << endl;
00565 
00566     QListViewItemIterator it( d->pList );
00567     for( ; it.current(); ++it ) {
00568         KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(it.current());
00569         if( pItem )
00570             pItem->setShortcut( pItem->shortcutDefault() );
00571     }
00572 
00573     updateButtons();
00574     emit keyChange();
00575 }
00576 
00577 void KKeyChooser::slotListItemSelected( QListViewItem* )
00578 {
00579     updateButtons();
00580 }
00581 
00582 void KKeyChooser::slotListItemDoubleClicked ( QListViewItem *, const QPoint & , int )
00583 { // KDE4 dump this
00584   captureCurrentItem();
00585 }
00586 
00587 void KKeyChooser::captureCurrentItem()
00588 {
00589   KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( d->pList->currentItem() );
00590   if( pItem != NULL && pItem->isConfigurable())
00591       d->pbtnShortcut->captureShortcut ( );
00592 }
00593 
00594 void KKeyChooser::setPreferFourModifierKeys( bool bPreferFourModifierKeys )
00595 {
00596     d->bPreferFourModifierKeys = bPreferFourModifierKeys;
00597 }
00598 
00599 void KKeyChooser::capturedShortcut( const KShortcut& cut )
00600 {
00601     if( cut.isNull() )
00602         slotNoKey();
00603     else
00604         setShortcut( cut );
00605 }
00606 
00607 // FIXME: give this functionality again -- I don't think it's ever used, though. -- ellis
00608 // TODO: Check lxr.kde.org to see if it's used anywhere
00609 void KKeyChooser::listSync()
00610 {
00611 /*  kdDebug(125) << "KKeyChooser::listSync()" << endl;
00612 
00613     if( d->pColl ) {
00614         // TODO: This is very inefficient.  Come up with something better.
00615         KAccelActions aa;
00616         d->pColl->createKeyMap( aa );
00617         d->actionsNew.updateShortcuts( aa );
00618     } else if( d->pActionsOrig ) {
00619         d->actionsNew.updateShortcuts( *d->pActionsOrig );
00620         update();
00621         updateButtons();
00622     }*/
00623 }
00624 
00625 void KKeyChooser::syncToConfig( const QString& sConfigGroup, KConfigBase* pConfig, bool bClearUnset )
00626 {
00627     kdDebug(125) << "KKeyChooser::syncToConfig( \"" << sConfigGroup << "\", " << pConfig << " ) start" << endl;
00628     if( !pConfig )
00629         pConfig = KGlobal::config();
00630     KConfigGroupSaver cgs( pConfig, sConfigGroup );
00631 
00632     QListViewItemIterator it( d->pList );
00633     for( ; it.current(); ++it ) {
00634         KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(it.current());
00635         if( pItem ) {
00636             QString sEntry = pConfig->readEntry( pItem->actionName() );
00637             if( !sEntry.isNull() || bClearUnset ) {
00638                 if( sEntry == "none" )
00639                     sEntry = QString::null;
00640                 pItem->setShortcut( sEntry );
00641             }
00642             kdDebug(125) << pItem->actionName() << " = " << pItem->shortcut().toStringInternal() << endl;
00643         }
00644     }
00645     updateButtons();
00646     kdDebug(125) << "KKeyChooser::syncToConfig() done" << endl;
00647 }
00648 
00649 void KKeyChooser::setShortcut( const KShortcut& cut )
00650 {
00651     kdDebug(125) << "KKeyChooser::setShortcut( " << cut.toString() << " )" << endl;
00652     KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(d->pList->currentItem());
00653     if( !pItem )
00654         return;
00655 
00656     for( uint i = 0; i < cut.count(); i++ ) {
00657         const KKeySequence& seq = cut.seq(i);
00658         const KKey& key = seq.key(0);
00659 
00660         if( !d->bAllowLetterShortcuts && key.modFlags() == 0
00661             && key.sym() < 0x3000 && QChar(key.sym()).isLetterOrNumber() ) {
00662             QString s = i18n(   "In order to use the '%1' key as a shortcut, "
00663                         "it must be combined with the "
00664                         "Win, Alt, Ctrl, and/or Shift keys." ).arg(QChar(key.sym()));
00665             KMessageBox::sorry( this, s, i18n("Invalid Shortcut Key") );
00666             return;
00667         }
00668     }
00669 
00670     // If key isn't already in use,
00671     if( !isKeyPresent( cut ) ) {
00672         // Set new key code
00673         pItem->setShortcut( cut );
00674         // Update display
00675         updateButtons();
00676         emit keyChange();
00677     }
00678 }
00679 
00680 // Returns iSeq index if cut2 has a sequence of equal or higher priority to a sequence in cut.
00681 // else -1
00682 static int keyConflict( const KShortcut& cut, const KShortcut& cut2 )
00683 {
00684     for( uint iSeq = 0; iSeq < cut.count(); iSeq++ ) {
00685         for( uint iSeq2 = 0; iSeq2 <= iSeq && iSeq2 < cut2.count(); iSeq2++ ) {
00686             if( cut.seq(iSeq) == cut2.seq(iSeq2) )
00687                 return iSeq;
00688         }
00689     }
00690     return -1;
00691 }
00692 
00693 bool KKeyChooser::isKeyPresent( const KShortcut& cut, bool bWarnUser )
00694 {
00695     KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(d->pList->currentItem());
00696 
00697     if (!pItem) {
00698         return false;
00699     }
00700 
00701         bool has_global_chooser = false;
00702         bool has_standard_chooser = false;
00703         for( QValueList< KKeyChooser* >::ConstIterator it = allChoosers->begin();
00704              it != allChoosers->end();
00705              ++it ) {
00706             has_global_chooser |= ((*it)->m_type == Global);
00707             has_standard_chooser |= ((*it)->m_type == Standard);
00708         }
00709 
00710     // If editing global shortcuts, check them for conflicts with the stdaccels.
00711     if( m_type == ApplicationGlobal || m_type == Global ) {
00712             if( !has_standard_chooser ) {
00713                 if( checkStandardShortcutsConflict( cut, bWarnUser, this ))
00714                     return true;
00715             }
00716     }
00717 
00718         // only check the global keys if one of the keychoosers isn't global
00719         if( !has_global_chooser ) {
00720             if( checkGlobalShortcutsConflict( cut, bWarnUser, this, d->mapGlobals,
00721                 m_type == Global ? pItem->actionName() : QString::null ))
00722                 return true;
00723         }
00724 
00725         if( isKeyPresentLocally( cut, pItem, bWarnUser ))
00726             return true;
00727 
00728         // check also other KKeyChooser's
00729         for( QValueList< KKeyChooser* >::ConstIterator it = allChoosers->begin();
00730              it != allChoosers->end();
00731              ++it ) {
00732             if( (*it) != this && (*it)->isKeyPresentLocally( cut, NULL, bWarnUser ))
00733                     return true;
00734             }
00735     return false;
00736 }
00737 
00738 // KDE4 remove
00739 bool KKeyChooser::isKeyPresentLocally( const KShortcut& cut, KKeyChooserItem* ignoreItem, const QString& warnText )
00740 {
00741     return isKeyPresentLocally( cut, ignoreItem, !warnText.isNull());
00742 }
00743 
00744 bool KKeyChooser::isKeyPresentLocally( const KShortcut& cut, KKeyChooserItem* ignoreItem, bool bWarnUser )
00745 {
00746     if ( cut.toString().isEmpty())
00747         return false;
00748     // Search for shortcut conflicts with other actions in the
00749     //  lists we're configuring.
00750     for( QListViewItemIterator it( d->pList ); it.current(); ++it ) {
00751         KKeyChooserItem* pItem2 = dynamic_cast<KKeyChooserItem*>(it.current());
00752         if( pItem2 && pItem2 != ignoreItem ) {
00753             int iSeq = keyConflict( cut, pItem2->shortcut() );
00754             if( iSeq > -1 ) {
00755                 if( bWarnUser ) {
00756                                         if( !promptForReassign( cut.seq(iSeq), pItem2->text(0), Application, this ))
00757                                 return true;
00758                                         // else remove the shortcut from it
00759                                         pItem2->setShortcut( KShortcut());
00760                                         updateButtons();
00761                                 emit keyChange();
00762                                 }
00763             }
00764         }
00765     }
00766         return false;
00767 }
00768 
00769 bool KKeyChooser::checkStandardShortcutsConflict( const KShortcut& cut, bool bWarnUser, QWidget* parent )
00770 {
00771     // For each key sequence in the shortcut,
00772     for( uint i = 0; i < cut.count(); i++ ) {
00773     const KKeySequence& seq = cut.seq(i);
00774     KStdAccel::StdAccel id = KStdAccel::findStdAccel( seq );
00775     if( id != KStdAccel::AccelNone
00776         && keyConflict( cut, KStdAccel::shortcut( id ) ) > -1 ) {
00777         if( bWarnUser ) {
00778             if( !promptForReassign( seq, KStdAccel::label(id), Standard, parent ))
00779                                 return true;
00780                         removeStandardShortcut( KStdAccel::label(id), dynamic_cast< KKeyChooser* > ( parent ));
00781                 }
00782     }
00783     }
00784     return false;
00785 }
00786 
00787 bool KKeyChooser::checkGlobalShortcutsConflict( const KShortcut& cut, bool bWarnUser, QWidget* parent )
00788 {
00789     QMap< QString, KShortcut > map;
00790     readGlobalKeys( map );
00791     return checkGlobalShortcutsConflict( cut, bWarnUser, parent, map, QString::null );
00792 }
00793 
00794 bool KKeyChooser::checkGlobalShortcutsConflict( const KShortcut& cut, bool bWarnUser, QWidget* parent,
00795     const QMap< QString, KShortcut >& map, const QString& ignoreAction )
00796 {
00797     QMap<QString, KShortcut>::ConstIterator it;
00798     for( it = map.begin(); it != map.end(); ++it ) {
00799         int iSeq = keyConflict( cut, (*it) );
00800         if( iSeq > -1 ) {
00801             if( ignoreAction.isEmpty() || it.key() != ignoreAction ) {
00802                     if( bWarnUser ) {
00803             if( !promptForReassign( cut.seq(iSeq), it.key(), Global, parent ))
00804                                     return true;
00805                             removeGlobalShortcut( it.key(), dynamic_cast< KKeyChooser* >( parent ));
00806                     }
00807         }
00808     }
00809     }
00810     return false;
00811 }
00812 
00813 void KKeyChooser::removeStandardShortcut( const QString& name, KKeyChooser* chooser )
00814 {
00815     bool was_in_choosers = false;
00816     if( allChoosers != NULL ) {
00817         for( QValueList< KKeyChooser* >::ConstIterator it = allChoosers->begin();
00818              it != allChoosers->end();
00819              ++it ) {
00820             if( (*it) != chooser && (*it)->m_type == Standard ) {
00821                 was_in_choosers |= ( (*it)->resetShortcut( name ));
00822             }
00823         }
00824     }
00825     if( !was_in_choosers ) { // not edited, needs to be changed in config file
00826         KStdAccel::ShortcutList std_list;
00827         std_list.setShortcut( std_list.index( name ), KShortcut());
00828         std_list.save();
00829     }
00830 }
00831 
00832 void KKeyChooser::removeGlobalShortcut( const QString& name, KKeyChooser* chooser )
00833 {
00834     bool was_in_choosers = false;
00835     if( allChoosers != NULL ) {
00836         for( QValueList< KKeyChooser* >::ConstIterator it = allChoosers->begin();
00837              it != allChoosers->end();
00838              ++it ) {
00839             if( (*it) != chooser && (*it)->m_type == Global ) {
00840                 was_in_choosers |= ( (*it)->resetShortcut( name ));
00841             }
00842         }
00843     }
00844     if( !was_in_choosers ) { // not edited, needs to be changed in config file
00845         KAccelActions actions;
00846         actions.insert( name, "", "", KShortcut(), KShortcut());
00847     actions.writeActions( "Global Shortcuts", 0, true, true );
00848     }
00849 }
00850 
00851 bool KKeyChooser::resetShortcut( const QString& name )
00852 {
00853     for( QListViewItemIterator it( d->pList ); it.current(); ++it ) {
00854         KKeyChooserItem* pItem2 = dynamic_cast<KKeyChooserItem*>(it.current());
00855                     if( pItem2 && pItem2->actionName() == name ) {
00856                         pItem2->setShortcut( KShortcut());
00857                         updateButtons();
00858                 emit keyChange();
00859                         return true;
00860                     }
00861         }
00862         return false;
00863 }
00864 
00865 // KDE4 remove this
00866 void KKeyChooser::_warning( const KKeySequence& cut, QString sAction, QString sTitle )
00867 {
00868     sAction = sAction.stripWhiteSpace();
00869 
00870     QString s =
00871         i18n("The '%1' key combination has already been allocated "
00872         "to the \"%2\" action.\n"
00873         "Please choose a unique key combination.").
00874         arg(cut.toString()).arg(sAction);
00875 
00876     KMessageBox::sorry( this, s, sTitle );
00877 }
00878 
00879 bool KKeyChooser::promptForReassign( const KKeySequence& cut, const QString& sAction, ActionType type, QWidget* parent )
00880 {
00881         QString sTitle;
00882         QString s;
00883         if( type == Standard ) {
00884                 sTitle = i18n("Conflict with Standard Application Shortcut");
00885         s = i18n("The '%1' key combination has already been allocated "
00886         "to the standard action \"%2\".\n"
00887         "Do you want to reassign it from that action to the current one?");
00888         }
00889         else if( type == Global ) {
00890                 sTitle = i18n("Conflict with Global Shortcut");
00891         s = i18n("The '%1' key combination has already been allocated "
00892         "to the global action \"%2\".\n"
00893         "Do you want to reassign it from that action to the current one?");
00894         }
00895         else {
00896                 sTitle = i18n("Key Conflict");
00897         s = i18n("The '%1' key combination has already been allocated "
00898         "to the \"%2\" action.\n"
00899         "Do you want to reassign it from that action to the current one?");
00900         }
00901     s = s.arg(cut.toString()).arg(sAction.stripWhiteSpace());
00902 
00903     return KMessageBox::warningYesNo( parent, s, sTitle ) == KMessageBox::Yes;
00904 }
00905 
00906 //---------------------------------------------------
00907 KKeyChooserItem::KKeyChooserItem( KListView* parent, QListViewItem* after, KShortcutList* pList, uint iAction )
00908 :   KListViewItem( parent, after )
00909 {
00910     m_pList = pList;
00911     m_iAction = iAction;
00912     m_bModified = false;
00913     m_cut = m_pList->shortcut(m_iAction);
00914 }
00915 
00916 KKeyChooserItem::KKeyChooserItem( QListViewItem* parent, QListViewItem* after, KShortcutList* pList, uint iAction )
00917 :   KListViewItem( parent, after )
00918 {
00919     m_pList = pList;
00920     m_iAction = iAction;
00921     m_bModified = false;
00922     m_cut = m_pList->shortcut(m_iAction);
00923 }
00924 
00925 QString KKeyChooserItem::actionName() const
00926 {
00927     return m_pList->name(m_iAction);
00928 }
00929 
00930 const KShortcut& KKeyChooserItem::shortcut() const
00931 {
00932     return m_cut;
00933 }
00934 
00935 void KKeyChooserItem::setShortcut( const KShortcut& cut )
00936 {
00937     m_cut = cut;
00938     m_bModified = (m_cut != m_pList->shortcut(m_iAction));
00939     listView()->repaintItem( this );
00940 }
00941 
00942 void KKeyChooserItem::commitChanges()
00943 {
00944     if( m_bModified )
00945         m_pList->setShortcut( m_iAction, m_cut );
00946 }
00947 
00948 QString KKeyChooserItem::text( int iCol ) const
00949 {
00950     if( iCol == 0 ) {
00951         // Quick HACK to get rid of '&'s.
00952         QString s = m_pList->label(m_iAction);
00953         QString s2;
00954         for( uint i = 0; i < s.length(); i++ )
00955             if( s[i] != '&' || ( i+1<s.length() && s[i+1] == '&' ) )
00956                 s2 += s[i];
00957         return s2;
00958     }
00959     else if( iCol <= (int) m_cut.count() )
00960         return m_cut.seq(iCol-1).toString();
00961     else
00962         return QString::null;
00963 }
00964 
00965 int KKeyChooserItem::compare( QListViewItem* item, int iCol, bool bAscending ) const
00966 {
00967     KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( item );
00968     if( iCol == 0 && pItem ) {
00969         QString psName1 = m_pList->name(m_iAction);
00970         QString psName2 = pItem->m_pList->name(pItem->m_iAction);
00971         QRegExp rxNumber1( " (\\d+)$" );
00972         QRegExp rxNumber2( " (\\d+)$" );
00973         int iNumber1 = rxNumber1.search( psName1 );
00974         int iNumber2 = rxNumber2.search( psName2 );
00975 
00976         // Check if the last word is one or more digits
00977         if( iNumber1 >= 0 && iNumber1 == iNumber2 && psName1.startsWith( psName2.left( iNumber1+1 ) ) ) {
00978             int n1 = rxNumber1.cap(1).toInt();
00979             int n2 = rxNumber2.cap(1).toInt();
00980             return (n1 < n2) ? -1 : (n1 > n2) ? 1 : 0;
00981         }
00982     }
00983 
00984     return QListViewItem::compare( item, iCol, bAscending );
00985 }
00986 
00987 /************************************************************************/
00988 /* KKeyDialog                                                           */
00989 /*                                                                      */
00990 /* Originally by Nicolas Hadacek <hadacek@via.ecp.fr>                   */
00991 /*                                                                      */
00992 /* Substantially revised by Mark Donohoe <donohoe@kde.org>              */
00993 /*                                                                      */
00994 /* And by Espen Sand <espen@kde.org> 1999-10-19                         */
00995 /* (by using KDialogBase there is almost no code left ;)                */
00996 /*                                                                      */
00997 /************************************************************************/
00998 KKeyDialog::KKeyDialog( KKeyChooser::ActionType type, bool bAllowLetterShortcuts, QWidget *parent, const char* name )
00999 : KDialogBase( parent, name, true, i18n("Configure Shortcuts"), Help|Default|Ok|Cancel, Ok )
01000 {
01001     m_pKeyChooser = new KKeyChooser( this, type, bAllowLetterShortcuts );
01002     setMainWidget( m_pKeyChooser );
01003     connect( this, SIGNAL(defaultClicked()), m_pKeyChooser, SLOT(allDefault()) );
01004     enableButton( Help, false );
01005 
01006     KConfigGroup group( KGlobal::config(), "KKeyDialog Settings" );
01007     QSize sz = size();
01008     resize( group.readSizeEntry( "Dialog Size", &sz ) );
01009 }
01010 
01011 KKeyDialog::KKeyDialog( bool bAllowLetterShortcuts, QWidget *parent, const char* name )
01012 : KDialogBase( parent, name, true, i18n("Configure Shortcuts"), Help|Default|Ok|Cancel, Ok )
01013 {
01014     m_pKeyChooser = new KKeyChooser( this, KKeyChooser::Application, bAllowLetterShortcuts );
01015     setMainWidget( m_pKeyChooser );
01016     connect( this, SIGNAL(defaultClicked()), m_pKeyChooser, SLOT(allDefault()) );
01017     enableButton( Help, false );
01018 
01019     KConfigGroup group( KGlobal::config(), "KKeyDialog Settings" );
01020     QSize sz = size();
01021     resize( group.readSizeEntry( "Dialog Size", &sz ) );
01022 }
01023 
01024 KKeyDialog::~KKeyDialog()
01025 {
01026     KConfigGroup group( KGlobal::config(), "KKeyDialog Settings" );
01027     group.writeEntry( "Dialog Size", size(), true, true );
01028 }
01029 
01030 bool KKeyDialog::insert( KActionCollection* pColl )
01031 {
01032     return m_pKeyChooser->insert( pColl );
01033 }
01034 
01035 bool KKeyDialog::insert(KActionCollection *pColl, const QString &title)
01036 {
01037     return m_pKeyChooser->insert(pColl, title);
01038 }
01039 
01040 bool KKeyDialog::configure( bool bSaveSettings )
01041 {
01042     int retcode = exec();
01043     if( retcode == Accepted ) {
01044         if( bSaveSettings )
01045             m_pKeyChooser->save();
01046         else
01047             commitChanges();
01048     }
01049     return retcode;
01050 }
01051 
01052 void KKeyDialog::commitChanges()
01053 {
01054     m_pKeyChooser->commitChanges();
01055 }
01056 
01057 int KKeyDialog::configure( KActionCollection* coll, QWidget* parent, bool bSaveSettings )
01058 {
01059     return configure( coll, true, parent, bSaveSettings);
01060 }
01061 
01062 int KKeyDialog::configure( KAccel* keys, QWidget* parent, bool bSaveSettings )
01063 {
01064     return configure( keys, true, parent, bSaveSettings);
01065 }
01066 
01067 int KKeyDialog::configure( KGlobalAccel* keys, QWidget* parent, bool bSaveSettings )
01068 {
01069     return configure( keys, true, parent, bSaveSettings);
01070 }
01071 
01072 int KKeyDialog::configure( KAccel* keys, bool bAllowLetterShortcuts, QWidget *parent, bool bSaveSettings )
01073 {
01074     KKeyDialog dlg( bAllowLetterShortcuts, parent );
01075     dlg.m_pKeyChooser->insert( keys );
01076     bool b = dlg.configure( bSaveSettings );
01077     if( b && bSaveSettings )
01078         keys->updateConnections();
01079     return b;
01080 }
01081 
01082 int KKeyDialog::configure( KGlobalAccel* keys, bool bAllowLetterShortcuts, QWidget *parent, bool bSaveSettings )
01083 {
01084     KKeyDialog dlg( KKeyChooser::ApplicationGlobal, bAllowLetterShortcuts, parent );
01085     dlg.m_pKeyChooser->insert( keys );
01086     bool b = dlg.configure( bSaveSettings );
01087     if( b && bSaveSettings )
01088         keys->updateConnections();
01089     return b;
01090 }
01091 
01092 int KKeyDialog::configure( KActionCollection* coll, bool bAllowLetterShortcuts, QWidget *parent, bool bSaveSettings )
01093 {
01094     kdDebug(125) << "KKeyDialog::configureKeys( KActionCollection*, " << bSaveSettings << " )" << endl;
01095     KKeyDialog dlg( bAllowLetterShortcuts, parent );
01096     dlg.m_pKeyChooser->insert( coll );
01097     return dlg.configure( bSaveSettings );
01098 }
01099 
01100 /*int KKeyDialog::configure( KActionPtrList* coll, const QString& file, QWidget *parent, bool bSaveSettings )
01101 {
01102     kdDebug(125) << "KKeyDialog::configureKeys( KActionCollection*, " << file << ", " << bSaveSettings << " )" << endl;
01103     KAccelActions actions;
01104     coll->createKeyMap( actions );
01105 
01106     int retcode = configure( actions, file, parent, bSaveSettings );
01107     if( retcode == Accepted )
01108         coll->setKeyMap( actions );
01109 
01110     return retcode;
01111 }*/
01112 
01113 void KKeyChooser::virtual_hook( int, void* )
01114 { /*BASE::virtual_hook( id, data );*/ }
01115 
01116 void KKeyDialog::virtual_hook( int id, void* data )
01117 { KDialogBase::virtual_hook( id, data ); }
01118 
01119 #include "kkeydialog.moc"
KDE Logo
This file is part of the documentation for kdeui Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Feb 14 09:16:33 2006 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003