00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "kaccelbase.h"
00024
00025 #include <qkeycode.h>
00026 #include <qlabel.h>
00027 #include <qpopupmenu.h>
00028
00029 #include <kconfig.h>
00030 #include <kckey.h>
00031 #include <kdebug.h>
00032 #include <kglobal.h>
00033 #include <kkeynative.h>
00034 #include <kkeyserver_x11.h>
00035 #include <klocale.h>
00036 #include <kshortcutmenu.h>
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046 KAccelBase::KAccelBase( int fInitCode )
00047 : m_rgActions( this )
00048 {
00049 kdDebug(125) << "KAccelBase(): this = " << this << endl;
00050 m_bNativeKeys = fInitCode & NATIVE_KEYS;
00051 m_bEnabled = true;
00052 m_sConfigGroup = "Shortcuts";
00053 m_bConfigIsGlobal = false;
00054 m_bAutoUpdate = false;
00055 mtemp_pActionRemoving = 0;
00056 }
00057
00058 KAccelBase::~KAccelBase()
00059 {
00060 kdDebug(125) << "~KAccelBase(): this = " << this << endl;
00061 }
00062
00063 uint KAccelBase::actionCount() const { return m_rgActions.count(); }
00064 KAccelActions& KAccelBase::actions() { return m_rgActions; }
00065 bool KAccelBase::isEnabled() const { return m_bEnabled; }
00066
00067 KAccelAction* KAccelBase::actionPtr( const QString& sAction )
00068 { return m_rgActions.actionPtr( sAction ); }
00069
00070 const KAccelAction* KAccelBase::actionPtr( const QString& sAction ) const
00071 { return m_rgActions.actionPtr( sAction ); }
00072
00073 KAccelAction* KAccelBase::actionPtr( const KKeyServer::Key& key )
00074 {
00075 if( !m_mapKeyToAction.contains( key ) )
00076 return 0;
00077
00078 return m_mapKeyToAction[key].pAction;
00079 }
00080
00081 KAccelAction* KAccelBase::actionPtr( const KKey& key )
00082 {
00083 KKeyServer::Key k2;
00084 k2.init( key, !m_bNativeKeys );
00085 return actionPtr( k2 );
00086 }
00087
00088 void KAccelBase::setConfigGroup( const QString& sConfigGroup )
00089 { m_sConfigGroup = sConfigGroup; }
00090
00091 void KAccelBase::setConfigGlobal( bool global )
00092 { m_bConfigIsGlobal = global; }
00093
00094 bool KAccelBase::setActionEnabled( const QString& sAction, bool bEnable )
00095 {
00096 KAccelAction* pAction = actionPtr( sAction );
00097 if( pAction ) {
00098 if( pAction->m_bEnabled != bEnable ) {
00099 kdDebug(125) << "KAccelBase::setActionEnabled( " << sAction << ", " << bEnable << " )" << endl;
00100 pAction->m_bEnabled = bEnable;
00101 if( m_bAutoUpdate ) {
00102
00103 if( bEnable )
00104 insertConnection( pAction );
00105 else if( pAction->isConnected() )
00106 removeConnection( pAction );
00107 }
00108 }
00109 return true;
00110 }
00111 return false;
00112 }
00113
00114 bool KAccelBase::setAutoUpdate( bool bAuto )
00115 {
00116 kdDebug(125) << "KAccelBase::setAutoUpdate( " << bAuto << " ): m_bAutoUpdate on entrance = " << m_bAutoUpdate << endl;
00117 bool b = m_bAutoUpdate;
00118 if( !m_bAutoUpdate && bAuto )
00119 updateConnections();
00120 m_bAutoUpdate = bAuto;
00121 return b;
00122 }
00123
00124 KAccelAction* KAccelBase::insert( const QString& sAction, const QString& sDesc, const QString& sHelp,
00125 const KShortcut& rgCutDefaults3, const KShortcut& rgCutDefaults4,
00126 const QObject* pObjSlot, const char* psMethodSlot,
00127 bool bConfigurable, bool bEnabled )
00128 {
00129
00130 KAccelAction* pAction = m_rgActions.insert(
00131 sAction, sDesc, sHelp,
00132 rgCutDefaults3, rgCutDefaults4,
00133 pObjSlot, psMethodSlot,
00134 bConfigurable, bEnabled );
00135
00136 if( pAction && m_bAutoUpdate )
00137 insertConnection( pAction );
00138
00139
00140 return pAction;
00141 }
00142
00143 KAccelAction* KAccelBase::insert( const QString& sName, const QString& sDesc )
00144 { return m_rgActions.insert( sName, sDesc ); }
00145
00146 bool KAccelBase::remove( const QString& sAction )
00147 {
00148 return m_rgActions.remove( sAction );
00149 }
00150
00151 void KAccelBase::slotRemoveAction( KAccelAction* pAction )
00152 {
00153 removeConnection( pAction );
00154 }
00155
00156 bool KAccelBase::setActionSlot( const QString& sAction, const QObject* pObjSlot, const char* psMethodSlot )
00157 {
00158 kdDebug(125) << "KAccelBase::setActionSlot( " << sAction << ", " << pObjSlot << ", " << psMethodSlot << " )\n";
00159 KAccelAction* pAction = m_rgActions.actionPtr( sAction );
00160 if( pAction ) {
00161
00162 if( m_bAutoUpdate && pAction->isConnected() ) {
00163 kdDebug(125) << "\tm_pObjSlot = " << pAction->m_pObjSlot << " m_psMethodSlot = " << pAction->m_psMethodSlot << endl;
00164 removeConnection( pAction );
00165 }
00166
00167 pAction->m_pObjSlot = pObjSlot;
00168 pAction->m_psMethodSlot = psMethodSlot;
00169
00170
00171 if( m_bAutoUpdate && pObjSlot && psMethodSlot )
00172 insertConnection( pAction );
00173
00174 return true;
00175 } else
00176 return false;
00177 }
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243 struct X
00244 {
00245 uint iAction, iSeq, iVari;
00246 KKeyServer::Key key;
00247
00248 X() {}
00249 X( uint _iAction, uint _iSeq, uint _iVari, const KKeyServer::Key& _key )
00250 { iAction = _iAction; iSeq = _iSeq; iVari = _iVari; key = _key; }
00251
00252 int compare( const X& x )
00253 {
00254 int n = key.compare( x.key );
00255 if( n != 0 ) return n;
00256 if( iVari != x.iVari ) return iVari - x.iVari;
00257 if( iSeq != x.iSeq ) return iSeq - x.iSeq;
00258 return 0;
00259 }
00260
00261 bool operator <( const X& x ) { return compare( x ) < 0; }
00262 bool operator >( const X& x ) { return compare( x ) > 0; }
00263 bool operator <=( const X& x ) { return compare( x ) <= 0; }
00264 };
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308 bool KAccelBase::updateConnections()
00309 {
00310 kdDebug(125) << "KAccelBase::updateConnections() this = " << this << endl;
00311
00312
00313 QValueVector<X> rgKeys;
00314 createKeyList( rgKeys );
00315 m_rgActionsNonUnique.clear();
00316
00317 KKeyToActionMap mapKeyToAction;
00318 for( uint i = 0; i < rgKeys.size(); i++ ) {
00319 X& x = rgKeys[i];
00320 KKeyServer::Key& key = x.key;
00321 ActionInfo info;
00322 bool bNonUnique = false;
00323
00324 info.pAction = m_rgActions.actionPtr( x.iAction );
00325 info.iSeq = x.iSeq;
00326 info.iVariation = x.iVari;
00327
00328
00329 if( info.pAction->shortcut().seq(info.iSeq).count() > 1 )
00330 bNonUnique = true;
00331
00332 else if( i < rgKeys.size() - 1 && key == rgKeys[i+1].key ) {
00333
00334
00335 if( info.iVariation == rgKeys[i+1].iVari && info.iSeq == rgKeys[i+1].iSeq )
00336 bNonUnique = true;
00337
00338 kdDebug(125) << "key conflict = " << key.key().toStringInternal()
00339 << " action1 = " << info.pAction->name()
00340 << " action2 = " << m_rgActions.actionPtr( rgKeys[i+1].iAction )->name()
00341 << " non-unique = " << bNonUnique << endl;
00342
00343
00344 while( i < rgKeys.size() - 1 && key == rgKeys[i+1].key )
00345 i++;
00346 }
00347
00348 if( bNonUnique ) {
00349
00350 if( m_mapKeyToAction.contains( key ) ) {
00351 KAccelAction* pAction = m_mapKeyToAction[key].pAction;
00352 if( pAction ) {
00353 m_mapKeyToAction.remove( key );
00354 disconnectKey( *pAction, key );
00355 pAction->decConnections();
00356 m_rgActionsNonUnique.append( pAction );
00357 }
00358 }
00359
00360 m_rgActionsNonUnique.append( info.pAction );
00361 info.pAction = 0;
00362 }
00363
00364
00365 mapKeyToAction[key] = info;
00366 }
00367
00368
00369 for( KKeyToActionMap::iterator it = m_mapKeyToAction.begin(); it != m_mapKeyToAction.end(); ++it ) {
00370 const KKeyServer::Key& key = it.key();
00371 KAccelAction* pAction = (*it).pAction;
00372
00373 if( !mapKeyToAction.contains( key ) || mapKeyToAction[key].pAction != pAction ) {
00374 if( pAction ) {
00375 disconnectKey( *pAction, key );
00376 pAction->decConnections();
00377 } else
00378 disconnectKey( key );
00379 }
00380 }
00381
00382
00383
00384
00385 for( KKeyToActionMap::iterator it = mapKeyToAction.begin(); it != mapKeyToAction.end(); ++it ) {
00386 const KKeyServer::Key& key = it.key();
00387 KAccelAction* pAction = (*it).pAction;
00388 if( !m_mapKeyToAction.contains( key ) || m_mapKeyToAction[key].pAction != pAction ) {
00389
00390
00391 if( pAction ) {
00392 if( connectKey( *pAction, key ) )
00393 pAction->incConnections();
00394 } else
00395 connectKey( key );
00396 }
00397 }
00398
00399
00400 m_mapKeyToAction = mapKeyToAction;
00401
00402 #ifndef NDEBUG
00403 for( KKeyToActionMap::iterator it = m_mapKeyToAction.begin(); it != m_mapKeyToAction.end(); ++it ) {
00404 kdDebug(125) << "Key: " << it.key().key().toStringInternal() << " => '"
00405 << (((*it).pAction) ? (*it).pAction->name() : QString::null) << "'" << endl;
00406 }
00407 #endif
00408 return true;
00409 }
00410
00411
00412 void KAccelBase::createKeyList( QValueVector<struct X>& rgKeys )
00413 {
00414
00415 if( !m_bEnabled )
00416 return;
00417
00418
00419
00420 for( uint iAction = 0; iAction < m_rgActions.count(); iAction++ ) {
00421 KAccelAction* pAction = m_rgActions.actionPtr( iAction );
00422 if( pAction && pAction->m_pObjSlot && pAction->m_psMethodSlot && pAction != mtemp_pActionRemoving ) {
00423
00424 for( uint iSeq = 0; iSeq < pAction->shortcut().count(); iSeq++ ) {
00425 const KKeySequence& seq = pAction->shortcut().seq(iSeq);
00426 if( seq.count() > 0 ) {
00427 KKeyServer::Variations vars;
00428 vars.init( seq.key(0), !m_bNativeKeys );
00429 for( uint iVari = 0; iVari < vars.count(); iVari++ ) {
00430 if( vars.key(iVari).code() && vars.key(iVari).sym() )
00431 rgKeys.push_back( X( iAction, iSeq, iVari, vars.key( iVari ) ) );
00432
00433 }
00434 }
00435
00436
00437 }
00438 }
00439 }
00440
00441
00442 qHeapSort( rgKeys.begin(), rgKeys.end() );
00443 }
00444
00445 bool KAccelBase::insertConnection( KAccelAction* pAction )
00446 {
00447 if( !pAction->m_pObjSlot || !pAction->m_psMethodSlot )
00448 return true;
00449
00450 kdDebug(125) << "KAccelBase::insertConnection( " << pAction << "=\"" << pAction->m_sName << "\"; shortcut = " << pAction->shortcut().toStringInternal() << " ) this = " << this << endl;
00451
00452
00453 for( uint iSeq = 0; iSeq < pAction->shortcut().count(); iSeq++ ) {
00454
00455 KKeyServer::Variations vars;
00456 vars.init( pAction->shortcut().seq(iSeq).key(0), !m_bNativeKeys );
00457 for( uint iVari = 0; iVari < vars.count(); iVari++ ) {
00458 const KKeyServer::Key& key = vars.key( iVari );
00459
00460
00461 if( key.sym() ) {
00462 if( !m_mapKeyToAction.contains( key ) ) {
00463
00464 if( pAction->shortcut().seq(iSeq).count() == 1 ) {
00465 m_mapKeyToAction[key] = ActionInfo( pAction, iSeq, iVari );
00466 if( connectKey( *pAction, key ) )
00467 pAction->incConnections();
00468 }
00469
00470 else {
00471 m_mapKeyToAction[key] = ActionInfo( 0, 0, 0 );
00472
00473 if( m_rgActionsNonUnique.findIndex( pAction ) == -1 )
00474 m_rgActionsNonUnique.append( pAction );
00475 if( connectKey( key ) )
00476 pAction->incConnections();
00477 }
00478 } else {
00479
00480
00481
00482 if( m_mapKeyToAction[key].pAction != pAction
00483 && m_mapKeyToAction[key].pAction != 0 ) {
00484 kdDebug(125) << "Key conflict: call updateConnections():"
00485 << " key = " << key.key().toStringInternal() << endl;
00486 return updateConnections();
00487 }
00488 }
00489 }
00490 }
00491 }
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505 return true;
00506 }
00507
00508 bool KAccelBase::removeConnection( KAccelAction* pAction )
00509 {
00510 kdDebug(125) << "KAccelBase::removeConnection( " << pAction << " = \"" << pAction->m_sName << "\"; shortcut = " << pAction->m_cut.toStringInternal() << " ): this = " << this << endl;
00511
00512
00513
00514
00515 if( m_rgActionsNonUnique.findIndex( pAction ) >= 0 ) {
00516 mtemp_pActionRemoving = pAction;
00517 bool b = updateConnections();
00518 mtemp_pActionRemoving = 0;
00519 return b;
00520 }
00521
00522 KKeyToActionMap::iterator it = m_mapKeyToAction.begin();
00523 while( it != m_mapKeyToAction.end() ) {
00524 KKeyServer::Key key = it.key();
00525 ActionInfo* pInfo = &(*it);
00526
00527
00528 if( pAction == pInfo->pAction ) {
00529 disconnectKey( *pAction, key );
00530 pAction->decConnections();
00531
00532 KKeyToActionMap::iterator itRemove = it++;
00533 m_mapKeyToAction.remove( itRemove );
00534 } else
00535 it++;
00536 }
00537 return true;
00538 }
00539
00540 bool KAccelBase::setShortcut( const QString& sAction, const KShortcut& cut )
00541 {
00542 KAccelAction* pAction = actionPtr( sAction );
00543 if( pAction ) {
00544 if( m_bAutoUpdate )
00545 removeConnection( pAction );
00546
00547 pAction->setShortcut( cut );
00548
00549 if( m_bAutoUpdate && !pAction->shortcut().isNull() )
00550 insertConnection( pAction );
00551 return true;
00552 } else
00553 return false;
00554 }
00555
00556 void KAccelBase::readSettings( KConfigBase* pConfig )
00557 {
00558 m_rgActions.readActions( m_sConfigGroup, pConfig );
00559 if( m_bAutoUpdate )
00560 updateConnections();
00561 }
00562
00563 void KAccelBase::writeSettings( KConfigBase* pConfig ) const
00564 {
00565 m_rgActions.writeActions( m_sConfigGroup, pConfig, m_bConfigIsGlobal, m_bConfigIsGlobal );
00566 }
00567
00568 QPopupMenu* KAccelBase::createPopupMenu( QWidget* pParent, const KKeySequence& seq )
00569 {
00570 KShortcutMenu* pMenu = new KShortcutMenu( pParent, &actions(), seq );
00571
00572 bool bActionInserted = false;
00573 bool bInsertSeparator = false;
00574 for( uint i = 0; i < actionCount(); i++ ) {
00575 const KAccelAction* pAction = actions().actionPtr( i );
00576
00577 if( !pAction->isEnabled() )
00578 continue;
00579
00580
00581
00582
00583 if( bActionInserted && !pAction->isConfigurable() && pAction->name().contains( ':' ) )
00584 bInsertSeparator = true;
00585
00586 for( uint iSeq = 0; iSeq < pAction->shortcut().count(); iSeq++ ) {
00587 const KKeySequence& seqAction = pAction->shortcut().seq(iSeq);
00588 if( seqAction.startsWith( seq ) ) {
00589 if( bInsertSeparator ) {
00590 pMenu->insertSeparator();
00591 bInsertSeparator = false;
00592 }
00593
00594 pMenu->insertAction( i, seqAction );
00595
00596
00597
00598 bActionInserted = true;
00599 break;
00600 }
00601 }
00602 }
00603 pMenu->updateShortcuts();
00604 return pMenu;
00605 }