/* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe <donohoe@kde.org> Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org> Copyright (C) 1998 Matthias Ettrich <ettrich@kde.org> Copyright (C) 2001 Ellis Whitehead <ellis@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kkeydialog.h" #include "kkeybutton.h" #include <string.h> #include <tqbuttongroup.h> #include <tqlabel.h> #include <tqlayout.h> #include <tqdrawutil.h> #include <tqpainter.h> #include <tqradiobutton.h> #include <tqregexp.h> #include <tqtoolbutton.h> #include <tqwhatsthis.h> #include <tdeaccel.h> #include <tdeaction.h> #include <tdeaccelaction.h> #include <tdeactionshortcutlist.h> #include <tdeapplication.h> #include <tdeconfig.h> #include <kdebug.h> #include <tdeglobal.h> #include <kglobalaccel.h> #include <kiconloader.h> #include <tdelistviewsearchline.h> #include <tdelocale.h> #include <tdemessagebox.h> #include <tdeshortcut.h> #include <tdeshortcutlist.h> #include <kxmlguifactory.h> #include <tdeaboutdata.h> #include <kstaticdeleter.h> #ifdef Q_WS_X11 #define XK_XKB_KEYS #define XK_MISCELLANY #include <X11/Xlib.h> // For x11Event() #include <X11/keysymdef.h> // For XK_... #include <tqwhatsthis.h> #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 // KEYPRESS #endif // Q_WX_X11 //--------------------------------------------------------------------- // KKeyChooserItem //--------------------------------------------------------------------- class KKeyChooserItem : public TDEListViewItem { public: KKeyChooserItem( TDEListView* parent, TQListViewItem* after, TDEShortcutList* pList, uint iAction ); KKeyChooserItem( TQListViewItem* parent, TQListViewItem* after, TDEShortcutList* pList, uint iAction ); TQString actionName() const; const TDEShortcut& shortcut() const; bool isConfigurable() const { return m_pList->isConfigurable( m_iAction ); } const TDEShortcut& shortcutDefault() const { return m_pList->shortcutDefault( m_iAction ); } TQString whatsThis() const { return m_pList->whatsThis( m_iAction ); } void setShortcut( const TDEShortcut& cut ); void commitChanges(); virtual TQString text( int iCol ) const; virtual int compare( TQListViewItem*, int iCol, bool bAscending ) const; protected: TDEShortcutList* m_pList; uint m_iAction; bool m_bModified; TDEShortcut m_cut; }; // WhatsThis on KKeyChooserItems class KKeyChooserWhatsThis : public TQWhatsThis { public: KKeyChooserWhatsThis( TQListView* listview ) : TQWhatsThis( listview->viewport() ), m_listView( listview ) {} protected: virtual TQString text( const TQPoint& p ); private: TQListView* m_listView; }; //--------------------------------------------------------------------- // KKeyChooserPrivate //--------------------------------------------------------------------- class KKeyChooserPrivate { public: TQValueList<TDEShortcutList*> rgpLists; TQValueList<TDEShortcutList*> rgpListsAllocated; TDEListView *pList; TQLabel *lInfo; KKeyButton *pbtnShortcut; TQGroupBox *fCArea; TQButtonGroup *kbGroup; TQMap<TQString, TDEShortcut> mapGlobals; // 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; }; //--------------------------------------------------------------------- // KKeyChooser //--------------------------------------------------------------------- KKeyChooser::KKeyChooser( TQWidget* parent, ActionType type, bool bAllowLetterShortcuts ) : TQWidget( parent ) { initGUI( type, bAllowLetterShortcuts ); } KKeyChooser::KKeyChooser( TDEActionCollection* coll, TQWidget* parent, bool bAllowLetterShortcuts ) : TQWidget( parent ) { initGUI( Application, bAllowLetterShortcuts ); insert( coll ); } KKeyChooser::KKeyChooser( TDEAccel* pAccel, TQWidget* parent, bool bAllowLetterShortcuts ) : TQWidget( parent ) { initGUI( Application, bAllowLetterShortcuts ); insert( pAccel ); } KKeyChooser::KKeyChooser( TDEGlobalAccel* pAccel, TQWidget* parent ) : TQWidget( parent ) { initGUI( ApplicationGlobal, false ); insert( pAccel ); } KKeyChooser::KKeyChooser( TDEShortcutList* pList, TQWidget* parent, ActionType type, bool bAllowLetterShortcuts ) : TQWidget( parent ) { initGUI( type, bAllowLetterShortcuts ); insert( pList ); } KKeyChooser::KKeyChooser( TDEAccel* actions, TQWidget* parent, bool bCheckAgainstStdKeys, bool bAllowLetterShortcuts, bool bAllowWinKey ) : TQWidget( parent ) { ActionType type; if( bAllowWinKey ) type = (bCheckAgainstStdKeys) ? ApplicationGlobal : Global; else type = Application; initGUI( type, bAllowLetterShortcuts ); insert( actions ); } KKeyChooser::KKeyChooser( TDEGlobalAccel* actions, TQWidget* parent, bool bCheckAgainstStdKeys, bool bAllowLetterShortcuts, bool /*bAllowWinKey*/ ) : TQWidget( parent ) { ActionType type = (bCheckAgainstStdKeys) ? ApplicationGlobal : Global; initGUI( type, bAllowLetterShortcuts ); insert( actions ); } // list of all existing KKeyChooser's // Used when checking global shortcut for a possible conflict // (just checking against kdeglobals isn't enough, the shortcuts // might have changed in KKeyChooser and not being saved yet). // Also used when reassigning a shortcut from one chooser to another. static TQValueList< KKeyChooser* >* allChoosers = NULL; static KStaticDeleter< TQValueList< KKeyChooser* > > allChoosersDeleter; KKeyChooser::~KKeyChooser() { allChoosers->remove( this ); // Delete allocated TDEShortcutLists for( uint i = 0; i < d->rgpListsAllocated.count(); i++ ) delete d->rgpListsAllocated[i]; delete d; } bool KKeyChooser::insert( TDEActionCollection *pColl) { return insert(pColl, TQString::null); } bool KKeyChooser::insert( TDEActionCollection* pColl, const TQString &title ) { TQString str = title; if ( title.isEmpty() && pColl->instance() && pColl->instance()->aboutData() ) str = pColl->instance()->aboutData()->programName(); TDEShortcutList* pList = new TDEActionShortcutList( pColl ); d->rgpListsAllocated.append( pList ); d->rgpLists.append( pList ); buildListView(d->rgpLists.count() - 1, str); return true; } bool KKeyChooser::insert( TDEAccel* pAccel ) { TDEShortcutList* pList = new TDEAccelShortcutList( pAccel ); d->rgpListsAllocated.append( pList ); return insert( pList ); } bool KKeyChooser::insert( TDEGlobalAccel* pAccel ) { TDEShortcutList* pList = new TDEAccelShortcutList( pAccel ); d->rgpListsAllocated.append( pList ); return insert( pList ); } bool KKeyChooser::insert( TDEShortcutList* pList ) { d->rgpLists.append( pList ); buildListView( d->rgpLists.count() - 1, TQString::null ); return true; } void KKeyChooser::commitChanges() { kdDebug(125) << "KKeyChooser::commitChanges()" << endl; TQListViewItemIterator it( d->pList ); for( ; it.current(); ++it ) { KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(it.current()); if( pItem ) pItem->commitChanges(); } } void KKeyChooser::save() { commitChanges(); for( uint i = 0; i < d->rgpLists.count(); i++ ) d->rgpLists[i]->save(); } void KKeyChooser::initGUI( ActionType type, bool bAllowLetterShortcuts ) { d = new KKeyChooserPrivate(); m_type = type; d->bAllowLetterShortcuts = bAllowLetterShortcuts; d->bPreferFourModifierKeys = TDEGlobalAccel::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. // TQBoxLayout *topLayout = new TQVBoxLayout( this, 0, KDialog::spacingHint() ); // // ADD SEARCHLINE // TQHBoxLayout* searchLayout = new TQHBoxLayout(0, 0, KDialog::spacingHint()); topLayout->addLayout(searchLayout, 10); TQToolButton *clearSearch = new TQToolButton(this); clearSearch->setTextLabel(i18n("Clear Search"), true); clearSearch->setIconSet(SmallIconSet(TQApplication::reverseLayout() ? "clear_left" : "locationbar_erase")); searchLayout->addWidget(clearSearch); TQLabel* slbl = new TQLabel(i18n("&Search:"), this); searchLayout->addWidget(slbl); TDEListViewSearchLine* listViewSearch = new TDEListViewSearchLine(this); searchLayout->addWidget(listViewSearch); slbl->setBuddy(listViewSearch); connect(clearSearch, TQT_SIGNAL(pressed()), listViewSearch, TQT_SLOT(clear())); TQString wtstr = i18n("Search interactively for shortcut names (e.g. Copy) " "or combination of keys (e.g. Ctrl+C) by typing them here."); TQWhatsThis::add(slbl, wtstr); TQWhatsThis::add(listViewSearch, wtstr); // // CREATE SPLIT LIST BOX // // fill up the split list box with the action/key pairs. // TQGridLayout *stackLayout = new TQGridLayout(2, 2, 2); topLayout->addLayout( TQT_TQLAYOUT(stackLayout), 10 ); stackLayout->setRowStretch( 1, 10 ); // Only list will stretch d->pList = new TDEListView( this ); listViewSearch->setListView(d->pList); // Plug into search line TQValueList<int> columns; columns.append(0); listViewSearch->setSearchColumns(columns); stackLayout->addMultiCellWidget( d->pList, 1, 1, 0, 1 ); wtstr = i18n("Here you can see a list of key bindings, " "i.e. associations between actions (e.g. 'Copy') " "shown in the left column and keys or combination " "of keys (e.g. Ctrl+V) shown in the right column."); TQWhatsThis::add( d->pList, wtstr ); new KKeyChooserWhatsThis( d->pList ); d->pList->setAllColumnsShowFocus( true ); d->pList->addColumn(i18n("Action")); d->pList->addColumn(i18n("Shortcut")); d->pList->addColumn(i18n("Alternate")); connect( d->pList, TQT_SIGNAL(currentChanged(TQListViewItem*)), TQT_SLOT(slotListItemSelected(TQListViewItem*)) ); // handle double clicking an item connect ( d->pList, TQT_SIGNAL ( doubleClicked ( TQListViewItem *, const TQPoint &, int ) ), TQT_SLOT ( captureCurrentItem()) ); connect ( d->pList, TQT_SIGNAL ( spacePressed( TQListViewItem* )), TQT_SLOT( captureCurrentItem())); // // CREATE CHOOSE KEY GROUP // d->fCArea = new TQGroupBox( this ); topLayout->addWidget( d->fCArea, 1 ); d->fCArea->setTitle( i18n("Shortcut for Selected Action") ); d->fCArea->setFrameStyle( TQFrame::GroupBoxPanel | TQFrame::Plain ); // // CHOOSE KEY GROUP LAYOUT MANAGER // TQGridLayout *grid = new TQGridLayout( d->fCArea, 3, 4, KDialog::spacingHint() ); grid->addRowSpacing( 0, fontMetrics().lineSpacing() ); d->kbGroup = new TQButtonGroup( d->fCArea ); d->kbGroup->hide(); d->kbGroup->setExclusive( true ); m_prbNone = new TQRadioButton( i18n("no key", "&None"), d->fCArea ); d->kbGroup->insert( m_prbNone, NoKey ); m_prbNone->setEnabled( false ); //grid->addMultiCellWidget( rb, 1, 1, 1, 2 ); grid->addWidget( m_prbNone, 1, 0 ); TQWhatsThis::add( m_prbNone, i18n("The selected action will not be associated with any key.") ); connect( m_prbNone, TQT_SIGNAL(clicked()), TQT_SLOT(slotNoKey()) ); m_prbDef = new TQRadioButton( i18n("default key", "De&fault"), d->fCArea ); d->kbGroup->insert( m_prbDef, DefaultKey ); m_prbDef->setEnabled( false ); //grid->addMultiCellWidget( rb, 2, 2, 1, 2 ); grid->addWidget( m_prbDef, 1, 1 ); TQWhatsThis::add( m_prbDef, i18n("This will bind the default key to the selected action. Usually a reasonable choice.") ); connect( m_prbDef, TQT_SIGNAL(clicked()), TQT_SLOT(slotDefaultKey()) ); m_prbCustom = new TQRadioButton( i18n("C&ustom"), d->fCArea ); d->kbGroup->insert( m_prbCustom, CustomKey ); m_prbCustom->setEnabled( false ); //grid->addMultiCellWidget( rb, 3, 3, 1, 2 ); grid->addWidget( m_prbCustom, 1, 2 ); TQWhatsThis::add( m_prbCustom, i18n("If this option is selected you can create a customized key binding for the" " selected action using the buttons below.") ); connect( m_prbCustom, TQT_SIGNAL(clicked()), TQT_SLOT(slotCustomKey()) ); //connect( d->kbGroup, TQT_SIGNAL( clicked( int ) ), TQT_SLOT( keyMode( int ) ) ); TQBoxLayout *pushLayout = new TQHBoxLayout( KDialog::spacingHint() ); grid->addLayout( pushLayout, 1, 3 ); d->pbtnShortcut = new KKeyButton(d->fCArea, "key"); d->pbtnShortcut->setEnabled( false ); connect( d->pbtnShortcut, TQT_SIGNAL(capturedShortcut(const TDEShortcut&)), TQT_SLOT(capturedShortcut(const TDEShortcut&)) ); grid->addRowSpacing( 1, d->pbtnShortcut->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."); TQWhatsThis::add( d->pbtnShortcut, wtstr ); // // Add widgets to the geometry manager // pushLayout->addSpacing( KDialog::spacingHint()*2 ); pushLayout->addWidget( d->pbtnShortcut ); pushLayout->addStretch( 10 ); d->lInfo = new TQLabel(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 TQDict<int> ( 100, false ); //d->globalDict->setAutoDelete( true ); readGlobalKeys(); //d->stdDict = new TQDict<int> ( 100, false ); //d->stdDict->setAutoDelete( true ); //if (type == Application || type == ApplicationGlobal) // readStdKeys(); connect( kapp, TQT_SIGNAL( settingsChanged( int )), TQT_SLOT( slotSettingsChanged( int ))); if( allChoosers == NULL ) allChoosers = allChoosersDeleter.setObject( allChoosers, new TQValueList< KKeyChooser* > ); allChoosers->append( this ); } // Add all shortcuts to the list void KKeyChooser::buildListView( uint iList, const TQString &title ) { TDEShortcutList* pList = d->rgpLists[iList]; TDEActionShortcutList *pAList = dynamic_cast<TDEActionShortcutList*>(pList); if( m_type == Global || m_type == ApplicationGlobal ) d->pList->setSorting( -1 ); TDEListViewItem *pProgramItem, *pGroupItem = 0, *pParentItem, *pItem; TQString str = (title.isEmpty() ? i18n("Shortcuts") : title); pParentItem = pProgramItem = pItem = new TDEListViewItem( d->pList, str ); pParentItem->setExpandable( true ); pParentItem->setOpen( true ); pParentItem->setSelectable( false ); uint nSize = pList->count(); for( uint iAction = 0; iAction < nSize; iAction++ ) { TQString sName = pList->name(iAction); kdDebug(125) << "Key: " << sName << endl; if( sName.startsWith( "Program:" ) ) { pItem = new TDEListViewItem( d->pList, pProgramItem, pList->label(iAction) ); pItem->setSelectable( false ); pItem->setExpandable( true ); pItem->setOpen( true ); if( !pProgramItem->firstChild() ) delete pProgramItem; pProgramItem = pParentItem = pItem; } else if( sName.startsWith( "Group:" ) ) { pItem = new TDEListViewItem( pProgramItem, pParentItem, pList->label(iAction) ); pItem->setSelectable( false ); pItem->setExpandable( true ); pItem->setOpen( true ); if( pGroupItem && !pGroupItem->firstChild() ) delete pGroupItem; pGroupItem = pParentItem = pItem; } else if( !sName.isEmpty() && sName != "unnamed" && pList->isConfigurable(iAction) ) { pItem = new KKeyChooserItem( pParentItem, pItem, pList, iAction ); if(pAList) pItem->setPixmap(0,pAList->action(iAction)->iconSet().pixmap(TQIconSet::Small,TQIconSet::Normal)); } } if( !pProgramItem->firstChild() ) delete pProgramItem; if( pGroupItem && !pGroupItem->firstChild() ) delete pGroupItem; } void KKeyChooser::updateButtons() { // 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()! releaseKeyboard(); KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( d->pList->currentItem() ); if ( !pItem ) { // if nothing is selected -> disable radio boxes m_prbNone->setEnabled( false ); m_prbDef->setEnabled( false ); m_prbCustom->setEnabled( false ); d->pbtnShortcut->setEnabled( false ); d->pbtnShortcut->setShortcut( TDEShortcut(), false ); } else { bool bConfigurable = pItem->isConfigurable(); bool bQtShortcut = (m_type == Application || m_type == Standard); const TDEShortcut& cutDef = pItem->shortcutDefault(); // Set key strings TQString keyStrCfg = pItem->shortcut().toString(); TQString keyStrDef = cutDef.toString(); d->pbtnShortcut->setShortcut( pItem->shortcut(), bQtShortcut ); //item->setText( 1, keyStrCfg ); pItem->repaint(); d->lInfo->setText( i18n("Default key:") + TQString(" %1").arg(keyStrDef.isEmpty() ? i18n("None") : keyStrDef) ); // Select the appropriate radio button. int index = (pItem->shortcut().isNull()) ? NoKey : (pItem->shortcut() == cutDef) ? DefaultKey : CustomKey; m_prbNone->setChecked( index == NoKey ); m_prbDef->setChecked( index == DefaultKey ); m_prbCustom->setChecked( index == CustomKey ); // Enable buttons if this key is configurable. // The 'Default Key' button must also have a default key. m_prbNone->setEnabled( bConfigurable ); m_prbDef->setEnabled( bConfigurable && cutDef.count() != 0 ); m_prbCustom->setEnabled( bConfigurable ); d->pbtnShortcut->setEnabled( bConfigurable ); } } void KKeyChooser::slotNoKey() { // return if no key is selected KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( d->pList->currentItem() ); if( pItem ) { //kdDebug(125) << "no Key" << d->pList->currentItem()->text(0) << endl; pItem->setShortcut( TDEShortcut() ); updateButtons(); emit keyChange(); } } void KKeyChooser::slotDefaultKey() { // return if no key is selected KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( d->pList->currentItem() ); if( pItem ) // don't set it directly, check for conflicts setShortcut( pItem->shortcutDefault() ); } void KKeyChooser::slotCustomKey() { d->pbtnShortcut->captureShortcut(); } void KKeyChooser::readGlobalKeys() { d->mapGlobals.clear(); if( m_type == Global ) return; // they will be checked normally, because we're configuring them readGlobalKeys( d->mapGlobals ); } void KKeyChooser::readGlobalKeys( TQMap< TQString, TDEShortcut >& map ) { TQMap<TQString, TQString> mapEntry = TDEGlobal::config()->entryMap( "Global Shortcuts" ); TQMap<TQString, TQString>::Iterator it( mapEntry.begin() ); for( uint i = 0; it != mapEntry.end(); ++it, i++ ) map[it.key()] = TDEShortcut(*it); } void KKeyChooser::slotSettingsChanged( int category ) { if( category == TDEApplication::SETTINGS_SHORTCUTS ) readGlobalKeys(); // reread } void KKeyChooser::fontChange( const TQFont & ) { d->fCArea->setMinimumHeight( 4*d->pbtnShortcut->sizeHint().height() ); int widget_width = 0; setMinimumWidth( 20+5*(widget_width+10) ); } // KDE4 IMHO this shouldn't be here at all - it cannot check whether the default // shortcut don't conflict with some already changed ones (e.g. global shortcuts). // Also, I personally find reseting all shortcuts to default (i.e. hardcoded in the app) // ones after pressing the 'Default' button rather a misfeature. void KKeyChooser::allDefault() { kdDebug(125) << "KKeyChooser::allDefault()" << endl; TQListViewItemIterator it( d->pList ); for( ; it.current(); ++it ) { KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(it.current()); if( pItem ) pItem->setShortcut( pItem->shortcutDefault() ); } updateButtons(); emit keyChange(); } void KKeyChooser::slotListItemSelected( TQListViewItem* ) { updateButtons(); } void KKeyChooser::slotListItemDoubleClicked ( TQListViewItem *, const TQPoint & , int ) { // KDE4 dump this captureCurrentItem(); } void KKeyChooser::captureCurrentItem() { KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( d->pList->currentItem() ); if( pItem != NULL && pItem->isConfigurable()) d->pbtnShortcut->captureShortcut ( ); } void KKeyChooser::setPreferFourModifierKeys( bool bPreferFourModifierKeys ) { d->bPreferFourModifierKeys = bPreferFourModifierKeys; } void KKeyChooser::capturedShortcut( const TDEShortcut& cut ) { if( cut.isNull() ) slotNoKey(); else setShortcut( cut ); } // FIXME: give this functionality again -- I don't think it's ever used, though. -- ellis // TODO: Check lxr.kde.org to see if it's used anywhere void KKeyChooser::listSync() { /* kdDebug(125) << "KKeyChooser::listSync()" << endl; if( d->pColl ) { // TODO: This is very inefficient. Come up with something better. TDEAccelActions aa; d->pColl->createKeyMap( aa ); d->actionsNew.updateShortcuts( aa ); } else if( d->pActionsOrig ) { d->actionsNew.updateShortcuts( *d->pActionsOrig ); update(); updateButtons(); }*/ } void KKeyChooser::syncToConfig( const TQString& sConfigGroup, TDEConfigBase* pConfig, bool bClearUnset ) { kdDebug(125) << "KKeyChooser::syncToConfig( \"" << sConfigGroup << "\", " << pConfig << " ) start" << endl; if( !pConfig ) pConfig = TDEGlobal::config(); TDEConfigGroupSaver cgs( pConfig, sConfigGroup ); TQListViewItemIterator it( d->pList ); for( ; it.current(); ++it ) { KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(it.current()); if( pItem ) { TQString sEntry = pConfig->readEntry( pItem->actionName() ); if( !sEntry.isNull() || bClearUnset ) { if( sEntry == "none" ) sEntry = TQString::null; pItem->setShortcut( sEntry ); } kdDebug(125) << pItem->actionName() << " = " << pItem->shortcut().toStringInternal() << endl; } } updateButtons(); kdDebug(125) << "KKeyChooser::syncToConfig() done" << endl; } void KKeyChooser::setShortcut( const TDEShortcut& cut ) { kdDebug(125) << "KKeyChooser::setShortcut( " << cut.toString() << " )" << endl; KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(d->pList->currentItem()); if( !pItem ) return; for( uint i = 0; i < cut.count(); i++ ) { const KKeySequence& seq = cut.seq(i); const KKey& key = seq.key(0); if( !d->bAllowLetterShortcuts && key.modFlags() == 0 && key.sym() < 0x3000 && TQChar(key.sym()).isLetterOrNumber() ) { TQString s = i18n( "In order to use the '%1' key as a shortcut, " "it must be combined with the " "Win, Alt, Ctrl, and/or Shift keys." ).arg(TQChar(key.sym())); KMessageBox::sorry( this, s, i18n("Invalid Shortcut Key") ); return; } } // If key isn't already in use, if( !isKeyPresent( cut ) ) { // Set new key code pItem->setShortcut( cut ); // Update display updateButtons(); emit keyChange(); } } // Returns iSeq index if cut2 has a sequence of equal or higher priority to a sequence in cut. // else -1 static int keyConflict( const TDEShortcut& cut, const TDEShortcut& cut2 ) { for( uint iSeq = 0; iSeq < cut.count(); iSeq++ ) { for( uint iSeq2 = 0; iSeq2 < cut2.count(); iSeq2++ ) { if( cut.seq(iSeq) == cut2.seq(iSeq2) ) return iSeq; } } return -1; } // Removes the sequences in cut2 from cut1 static void removeFromShortcut( TDEShortcut & cut1, const TDEShortcut &cut2) { for( uint iSeq2 = 0; iSeq2 < cut2.count(); iSeq2++ ) cut1.remove(cut2.seq(iSeq2)); } bool KKeyChooser::isKeyPresent( const TDEShortcut& cut, bool bWarnUser ) { KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(d->pList->currentItem()); if (!pItem) { return false; } bool has_global_chooser = false; bool has_standard_chooser = false; for( TQValueList< KKeyChooser* >::ConstIterator it = allChoosers->begin(); it != allChoosers->end(); ++it ) { has_global_chooser |= ((*it)->m_type == Global); has_standard_chooser |= ((*it)->m_type == Standard); } // If editing global shortcuts, check them for conflicts with the stdaccels. if( m_type == ApplicationGlobal || m_type == Global ) { if( !has_standard_chooser ) { if( checkStandardShortcutsConflict( cut, bWarnUser, this )) return true; } } // only check the global keys if one of the keychoosers isn't global if( !has_global_chooser ) { if( checkGlobalShortcutsConflict( cut, bWarnUser, this, d->mapGlobals, m_type == Global ? pItem->actionName() : TQString::null )) return true; } if( isKeyPresentLocally( cut, pItem, bWarnUser )) return true; // check also other KKeyChooser's for( TQValueList< KKeyChooser* >::ConstIterator it = allChoosers->begin(); it != allChoosers->end(); ++it ) { if( (*it) != this && (*it)->isKeyPresentLocally( cut, NULL, bWarnUser )) return true; } return false; } // KDE4 remove bool KKeyChooser::isKeyPresentLocally( const TDEShortcut& cut, KKeyChooserItem* ignoreItem, const TQString& warnText ) { return isKeyPresentLocally( cut, ignoreItem, !warnText.isNull()); } bool KKeyChooser::isKeyPresentLocally( const TDEShortcut& cut, KKeyChooserItem* ignoreItem, bool bWarnUser ) { if ( cut.toString().isEmpty()) return false; // Search for shortcut conflicts with other actions in the // lists we're configuring. for( TQListViewItemIterator it( d->pList ); it.current(); ++it ) { KKeyChooserItem* pItem2 = dynamic_cast<KKeyChooserItem*>(it.current()); if( pItem2 && pItem2 != ignoreItem ) { int iSeq = keyConflict( cut, pItem2->shortcut() ); if( iSeq > -1 ) { if( bWarnUser ) { if( !promptForReassign( cut.seq(iSeq), pItem2->text(0), Application, this )) return true; // else remove the shortcut from it TDEShortcut cut2 = pItem2->shortcut(); removeFromShortcut(cut2, cut); pItem2->setShortcut(cut2); updateButtons(); emit keyChange(); } } } } return false; } bool KKeyChooser::checkStandardShortcutsConflict( const TDEShortcut& cut, bool bWarnUser, TQWidget* parent ) { // For each key sequence in the shortcut, for( uint i = 0; i < cut.count(); i++ ) { const KKeySequence& seq = cut.seq(i); TDEStdAccel::StdAccel id = TDEStdAccel::findStdAccel( seq ); if( id != TDEStdAccel::AccelNone && keyConflict( cut, TDEStdAccel::shortcut( id ) ) > -1 ) { if( bWarnUser ) { if( !promptForReassign( seq, TDEStdAccel::label(id), Standard, parent )) return true; removeStandardShortcut( TDEStdAccel::label(id), dynamic_cast< KKeyChooser* > ( parent ), TDEStdAccel::shortcut( id ), cut); } } } return false; } bool KKeyChooser::checkGlobalShortcutsConflict( const TDEShortcut& cut, bool bWarnUser, TQWidget* parent ) { TQMap< TQString, TDEShortcut > map; readGlobalKeys( map ); return checkGlobalShortcutsConflict( cut, bWarnUser, parent, map, TQString::null ); } bool KKeyChooser::checkGlobalShortcutsConflict( const TDEShortcut& cut, bool bWarnUser, TQWidget* parent, const TQMap< TQString, TDEShortcut >& map, const TQString& ignoreAction ) { TQMap<TQString, TDEShortcut>::ConstIterator it; for( it = map.begin(); it != map.end(); ++it ) { int iSeq = keyConflict( cut, (*it) ); if( iSeq > -1 ) { if( ignoreAction.isEmpty() || it.key() != ignoreAction ) { if( bWarnUser ) { if( !promptForReassign( cut.seq(iSeq), it.key(), Global, parent )) return true; removeGlobalShortcut( it.key(), dynamic_cast< KKeyChooser* >( parent ), (*it), cut); } } } } return false; } void KKeyChooser::removeStandardShortcut( const TQString& name, KKeyChooser* chooser, const TDEShortcut &origCut, const TDEShortcut &cut ) { bool was_in_choosers = false; if( allChoosers != NULL ) { for( TQValueList< KKeyChooser* >::ConstIterator it = allChoosers->begin(); it != allChoosers->end(); ++it ) { if( (*it) != chooser && (*it)->m_type == Standard ) { was_in_choosers |= ( (*it)->removeShortcut( name, cut )); } } } if( !was_in_choosers ) { // not edited, needs to be changed in config file TDEStdAccel::ShortcutList std_list; TDEShortcut newCut = origCut; removeFromShortcut(newCut, cut); int index = std_list.index( name ); if ( index >= 0 ) { std_list.setShortcut( index, newCut ); std_list.save(); } } } void KKeyChooser::removeGlobalShortcut( const TQString& name, KKeyChooser* chooser, const TDEShortcut &origCut, const TDEShortcut &cut ) { bool was_in_choosers = false; if( allChoosers != NULL ) { for( TQValueList< KKeyChooser* >::ConstIterator it = allChoosers->begin(); it != allChoosers->end(); ++it ) { if( (*it) != chooser && (*it)->m_type == Global ) { was_in_choosers |= ( (*it)->removeShortcut( name, cut )); } } } if( !was_in_choosers ) { // not edited, needs to be changed in config file TDEAccelActions actions; TDEShortcut newCut = origCut; removeFromShortcut(newCut, cut); actions.insert( name, "", "", newCut, newCut); actions.writeActions( "Global Shortcuts", 0, true, true ); } } bool KKeyChooser::removeShortcut( const TQString& name, const TDEShortcut &cut ) { for( TQListViewItemIterator it( d->pList ); it.current(); ++it ) { KKeyChooserItem* pItem2 = dynamic_cast<KKeyChooserItem*>(it.current()); if( pItem2 && pItem2->actionName() == name ) { // remove the shortcut from it TDEShortcut cut2 = pItem2->shortcut(); removeFromShortcut(cut2, cut); pItem2->setShortcut(cut2); updateButtons(); emit keyChange(); return true; } } return false; } // KDE4 remove this void KKeyChooser::_warning( const KKeySequence& cut, TQString sAction, TQString sTitle ) { sAction = sAction.stripWhiteSpace(); TQString s = i18n("The '%1' key combination has already been allocated " "to the \"%2\" action.\n" "Please choose a unique key combination."). arg(cut.toString()).arg(sAction); KMessageBox::sorry( this, s, sTitle ); } bool KKeyChooser::promptForReassign( const KKeySequence& cut, const TQString& sAction, ActionType type, TQWidget* parent ) { if(cut.isNull()) return true; TQString sTitle; TQString s; if( type == Standard ) { sTitle = i18n("Conflict with Standard Application Shortcut"); s = i18n("The '%1' key combination has already been allocated " "to the standard action \"%2\".\n" "Do you want to reassign it from that action to the current one?"); } else if( type == Global ) { sTitle = i18n("Conflict with Global Shortcut"); s = i18n("The '%1' key combination has already been allocated " "to the global action \"%2\".\n" "Do you want to reassign it from that action to the current one?"); } else { sTitle = i18n("Key Conflict"); s = i18n("The '%1' key combination has already been allocated " "to the \"%2\" action.\n" "Do you want to reassign it from that action to the current one?"); } s = s.arg(cut.toString()).arg(sAction.stripWhiteSpace()); return KMessageBox::warningContinueCancel( parent, s, sTitle, i18n("Reassign") ) == KMessageBox::Continue; } //--------------------------------------------------- KKeyChooserItem::KKeyChooserItem( TDEListView* parent, TQListViewItem* after, TDEShortcutList* pList, uint iAction ) : TDEListViewItem( parent, after ) { m_pList = pList; m_iAction = iAction; m_bModified = false; m_cut = m_pList->shortcut(m_iAction); } KKeyChooserItem::KKeyChooserItem( TQListViewItem* parent, TQListViewItem* after, TDEShortcutList* pList, uint iAction ) : TDEListViewItem( parent, after ) { m_pList = pList; m_iAction = iAction; m_bModified = false; m_cut = m_pList->shortcut(m_iAction); } TQString KKeyChooserItem::actionName() const { return m_pList->name(m_iAction); } const TDEShortcut& KKeyChooserItem::shortcut() const { return m_cut; } void KKeyChooserItem::setShortcut( const TDEShortcut& cut ) { m_cut = cut; m_bModified = (m_cut != m_pList->shortcut(m_iAction)); listView()->repaintItem( this ); } void KKeyChooserItem::commitChanges() { if( m_bModified ) m_pList->setShortcut( m_iAction, m_cut ); } TQString KKeyChooserItem::text( int iCol ) const { if( iCol == 0 ) { // Quick HACK to get rid of '&'s. TQString s = m_pList->label(m_iAction); TQString s2; for( uint i = 0; i < s.length(); i++ ) if( s[i] != '&' || ( i+1<s.length() && s[i+1] == '&' ) ) s2 += s[i]; return s2; } else if( iCol <= (int) m_cut.count() ) return m_cut.seq(iCol-1).toString(); else return TQString::null; } int KKeyChooserItem::compare( TQListViewItem* item, int iCol, bool bAscending ) const { KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( item ); if( iCol == 0 && pItem ) { TQString psName1 = m_pList->name(m_iAction); TQString psName2 = pItem->m_pList->name(pItem->m_iAction); TQRegExp rxNumber1( " (\\d+)$" ); TQRegExp rxNumber2( " (\\d+)$" ); int iNumber1 = rxNumber1.search( psName1 ); int iNumber2 = rxNumber2.search( psName2 ); // Check if the last word is one or more digits if( iNumber1 >= 0 && iNumber1 == iNumber2 && psName1.startsWith( psName2.left( iNumber1+1 ) ) ) { int n1 = rxNumber1.cap(1).toInt(); int n2 = rxNumber2.cap(1).toInt(); return (n1 < n2) ? -1 : (n1 > n2) ? 1 : 0; } } return TQListViewItem::compare( item, iCol, bAscending ); } //// TQString KKeyChooserWhatsThis::text( const TQPoint& p ) { if ( !m_listView ) return TQString::null; const TQListViewItem* item = m_listView->itemAt( p ); const KKeyChooserItem* pItem = dynamic_cast<const KKeyChooserItem*>(item); if ( !pItem ) return TQWhatsThis::textFor( m_listView ); const TQString itemWhatsThis = pItem->whatsThis(); if ( itemWhatsThis.isEmpty() ) return TQWhatsThis::textFor( m_listView ); return itemWhatsThis; } /************************************************************************/ /* KKeyDialog */ /* */ /* Originally by Nicolas Hadacek <hadacek@via.ecp.fr> */ /* */ /* Substantially revised by Mark Donohoe <donohoe@kde.org> */ /* */ /* And by Espen Sand <espen@kde.org> 1999-10-19 */ /* (by using KDialogBase there is almost no code left ;) */ /* */ /************************************************************************/ KKeyDialog::KKeyDialog( KKeyChooser::ActionType type, bool bAllowLetterShortcuts, TQWidget *parent, const char* name ) : KDialogBase( parent, name ? name : "kkeydialog", true, i18n("Configure Shortcuts"), Default|Ok|Cancel, Ok ) { m_pKeyChooser = new KKeyChooser( this, type, bAllowLetterShortcuts ); setMainWidget( m_pKeyChooser ); connect( this, TQT_SIGNAL(defaultClicked()), m_pKeyChooser, TQT_SLOT(allDefault()) ); TDEConfigGroup group( TDEGlobal::config(), "KKeyDialog Settings" ); TQSize sz = size(); resize( group.readSizeEntry( "Dialog Size", &sz ) ); } KKeyDialog::KKeyDialog( bool bAllowLetterShortcuts, TQWidget *parent, const char* name ) : KDialogBase( parent, name ? name : "kkeydialog", true, i18n("Configure Shortcuts"), Default|Ok|Cancel, Ok ) { m_pKeyChooser = new KKeyChooser( this, KKeyChooser::Application, bAllowLetterShortcuts ); setMainWidget( m_pKeyChooser ); connect( this, TQT_SIGNAL(defaultClicked()), m_pKeyChooser, TQT_SLOT(allDefault()) ); TDEConfigGroup group( TDEGlobal::config(), "KKeyDialog Settings" ); TQSize sz = size(); resize( group.readSizeEntry( "Dialog Size", &sz ) ); } KKeyDialog::~KKeyDialog() { TDEConfigGroup group( TDEGlobal::config(), "KKeyDialog Settings" ); group.writeEntry( "Dialog Size", size(), true, true ); } bool KKeyDialog::insert( TDEActionCollection* pColl ) { return m_pKeyChooser->insert( pColl ); } bool KKeyDialog::insert(TDEActionCollection *pColl, const TQString &title) { return m_pKeyChooser->insert(pColl, title); } bool KKeyDialog::configure( bool bSaveSettings ) { int retcode = exec(); if( retcode == Accepted ) { if( bSaveSettings ) m_pKeyChooser->save(); else commitChanges(); } return retcode; } void KKeyDialog::commitChanges() { m_pKeyChooser->commitChanges(); } int KKeyDialog::configure( TDEActionCollection* coll, TQWidget* parent, bool bSaveSettings ) { return configure( coll, true, parent, bSaveSettings); } int KKeyDialog::configure( TDEAccel* keys, TQWidget* parent, bool bSaveSettings ) { return configure( keys, true, parent, bSaveSettings); } int KKeyDialog::configure( TDEGlobalAccel* keys, TQWidget* parent, bool bSaveSettings ) { return configure( keys, true, parent, bSaveSettings); } int KKeyDialog::configure( TDEAccel* keys, bool bAllowLetterShortcuts, TQWidget *parent, bool bSaveSettings ) { KKeyDialog dlg( bAllowLetterShortcuts, parent ); dlg.m_pKeyChooser->insert( keys ); bool b = dlg.configure( bSaveSettings ); if( b && bSaveSettings ) keys->updateConnections(); return b; } int KKeyDialog::configure( TDEGlobalAccel* keys, bool bAllowLetterShortcuts, TQWidget *parent, bool bSaveSettings ) { KKeyDialog dlg( KKeyChooser::ApplicationGlobal, bAllowLetterShortcuts, parent ); dlg.m_pKeyChooser->insert( keys ); bool b = dlg.configure( bSaveSettings ); if( b && bSaveSettings ) keys->updateConnections(); return b; } int KKeyDialog::configure( TDEActionCollection* coll, bool bAllowLetterShortcuts, TQWidget *parent, bool bSaveSettings ) { kdDebug(125) << "KKeyDialog::configureKeys( TDEActionCollection*, " << bSaveSettings << " )" << endl; KKeyDialog dlg( bAllowLetterShortcuts, parent ); dlg.m_pKeyChooser->insert( coll ); return dlg.configure( bSaveSettings ); } /*int KKeyDialog::configure( TDEActionPtrList* coll, const TQString& file, TQWidget *parent, bool bSaveSettings ) { kdDebug(125) << "KKeyDialog::configureKeys( TDEActionCollection*, " << file << ", " << bSaveSettings << " )" << endl; TDEAccelActions actions; coll->createKeyMap( actions ); int retcode = configure( actions, file, parent, bSaveSettings ); if( retcode == Accepted ) coll->setKeyMap( actions ); return retcode; }*/ void KKeyChooser::virtual_hook( int, void* ) { /*BASE::virtual_hook( id, data );*/ } void KKeyDialog::virtual_hook( int id, void* data ) { KDialogBase::virtual_hook( id, data ); } #include "kkeydialog.moc"