summaryrefslogtreecommitdiffstats
path: root/kdeui/kkeydialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdeui/kkeydialog.cpp')
-rw-r--r--kdeui/kkeydialog.cpp1209
1 files changed, 1209 insertions, 0 deletions
diff --git a/kdeui/kkeydialog.cpp b/kdeui/kkeydialog.cpp
new file mode 100644
index 000000000..5f831767a
--- /dev/null
+++ b/kdeui/kkeydialog.cpp
@@ -0,0 +1,1209 @@
+/* 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 <qbuttongroup.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qdrawutil.h>
+#include <qpainter.h>
+#include <qradiobutton.h>
+#include <qregexp.h>
+#include <qtoolbutton.h>
+#include <qwhatsthis.h>
+
+#include <kaccel.h>
+#include <kaction.h>
+#include <kaccelaction.h>
+#include <kactionshortcutlist.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kglobalaccel.h>
+#include <kiconloader.h>
+#include <klistviewsearchline.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kshortcut.h>
+#include <kshortcutlist.h>
+#include <kxmlguifactory.h>
+#include <kaboutdata.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 <qwhatsthis.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 KListViewItem
+{
+ public:
+ KKeyChooserItem( KListView* parent, QListViewItem* after, KShortcutList* pList, uint iAction );
+ KKeyChooserItem( QListViewItem* parent, QListViewItem* after, KShortcutList* pList, uint iAction );
+
+ QString actionName() const;
+ const KShortcut& shortcut() const;
+ bool isConfigurable() const
+ { return m_pList->isConfigurable( m_iAction ); }
+ const KShortcut& shortcutDefault() const
+ { return m_pList->shortcutDefault( m_iAction ); }
+ QString whatsThis() const
+ { return m_pList->whatsThis( m_iAction ); }
+
+ void setShortcut( const KShortcut& cut );
+ void commitChanges();
+
+ virtual QString text( int iCol ) const;
+ virtual int compare( QListViewItem*, int iCol, bool bAscending ) const;
+
+ protected:
+ KShortcutList* m_pList;
+ uint m_iAction;
+ bool m_bModified;
+ KShortcut m_cut;
+};
+
+// WhatsThis on KKeyChooserItems
+class KKeyChooserWhatsThis : public QWhatsThis
+{
+public:
+ KKeyChooserWhatsThis( QListView* listview )
+ : QWhatsThis( listview->viewport() ), m_listView( listview ) {}
+
+protected:
+ virtual QString text( const QPoint& p );
+
+private:
+ QListView* m_listView;
+};
+
+//---------------------------------------------------------------------
+// KKeyChooserPrivate
+//---------------------------------------------------------------------
+
+class KKeyChooserPrivate
+{
+ public:
+ QValueList<KShortcutList*> rgpLists;
+ QValueList<KShortcutList*> rgpListsAllocated;
+
+ KListView *pList;
+ QLabel *lInfo;
+ KKeyButton *pbtnShortcut;
+ QGroupBox *fCArea;
+ QButtonGroup *kbGroup;
+
+ QMap<QString, KShortcut> 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( QWidget* parent, ActionType type, bool bAllowLetterShortcuts )
+: QWidget( parent )
+{
+ initGUI( type, bAllowLetterShortcuts );
+}
+
+KKeyChooser::KKeyChooser( KActionCollection* coll, QWidget* parent, bool bAllowLetterShortcuts )
+: QWidget( parent )
+{
+ initGUI( Application, bAllowLetterShortcuts );
+ insert( coll );
+}
+
+KKeyChooser::KKeyChooser( KAccel* pAccel, QWidget* parent, bool bAllowLetterShortcuts )
+: QWidget( parent )
+{
+ initGUI( Application, bAllowLetterShortcuts );
+ insert( pAccel );
+}
+
+KKeyChooser::KKeyChooser( KGlobalAccel* pAccel, QWidget* parent )
+: QWidget( parent )
+{
+ initGUI( ApplicationGlobal, false );
+ insert( pAccel );
+}
+
+KKeyChooser::KKeyChooser( KShortcutList* pList, QWidget* parent, ActionType type, bool bAllowLetterShortcuts )
+: QWidget( parent )
+{
+ initGUI( type, bAllowLetterShortcuts );
+ insert( pList );
+}
+
+KKeyChooser::KKeyChooser( KAccel* actions, QWidget* parent,
+ bool bCheckAgainstStdKeys,
+ bool bAllowLetterShortcuts,
+ bool bAllowWinKey )
+: QWidget( parent )
+{
+ ActionType type;
+ if( bAllowWinKey )
+ type = (bCheckAgainstStdKeys) ? ApplicationGlobal : Global;
+ else
+ type = Application;
+
+ initGUI( type, bAllowLetterShortcuts );
+ insert( actions );
+}
+
+KKeyChooser::KKeyChooser( KGlobalAccel* actions, QWidget* parent,
+ bool bCheckAgainstStdKeys,
+ bool bAllowLetterShortcuts,
+ bool /*bAllowWinKey*/ )
+: QWidget( 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 QValueList< KKeyChooser* >* allChoosers = NULL;
+static KStaticDeleter< QValueList< KKeyChooser* > > allChoosersDeleter;
+
+KKeyChooser::~KKeyChooser()
+{
+ allChoosers->remove( this );
+ // Delete allocated KShortcutLists
+ for( uint i = 0; i < d->rgpListsAllocated.count(); i++ )
+ delete d->rgpListsAllocated[i];
+ delete d;
+}
+
+bool KKeyChooser::insert( KActionCollection *pColl)
+{
+ return insert(pColl, QString::null);
+}
+
+bool KKeyChooser::insert( KActionCollection* pColl, const QString &title )
+{
+ QString str = title;
+ if ( title.isEmpty() && pColl->instance()
+ && pColl->instance()->aboutData() )
+ str = pColl->instance()->aboutData()->programName();
+
+ KShortcutList* pList = new KActionShortcutList( pColl );
+ d->rgpListsAllocated.append( pList );
+ d->rgpLists.append( pList );
+ buildListView(d->rgpLists.count() - 1, str);
+ return true;
+}
+
+bool KKeyChooser::insert( KAccel* pAccel )
+{
+ KShortcutList* pList = new KAccelShortcutList( pAccel );
+ d->rgpListsAllocated.append( pList );
+ return insert( pList );
+}
+
+bool KKeyChooser::insert( KGlobalAccel* pAccel )
+{
+ KShortcutList* pList = new KAccelShortcutList( pAccel );
+ d->rgpListsAllocated.append( pList );
+ return insert( pList );
+}
+
+bool KKeyChooser::insert( KShortcutList* pList )
+{
+ d->rgpLists.append( pList );
+ buildListView( d->rgpLists.count() - 1, QString::null );
+ return true;
+}
+
+void KKeyChooser::commitChanges()
+{
+ kdDebug(125) << "KKeyChooser::commitChanges()" << endl;
+
+ QListViewItemIterator 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 = KGlobalAccel::useFourModifierKeys();
+
+ //
+ // TOP LAYOUT MANAGER
+ //
+ // The following layout is used for the dialog
+ // LIST LABELS LAYOUT
+ // SPLIT LIST BOX WIDGET
+ // CHOOSE KEY GROUP BOX WIDGET
+ // BUTTONS LAYOUT
+ // Items are added to topLayout as they are created.
+ //
+
+ QBoxLayout *topLayout = new QVBoxLayout( this, 0, KDialog::spacingHint() );
+
+ //
+ // ADD SEARCHLINE
+ //
+ QHBoxLayout* searchLayout = new QHBoxLayout(0, 0, KDialog::spacingHint());
+ topLayout->addLayout(searchLayout, 10);
+
+ QToolButton *clearSearch = new QToolButton(this);
+ clearSearch->setTextLabel(i18n("Clear Search"), true);
+ clearSearch->setIconSet(SmallIconSet(QApplication::reverseLayout() ? "clear_left" : "locationbar_erase"));
+ searchLayout->addWidget(clearSearch);
+ QLabel* slbl = new QLabel(i18n("&Search:"), this);
+ searchLayout->addWidget(slbl);
+ KListViewSearchLine* listViewSearch = new KListViewSearchLine(this);
+ searchLayout->addWidget(listViewSearch);
+ slbl->setBuddy(listViewSearch);
+ connect(clearSearch, SIGNAL(pressed()), listViewSearch, SLOT(clear()));
+
+ QString wtstr = i18n("Search interactively for shortcut names (e.g. Copy) "
+ "or combination of keys (e.g. Ctrl+C) by typing them here.");
+
+ QWhatsThis::add(slbl, wtstr);
+ QWhatsThis::add(listViewSearch, wtstr);
+
+ //
+ // CREATE SPLIT LIST BOX
+ //
+ // fill up the split list box with the action/key pairs.
+ //
+ QGridLayout *stackLayout = new QGridLayout(2, 2, 2);
+ topLayout->addLayout( stackLayout, 10 );
+ stackLayout->setRowStretch( 1, 10 ); // Only list will stretch
+
+ d->pList = new KListView( this );
+ listViewSearch->setListView(d->pList); // Plug into search line
+ QValueList<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.");
+
+ QWhatsThis::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, SIGNAL(currentChanged(QListViewItem*)),
+ SLOT(slotListItemSelected(QListViewItem*)) );
+
+ // handle double clicking an item
+ connect ( d->pList, SIGNAL ( doubleClicked ( QListViewItem *, const QPoint &, int ) ),
+ SLOT ( captureCurrentItem()) );
+ connect ( d->pList, SIGNAL ( spacePressed( QListViewItem* )), SLOT( captureCurrentItem()));
+ //
+ // CREATE CHOOSE KEY GROUP
+ //
+ d->fCArea = new QGroupBox( this );
+ topLayout->addWidget( d->fCArea, 1 );
+
+ d->fCArea->setTitle( i18n("Shortcut for Selected Action") );
+ d->fCArea->setFrameStyle( QFrame::GroupBoxPanel | QFrame::Plain );
+
+ //
+ // CHOOSE KEY GROUP LAYOUT MANAGER
+ //
+ QGridLayout *grid = new QGridLayout( d->fCArea, 3, 4, KDialog::spacingHint() );
+ grid->addRowSpacing( 0, fontMetrics().lineSpacing() );
+
+ d->kbGroup = new QButtonGroup( d->fCArea );
+ d->kbGroup->hide();
+ d->kbGroup->setExclusive( true );
+
+ m_prbNone = new QRadioButton( 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 );
+ QWhatsThis::add( m_prbNone, i18n("The selected action will not be associated with any key.") );
+ connect( m_prbNone, SIGNAL(clicked()), SLOT(slotNoKey()) );
+
+ m_prbDef = new QRadioButton( 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 );
+ QWhatsThis::add( m_prbDef, i18n("This will bind the default key to the selected action. Usually a reasonable choice.") );
+ connect( m_prbDef, SIGNAL(clicked()), SLOT(slotDefaultKey()) );
+
+ m_prbCustom = new QRadioButton( 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 );
+ QWhatsThis::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, SIGNAL(clicked()), SLOT(slotCustomKey()) );
+
+ //connect( d->kbGroup, SIGNAL( clicked( int ) ), SLOT( keyMode( int ) ) );
+
+ QBoxLayout *pushLayout = new QHBoxLayout( KDialog::spacingHint() );
+ grid->addLayout( pushLayout, 1, 3 );
+
+ d->pbtnShortcut = new KKeyButton(d->fCArea, "key");
+ d->pbtnShortcut->setEnabled( false );
+ connect( d->pbtnShortcut, SIGNAL(capturedShortcut(const KShortcut&)), SLOT(capturedShortcut(const KShortcut&)) );
+ 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.");
+ QWhatsThis::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 QLabel(d->fCArea);
+ //resize(0,0);
+ //d->lInfo->setAlignment( AlignCenter );
+ //d->lInfo->setEnabled( false );
+ //d->lInfo->hide();
+ grid->addMultiCellWidget( d->lInfo, 2, 2, 0, 3 );
+
+ //d->globalDict = new QDict<int> ( 100, false );
+ //d->globalDict->setAutoDelete( true );
+ readGlobalKeys();
+ //d->stdDict = new QDict<int> ( 100, false );
+ //d->stdDict->setAutoDelete( true );
+ //if (type == Application || type == ApplicationGlobal)
+ // readStdKeys();
+ connect( kapp, SIGNAL( settingsChanged( int )), SLOT( slotSettingsChanged( int )));
+ if( allChoosers == NULL )
+ allChoosers = allChoosersDeleter.setObject( allChoosers, new QValueList< KKeyChooser* > );
+ allChoosers->append( this );
+}
+
+// Add all shortcuts to the list
+void KKeyChooser::buildListView( uint iList, const QString &title )
+{
+ KShortcutList* pList = d->rgpLists[iList];
+ KActionShortcutList *pAList = dynamic_cast<KActionShortcutList*>(pList);
+
+ if( m_type == Global || m_type == ApplicationGlobal )
+ d->pList->setSorting( -1 );
+ KListViewItem *pProgramItem, *pGroupItem = 0, *pParentItem, *pItem;
+
+ QString str = (title.isEmpty() ? i18n("Shortcuts") : title);
+ pParentItem = pProgramItem = pItem = new KListViewItem( d->pList, str );
+ pParentItem->setExpandable( true );
+ pParentItem->setOpen( true );
+ pParentItem->setSelectable( false );
+ uint nSize = pList->count();
+ for( uint iAction = 0; iAction < nSize; iAction++ ) {
+ QString sName = pList->name(iAction);
+ kdDebug(125) << "Key: " << sName << endl;
+ if( sName.startsWith( "Program:" ) ) {
+ pItem = new KListViewItem( 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 KListViewItem( 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(QIconSet::Small,QIconSet::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( KShortcut(), false );
+ } else {
+ bool bConfigurable = pItem->isConfigurable();
+ bool bQtShortcut = (m_type == Application || m_type == Standard);
+ const KShortcut& cutDef = pItem->shortcutDefault();
+
+ // Set key strings
+ QString keyStrCfg = pItem->shortcut().toString();
+ QString keyStrDef = cutDef.toString();
+
+ d->pbtnShortcut->setShortcut( pItem->shortcut(), bQtShortcut );
+ //item->setText( 1, keyStrCfg );
+ pItem->repaint();
+ d->lInfo->setText( i18n("Default key:") + QString(" %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( KShortcut() );
+ 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( QMap< QString, KShortcut >& map )
+{
+ QMap<QString, QString> mapEntry = KGlobal::config()->entryMap( "Global Shortcuts" );
+ QMap<QString, QString>::Iterator it( mapEntry.begin() );
+ for( uint i = 0; it != mapEntry.end(); ++it, i++ )
+ map[it.key()] = KShortcut(*it);
+}
+
+void KKeyChooser::slotSettingsChanged( int category )
+{
+ if( category == KApplication::SETTINGS_SHORTCUTS )
+ readGlobalKeys(); // reread
+}
+
+void KKeyChooser::fontChange( const QFont & )
+{
+ 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;
+
+ QListViewItemIterator 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( QListViewItem* )
+{
+ updateButtons();
+}
+
+void KKeyChooser::slotListItemDoubleClicked ( QListViewItem *, const QPoint & , 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 KShortcut& 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.
+ KAccelActions aa;
+ d->pColl->createKeyMap( aa );
+ d->actionsNew.updateShortcuts( aa );
+ } else if( d->pActionsOrig ) {
+ d->actionsNew.updateShortcuts( *d->pActionsOrig );
+ update();
+ updateButtons();
+ }*/
+}
+
+void KKeyChooser::syncToConfig( const QString& sConfigGroup, KConfigBase* pConfig, bool bClearUnset )
+{
+ kdDebug(125) << "KKeyChooser::syncToConfig( \"" << sConfigGroup << "\", " << pConfig << " ) start" << endl;
+ if( !pConfig )
+ pConfig = KGlobal::config();
+ KConfigGroupSaver cgs( pConfig, sConfigGroup );
+
+ QListViewItemIterator it( d->pList );
+ for( ; it.current(); ++it ) {
+ KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>(it.current());
+ if( pItem ) {
+ QString sEntry = pConfig->readEntry( pItem->actionName() );
+ if( !sEntry.isNull() || bClearUnset ) {
+ if( sEntry == "none" )
+ sEntry = QString::null;
+ pItem->setShortcut( sEntry );
+ }
+ kdDebug(125) << pItem->actionName() << " = " << pItem->shortcut().toStringInternal() << endl;
+ }
+ }
+ updateButtons();
+ kdDebug(125) << "KKeyChooser::syncToConfig() done" << endl;
+}
+
+void KKeyChooser::setShortcut( const KShortcut& 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 && QChar(key.sym()).isLetterOrNumber() ) {
+ QString 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(QChar(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 KShortcut& cut, const KShortcut& 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( KShortcut & cut1, const KShortcut &cut2)
+{
+ for( uint iSeq2 = 0; iSeq2 < cut2.count(); iSeq2++ )
+ cut1.remove(cut2.seq(iSeq2));
+}
+
+bool KKeyChooser::isKeyPresent( const KShortcut& 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( QValueList< 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() : QString::null ))
+ return true;
+ }
+
+ if( isKeyPresentLocally( cut, pItem, bWarnUser ))
+ return true;
+
+ // check also other KKeyChooser's
+ for( QValueList< 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 KShortcut& cut, KKeyChooserItem* ignoreItem, const QString& warnText )
+{
+ return isKeyPresentLocally( cut, ignoreItem, !warnText.isNull());
+}
+
+bool KKeyChooser::isKeyPresentLocally( const KShortcut& 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( QListViewItemIterator 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
+ KShortcut cut2 = pItem2->shortcut();
+ removeFromShortcut(cut2, cut);
+ pItem2->setShortcut(cut2);
+ updateButtons();
+ emit keyChange();
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool KKeyChooser::checkStandardShortcutsConflict( const KShortcut& cut, bool bWarnUser, QWidget* parent )
+{
+ // For each key sequence in the shortcut,
+ for( uint i = 0; i < cut.count(); i++ ) {
+ const KKeySequence& seq = cut.seq(i);
+ KStdAccel::StdAccel id = KStdAccel::findStdAccel( seq );
+ if( id != KStdAccel::AccelNone
+ && keyConflict( cut, KStdAccel::shortcut( id ) ) > -1 ) {
+ if( bWarnUser ) {
+ if( !promptForReassign( seq, KStdAccel::label(id), Standard, parent ))
+ return true;
+ removeStandardShortcut( KStdAccel::label(id), dynamic_cast< KKeyChooser* > ( parent ), KStdAccel::shortcut( id ), cut);
+ }
+ }
+ }
+ return false;
+}
+
+bool KKeyChooser::checkGlobalShortcutsConflict( const KShortcut& cut, bool bWarnUser, QWidget* parent )
+{
+ QMap< QString, KShortcut > map;
+ readGlobalKeys( map );
+ return checkGlobalShortcutsConflict( cut, bWarnUser, parent, map, QString::null );
+}
+
+bool KKeyChooser::checkGlobalShortcutsConflict( const KShortcut& cut, bool bWarnUser, QWidget* parent,
+ const QMap< QString, KShortcut >& map, const QString& ignoreAction )
+{
+ QMap<QString, KShortcut>::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 QString& name, KKeyChooser* chooser, const KShortcut &origCut, const KShortcut &cut )
+{
+ bool was_in_choosers = false;
+ if( allChoosers != NULL ) {
+ for( QValueList< 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
+ KStdAccel::ShortcutList std_list;
+ KShortcut 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 QString& name, KKeyChooser* chooser, const KShortcut &origCut, const KShortcut &cut )
+{
+ bool was_in_choosers = false;
+ if( allChoosers != NULL ) {
+ for( QValueList< 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
+ KAccelActions actions;
+ KShortcut newCut = origCut;
+ removeFromShortcut(newCut, cut);
+ actions.insert( name, "", "", newCut, newCut);
+ actions.writeActions( "Global Shortcuts", 0, true, true );
+ }
+}
+
+bool KKeyChooser::removeShortcut( const QString& name, const KShortcut &cut )
+{
+ for( QListViewItemIterator it( d->pList ); it.current(); ++it ) {
+ KKeyChooserItem* pItem2 = dynamic_cast<KKeyChooserItem*>(it.current());
+ if( pItem2 && pItem2->actionName() == name ) {
+ // remove the shortcut from it
+ KShortcut 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, QString sAction, QString sTitle )
+{
+ sAction = sAction.stripWhiteSpace();
+
+ QString 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 QString& sAction, ActionType type, QWidget* parent )
+{
+ if(cut.isNull())
+ return true;
+ QString sTitle;
+ QString 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( KListView* parent, QListViewItem* after, KShortcutList* pList, uint iAction )
+: KListViewItem( parent, after )
+{
+ m_pList = pList;
+ m_iAction = iAction;
+ m_bModified = false;
+ m_cut = m_pList->shortcut(m_iAction);
+}
+
+KKeyChooserItem::KKeyChooserItem( QListViewItem* parent, QListViewItem* after, KShortcutList* pList, uint iAction )
+: KListViewItem( parent, after )
+{
+ m_pList = pList;
+ m_iAction = iAction;
+ m_bModified = false;
+ m_cut = m_pList->shortcut(m_iAction);
+}
+
+QString KKeyChooserItem::actionName() const
+{
+ return m_pList->name(m_iAction);
+}
+
+const KShortcut& KKeyChooserItem::shortcut() const
+{
+ return m_cut;
+}
+
+void KKeyChooserItem::setShortcut( const KShortcut& 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 );
+}
+
+QString KKeyChooserItem::text( int iCol ) const
+{
+ if( iCol == 0 ) {
+ // Quick HACK to get rid of '&'s.
+ QString s = m_pList->label(m_iAction);
+ QString 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 QString::null;
+}
+
+int KKeyChooserItem::compare( QListViewItem* item, int iCol, bool bAscending ) const
+{
+ KKeyChooserItem* pItem = dynamic_cast<KKeyChooserItem*>( item );
+ if( iCol == 0 && pItem ) {
+ QString psName1 = m_pList->name(m_iAction);
+ QString psName2 = pItem->m_pList->name(pItem->m_iAction);
+ QRegExp rxNumber1( " (\\d+)$" );
+ QRegExp 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 QListViewItem::compare( item, iCol, bAscending );
+}
+
+////
+
+QString KKeyChooserWhatsThis::text( const QPoint& p ) {
+ if ( !m_listView )
+ return QString::null;
+
+ const QListViewItem* item = m_listView->itemAt( p );
+ const KKeyChooserItem* pItem = dynamic_cast<const KKeyChooserItem*>(item);
+ if ( !pItem )
+ return QWhatsThis::textFor( m_listView );
+
+ const QString itemWhatsThis = pItem->whatsThis();
+ if ( itemWhatsThis.isEmpty() )
+ return QWhatsThis::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, QWidget *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, SIGNAL(defaultClicked()), m_pKeyChooser, SLOT(allDefault()) );
+
+ KConfigGroup group( KGlobal::config(), "KKeyDialog Settings" );
+ QSize sz = size();
+ resize( group.readSizeEntry( "Dialog Size", &sz ) );
+}
+
+KKeyDialog::KKeyDialog( bool bAllowLetterShortcuts, QWidget *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, SIGNAL(defaultClicked()), m_pKeyChooser, SLOT(allDefault()) );
+
+ KConfigGroup group( KGlobal::config(), "KKeyDialog Settings" );
+ QSize sz = size();
+ resize( group.readSizeEntry( "Dialog Size", &sz ) );
+}
+
+KKeyDialog::~KKeyDialog()
+{
+ KConfigGroup group( KGlobal::config(), "KKeyDialog Settings" );
+ group.writeEntry( "Dialog Size", size(), true, true );
+}
+
+bool KKeyDialog::insert( KActionCollection* pColl )
+{
+ return m_pKeyChooser->insert( pColl );
+}
+
+bool KKeyDialog::insert(KActionCollection *pColl, const QString &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( KActionCollection* coll, QWidget* parent, bool bSaveSettings )
+{
+ return configure( coll, true, parent, bSaveSettings);
+}
+
+int KKeyDialog::configure( KAccel* keys, QWidget* parent, bool bSaveSettings )
+{
+ return configure( keys, true, parent, bSaveSettings);
+}
+
+int KKeyDialog::configure( KGlobalAccel* keys, QWidget* parent, bool bSaveSettings )
+{
+ return configure( keys, true, parent, bSaveSettings);
+}
+
+int KKeyDialog::configure( KAccel* keys, bool bAllowLetterShortcuts, QWidget *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( KGlobalAccel* keys, bool bAllowLetterShortcuts, QWidget *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( KActionCollection* coll, bool bAllowLetterShortcuts, QWidget *parent, bool bSaveSettings )
+{
+ kdDebug(125) << "KKeyDialog::configureKeys( KActionCollection*, " << bSaveSettings << " )" << endl;
+ KKeyDialog dlg( bAllowLetterShortcuts, parent );
+ dlg.m_pKeyChooser->insert( coll );
+ return dlg.configure( bSaveSettings );
+}
+
+/*int KKeyDialog::configure( KActionPtrList* coll, const QString& file, QWidget *parent, bool bSaveSettings )
+{
+ kdDebug(125) << "KKeyDialog::configureKeys( KActionCollection*, " << file << ", " << bSaveSettings << " )" << endl;
+ KAccelActions 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"