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 KAccelBase::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 }