/* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe Copyright (C) 1997 Nicolas Hadacek Copyright (C) 1998 Matthias Ettrich Copyright (C) 2001 Ellis Whitehead 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 #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 #include #include #ifdef Q_WS_X11 #define XK_XKB_KEYS #define XK_MISCELLANY #include // For x11Event() #include // For XK_... #include #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 rgpLists; TQValueList rgpListsAllocated; TDEListView *pList; TQLabel *lInfo; KKeyButton *pbtnShortcut; TQGroupBox *fCArea; TQButtonGroup *kbGroup; TQMap 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(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 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 ( 100, false ); //d->globalDict->setAutoDelete( true ); readGlobalKeys(); //d->stdDict = new TQDict ( 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(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( 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( 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( 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 mapEntry = TDEGlobal::config()->entryMap( "Global Shortcuts" ); TQMap::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(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( 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(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(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(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(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::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(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( 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(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 */ /* */ /* Substantially revised by Mark Donohoe */ /* */ /* And by Espen Sand 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"