kaction.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 1999 Reginald Stadlbauer <reggie@kde.org> 00003 (C) 1999 Simon Hausmann <hausmann@kde.org> 00004 (C) 2000 Nicolas Hadacek <haadcek@kde.org> 00005 (C) 2000 Kurt Granroth <granroth@kde.org> 00006 (C) 2000 Michael Koch <koch@kde.org> 00007 (C) 2001 Holger Freyther <freyther@kde.org> 00008 (C) 2002 Ellis Whitehead <ellis@kde.org> 00009 (C) 2002 Joseph Wenninger <jowenn@kde.org> 00010 00011 This library is free software; you can redistribute it and/or 00012 modify it under the terms of the GNU Library General Public 00013 License version 2 as published by the Free Software Foundation. 00014 00015 This library is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 Library General Public License for more details. 00019 00020 You should have received a copy of the GNU Library General Public License 00021 along with this library; see the file COPYING.LIB. If not, write to 00022 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00023 Boston, MA 02110-1301, USA. 00024 */ 00025 00026 #include "kaction.h" 00027 00028 #include <assert.h> 00029 00030 #include <qtooltip.h> 00031 #include <qwhatsthis.h> 00032 00033 #include <kaccel.h> 00034 #include <kaccelbase.h> 00035 #include <kaccelprivate.h> 00036 #include <kapplication.h> 00037 #include <kdebug.h> 00038 #include <kguiitem.h> 00039 #include <kmainwindow.h> 00040 #include <kmenubar.h> 00041 #include <kpopupmenu.h> 00042 #include <ktoolbar.h> 00043 #include <ktoolbarbutton.h> 00044 00066 int KAction::getToolButtonID() 00067 { 00068 static int toolbutton_no = -2; 00069 return toolbutton_no--; 00070 } 00071 00072 //--------------------------------------------------------------------- 00073 // KAction::KActionPrivate 00074 //--------------------------------------------------------------------- 00075 00076 class KAction::KActionPrivate : public KGuiItem 00077 { 00078 public: 00079 KActionPrivate() : KGuiItem() 00080 { 00081 m_kaccel = 0; 00082 m_configurable = true; 00083 } 00084 00085 KAccel *m_kaccel; 00086 QValueList<KAccel*> m_kaccelList; 00087 00088 QString m_groupText; 00089 QString m_group; 00090 00091 KShortcut m_cut; 00092 KShortcut m_cutDefault; 00093 00094 bool m_configurable; 00095 00096 struct Container 00097 { 00098 Container() { m_container = 0; m_representative = 0; m_id = 0; } 00099 Container( const Container& s ) { m_container = s.m_container; 00100 m_id = s.m_id; m_representative = s.m_representative; } 00101 QWidget* m_container; 00102 int m_id; 00103 QWidget* m_representative; 00104 }; 00105 00106 QValueList<Container> m_containers; 00107 }; 00108 00109 //--------------------------------------------------------------------- 00110 // KAction 00111 //--------------------------------------------------------------------- 00112 00113 KAction::KAction( const QString& text, const KShortcut& cut, 00114 const QObject* receiver, const char* slot, 00115 KActionCollection* parent, const char* name ) 00116 : QObject( parent, name ), d(new KActionPrivate) 00117 { 00118 initPrivate( text, cut, receiver, slot ); 00119 } 00120 00121 KAction::KAction( const QString& text, const QString& sIconName, const KShortcut& cut, 00122 const QObject* receiver, const char* slot, 00123 KActionCollection* parent, const char* name ) 00124 : QObject( parent, name ), d(new KActionPrivate) 00125 { 00126 initPrivate( text, cut, receiver, slot ); 00127 d->setIconName( sIconName ); 00128 } 00129 00130 KAction::KAction( const QString& text, const QIconSet& pix, const KShortcut& cut, 00131 const QObject* receiver, const char* slot, 00132 KActionCollection* parent, const char* name ) 00133 : QObject( parent, name ), d(new KActionPrivate) 00134 { 00135 initPrivate( text, cut, receiver, slot ); 00136 d->setIconSet( pix ); 00137 } 00138 00139 KAction::KAction( const KGuiItem& item, const KShortcut& cut, 00140 const QObject* receiver, const char* slot, 00141 KActionCollection* parent, const char* name ) 00142 : QObject( parent, name ), d(new KActionPrivate) 00143 { 00144 initPrivate( item.text(), cut, receiver, slot ); 00145 if( item.hasIcon() ) 00146 setIcon( item.iconName() ); 00147 setToolTip( item.toolTip() ); 00148 setWhatsThis( item.whatsThis() ); 00149 } 00150 00151 #ifndef KDE_NO_COMPAT // KDE 4: remove 00152 KAction::KAction( const QString& text, const KShortcut& cut, 00153 QObject* parent, const char* name ) 00154 : QObject( parent, name ), d(new KActionPrivate) 00155 { 00156 initPrivate( text, cut, 0, 0 ); 00157 } 00158 00159 KAction::KAction( const QString& text, const KShortcut& cut, 00160 const QObject* receiver, 00161 const char* slot, QObject* parent, const char* name ) 00162 : QObject( parent, name ), d(new KActionPrivate) 00163 { 00164 initPrivate( text, cut, receiver, slot ); 00165 } 00166 00167 KAction::KAction( const QString& text, const QIconSet& pix, 00168 const KShortcut& cut, 00169 QObject* parent, const char* name ) 00170 : QObject( parent, name ), d(new KActionPrivate) 00171 { 00172 initPrivate( text, cut, 0, 0 ); 00173 setIconSet( pix ); 00174 } 00175 00176 KAction::KAction( const QString& text, const QString& pix, 00177 const KShortcut& cut, 00178 QObject* parent, const char* name ) 00179 : QObject( parent, name ), d(new KActionPrivate) 00180 { 00181 initPrivate( text, cut, 0, 0 ); 00182 d->setIconName( pix ); 00183 } 00184 00185 KAction::KAction( const QString& text, const QIconSet& pix, 00186 const KShortcut& cut, 00187 const QObject* receiver, const char* slot, QObject* parent, 00188 const char* name ) 00189 : QObject( parent, name ), d(new KActionPrivate) 00190 { 00191 initPrivate( text, cut, receiver, slot ); 00192 setIconSet( pix ); 00193 } 00194 00195 KAction::KAction( const QString& text, const QString& pix, 00196 const KShortcut& cut, 00197 const QObject* receiver, const char* slot, QObject* parent, 00198 const char* name ) 00199 : QObject( parent, name ), d(new KActionPrivate) 00200 { 00201 initPrivate( text, cut, receiver, slot ); 00202 d->setIconName(pix); 00203 } 00204 00205 KAction::KAction( QObject* parent, const char* name ) 00206 : QObject( parent, name ), d(new KActionPrivate) 00207 { 00208 initPrivate( QString::null, KShortcut(), 0, 0 ); 00209 } 00210 #endif // KDE 4: remove end 00211 00212 KAction::~KAction() 00213 { 00214 kdDebug(129) << "KAction::~KAction( this = \"" << name() << "\" )" << endl; // -- ellis 00215 #ifndef KDE_NO_COMPAT 00216 if (d->m_kaccel) 00217 unplugAccel(); 00218 #endif 00219 00220 // If actionCollection hasn't already been destructed, 00221 if ( m_parentCollection ) { 00222 m_parentCollection->take( this ); 00223 00224 const QValueList<KAccel*> & accelList = d->m_kaccelList; 00225 QValueList<KAccel*>::const_iterator itr = accelList.constBegin(); 00226 const QValueList<KAccel*>::const_iterator itrEnd = accelList.constEnd(); 00227 00228 const char * const namePtr = name(); 00229 for (; itr != itrEnd; ++itr ) 00230 (*itr)->remove(namePtr); 00231 00232 } 00233 00234 // Do not call unplugAll from here, as tempting as it sounds. 00235 // KAction is designed around the idea that you need to plug 00236 // _and_ to unplug it "manually". Unplugging leads to an important 00237 // slowdown when e.g. closing the window, in which case we simply 00238 // want to destroy everything asap, not to remove actions one by one 00239 // from the GUI. 00240 00241 delete d; 00242 } 00243 00244 void KAction::initPrivate( const QString& text, const KShortcut& cut, 00245 const QObject* receiver, const char* slot ) 00246 { 00247 d->m_cutDefault = cut; 00248 00249 m_parentCollection = dynamic_cast<KActionCollection *>( parent() ); 00250 kdDebug(129) << "KAction::initPrivate(): this = " << this << " name = \"" << name() << "\" cut = " << cut.toStringInternal() << " m_parentCollection = " << m_parentCollection << endl; 00251 if ( m_parentCollection ) 00252 m_parentCollection->insert( this ); 00253 00254 if ( receiver && slot ) 00255 connect( this, SIGNAL( activated() ), receiver, slot ); 00256 00257 if( !cut.isNull() && !qstrcmp( name(), "unnamed" ) ) 00258 kdWarning(129) << "KAction::initPrivate(): trying to assign a shortcut (" << cut.toStringInternal() << ") to an unnamed action." << endl; 00259 d->setText( text ); 00260 initShortcut( cut ); 00261 } 00262 00263 bool KAction::isPlugged() const 00264 { 00265 return (!d->m_containers.empty()) || d->m_kaccel; 00266 } 00267 00268 bool KAction::isPlugged( const QWidget *container ) const 00269 { 00270 return findContainer( container ) > -1; 00271 } 00272 00273 bool KAction::isPlugged( const QWidget *container, int id ) const 00274 { 00275 int i = findContainer( container ); 00276 return ( i > -1 && itemId( i ) == id ); 00277 } 00278 00279 bool KAction::isPlugged( const QWidget *container, const QWidget *_representative ) const 00280 { 00281 int i = findContainer( container ); 00282 return ( i > -1 && representative( i ) == _representative ); 00283 } 00284 00285 00286 /* 00287 Three actionCollection conditions: 00288 1) Scope is known on creation and KAccel object is created (e.g. KMainWindow) 00289 2) Scope is unknown and no KAccel object is available (e.g. KXMLGUIClient) 00290 a) addClient() will be called on object 00291 b) we just want to add the actions to another KXMLGUIClient object 00292 00293 The question is how to do we incorporate #2b into the XMLGUI framework? 00294 00295 00296 We have a KCommandHistory object with undo and redo actions in a passed actionCollection 00297 We have a KoDoc object which holds a KCommandHistory object and the actionCollection 00298 We have two KoView objects which both point to the same KoDoc object 00299 Undo and Redo should be available in both KoView objects, and 00300 calling the undo->setEnabled() should affect both KoViews 00301 00302 When addClient is called, it needs to be able to find the undo and redo actions 00303 When it calls plug() on them, they need to be inserted into the KAccel object of the appropriate KoView 00304 00305 In this case, the actionCollection belongs to KoDoc and we need to let it know that its shortcuts 00306 have the same scope as the KoView actionCollection 00307 00308 KXMLGUIClient::addSubActionCollection 00309 00310 Document: 00311 create document actions 00312 00313 View 00314 create view actions 00315 add document actionCollection as sub-collection 00316 00317 A parentCollection is created 00318 Scenario 1: parentCollection has a focus widget set (e.g. via KMainWindow) 00319 A KAccel object is created in the parentCollection 00320 A KAction is created with parent=parentCollection 00321 The shortcut is inserted into this actionCollection 00322 Scenario 1a: xml isn't used 00323 done 00324 Scenario 1b: KXMLGUIBuilder::addClient() called 00325 setWidget is called -- ignore 00326 shortcuts are set 00327 Scenario 2: parentCollection has no focus widget (e.g., KParts) 00328 A KAction is created with parent=parentCollection 00329 Scenario 2a: xml isn't used 00330 no shortcuts 00331 Scenario 2b: KXMLGUIBuilder::addClient() called 00332 setWidget is called 00333 shortcuts are inserted into current KAccel 00334 shortcuts are set in all other KAccels, if the action is present in the other KAccels 00335 */ 00336 00337 /* 00338 shortcut may be set: 00339 - on construction 00340 - on plug 00341 - on reading XML 00342 - on plugAccel (deprecated) 00343 00344 On Construction: [via initShortcut()] 00345 insert into KAccel of m_parentCollection, 00346 if kaccel() && isAutoConnectShortcuts() exists 00347 00348 On Plug: [via plug() -> plugShortcut()] 00349 insert into KAccel of m_parentCollection, if exists and not already inserted into 00350 00351 On Read XML: [via setShortcut()] 00352 set in all current KAccels 00353 insert into KAccel of m_parentCollection, if exists and not already inserted into 00354 */ 00355 00356 KAccel* KAction::kaccelCurrent() 00357 { 00358 if( m_parentCollection && m_parentCollection->builderKAccel() ) 00359 return m_parentCollection->builderKAccel(); 00360 else if( m_parentCollection && m_parentCollection->kaccel() ) 00361 return m_parentCollection->kaccel(); 00362 else 00363 return 0L; 00364 } 00365 00366 // Only to be called from initPrivate() 00367 bool KAction::initShortcut( const KShortcut& cut ) 00368 { 00369 d->m_cut = cut; 00370 00371 // Only insert action into KAccel if it has a valid name, 00372 if( qstrcmp( name(), "unnamed" ) && 00373 m_parentCollection && 00374 m_parentCollection->isAutoConnectShortcuts() && 00375 m_parentCollection->kaccel() ) 00376 { 00377 insertKAccel( m_parentCollection->kaccel() ); 00378 return true; 00379 } 00380 return false; 00381 } 00382 00383 // Only to be called from plug() 00384 void KAction::plugShortcut() 00385 { 00386 KAccel* const kaccel = kaccelCurrent(); 00387 00388 //kdDebug(129) << "KAction::plugShortcut(): this = " << this << " kaccel() = " << (m_parentCollection ? m_parentCollection->kaccel() : 0) << endl; 00389 if( kaccel && qstrcmp( name(), "unnamed" ) ) { 00390 // Check if already plugged into current KAccel object 00391 const QValueList<KAccel*> & accelList = d->m_kaccelList; 00392 QValueList<KAccel*>::const_iterator itr = accelList.constBegin(); 00393 const QValueList<KAccel*>::const_iterator itrEnd = accelList.constEnd(); 00394 00395 for( ; itr != itrEnd; ++itr) { 00396 if( (*itr) == kaccel ) 00397 return; 00398 } 00399 00400 insertKAccel( kaccel ); 00401 } 00402 } 00403 00404 bool KAction::setShortcut( const KShortcut& cut ) 00405 { 00406 bool bChanged = (d->m_cut != cut); 00407 d->m_cut = cut; 00408 00409 KAccel* const kaccel = kaccelCurrent(); 00410 bool bInsertRequired = true; 00411 // Apply new shortcut to all existing KAccel objects 00412 00413 const QValueList<KAccel*> & accelList = d->m_kaccelList; 00414 QValueList<KAccel*>::const_iterator itr = accelList.constBegin(); 00415 const QValueList<KAccel*>::const_iterator itrEnd = accelList.constEnd(); 00416 00417 for( ; itr != itrEnd; ++itr) { 00418 // Check whether shortcut has already been plugged into 00419 // the current kaccel object. 00420 if( (*itr) == kaccel ) 00421 bInsertRequired = false; 00422 if( bChanged ) 00423 updateKAccelShortcut( *itr ); 00424 } 00425 00426 // Only insert action into KAccel if it has a valid name, 00427 if( kaccel && bInsertRequired && qstrcmp( name(), "unnamed" ) ) 00428 insertKAccel( kaccel ); 00429 00430 if( bChanged ) { 00431 #ifndef KDE_NO_COMPAT // KDE 4: remove 00432 if ( d->m_kaccel ) 00433 d->m_kaccel->setShortcut( name(), cut ); 00434 #endif // KDE 4: remove end 00435 int len = containerCount(); 00436 for( int i = 0; i < len; ++i ) 00437 updateShortcut( i ); 00438 } 00439 return true; 00440 } 00441 00442 bool KAction::updateKAccelShortcut( KAccel* kaccel ) 00443 { 00444 // Check if action is permitted 00445 if (kapp && !kapp->authorizeKAction(name())) 00446 return false; 00447 00448 bool b = true; 00449 00450 if ( !kaccel->actions().actionPtr( name() ) ) { 00451 if(!d->m_cut.isNull() ) { 00452 kdDebug(129) << "Inserting " << name() << ", " << d->text() << ", " << d->plainText() << endl; 00453 b = kaccel->insert( name(), d->plainText(), QString::null, 00454 d->m_cut, 00455 this, SLOT(slotActivated()), 00456 isShortcutConfigurable(), isEnabled() ); 00457 } 00458 } 00459 else 00460 b = kaccel->setShortcut( name(), d->m_cut ); 00461 00462 return b; 00463 } 00464 00465 void KAction::insertKAccel( KAccel* kaccel ) 00466 { 00467 //kdDebug(129) << "KAction::insertKAccel( " << kaccel << " ): this = " << this << endl; 00468 if ( !kaccel->actions().actionPtr( name() ) ) { 00469 if( updateKAccelShortcut( kaccel ) ) { 00470 d->m_kaccelList.append( kaccel ); 00471 connect( kaccel, SIGNAL(destroyed()), this, SLOT(slotDestroyed()) ); 00472 } 00473 } 00474 else 00475 kdWarning(129) << "KAction::insertKAccel( kaccel = " << kaccel << " ): KAccel object already contains an action name \"" << name() << "\"" << endl; // -- ellis 00476 } 00477 00478 void KAction::removeKAccel( KAccel* kaccel ) 00479 { 00480 //kdDebug(129) << "KAction::removeKAccel( " << i << " ): this = " << this << endl; 00481 QValueList<KAccel*> & accelList = d->m_kaccelList; 00482 QValueList<KAccel*>::iterator itr = accelList.begin(); 00483 const QValueList<KAccel*>::iterator itrEnd = accelList.end(); 00484 00485 for( ; itr != itrEnd; ++itr) { 00486 if( (*itr) == kaccel ) { 00487 kaccel->remove( name() ); 00488 accelList.remove( itr ); 00489 disconnect( kaccel, SIGNAL(destroyed()), this, SLOT(slotDestroyed()) ); 00490 break; 00491 } 00492 } 00493 } 00494 00495 #ifndef KDE_NO_COMPAT 00496 // KDE 4: remove 00497 void KAction::setAccel( int keyQt ) 00498 { 00499 setShortcut( KShortcut(keyQt) ); 00500 } 00501 #endif // KDE 4: remove end 00502 00503 void KAction::updateShortcut( int i ) 00504 { 00505 int id = itemId( i ); 00506 00507 QWidget* w = container( i ); 00508 if ( ::qt_cast<QPopupMenu *>( w ) ) { 00509 QPopupMenu* menu = static_cast<QPopupMenu*>(w); 00510 updateShortcut( menu, id ); 00511 } 00512 else if ( ::qt_cast<QMenuBar *>( w ) ) 00513 static_cast<QMenuBar*>(w)->setAccel( d->m_cut.keyCodeQt(), id ); 00514 } 00515 00516 void KAction::updateShortcut( QPopupMenu* menu, int id ) 00517 { 00518 //kdDebug(129) << "KAction::updateShortcut(): this = " << this << " d->m_kaccelList.count() = " << d->m_kaccelList.count() << endl; 00519 // If the action has a KAccel object, 00520 // show the string representation of its shortcut. 00521 if ( d->m_kaccel || d->m_kaccelList.count() ) { 00522 QString s = menu->text( id ); 00523 int i = s.find( '\t' ); 00524 if ( i >= 0 ) 00525 s.replace( i+1, s.length()-i, d->m_cut.seq(0).toString() ); 00526 else 00527 s += "\t" + d->m_cut.seq(0).toString(); 00528 00529 menu->changeItem( id, s ); 00530 } 00531 // Otherwise insert the shortcut itself into the popup menu. 00532 else { 00533 // This is a fall-hack in case the KAction is missing a proper parent collection. 00534 // It should be removed eventually. --ellis 00535 menu->setAccel( d->m_cut.keyCodeQt(), id ); 00536 kdDebug(129) << "KAction::updateShortcut(): name = \"" << name() << "\", cut = " << d->m_cut.toStringInternal() << "; No KAccel, probably missing a parent collection." << endl; 00537 } 00538 } 00539 00540 const KShortcut& KAction::shortcut() const 00541 { 00542 return d->m_cut; 00543 } 00544 00545 const KShortcut& KAction::shortcutDefault() const 00546 { 00547 return d->m_cutDefault; 00548 } 00549 00550 QString KAction::shortcutText() const 00551 { 00552 return d->m_cut.toStringInternal(); 00553 } 00554 00555 void KAction::setShortcutText( const QString& s ) 00556 { 00557 setShortcut( KShortcut(s) ); 00558 } 00559 00560 #ifndef KDE_NO_COMPAT // Remove in KDE 4 00561 int KAction::accel() const 00562 { 00563 return d->m_cut.keyCodeQt(); 00564 } 00565 #endif 00566 00567 void KAction::setGroup( const QString& grp ) 00568 { 00569 d->m_group = grp; 00570 00571 int len = containerCount(); 00572 for( int i = 0; i < len; ++i ) 00573 updateGroup( i ); 00574 } 00575 00576 void KAction::updateGroup( int ) 00577 { 00578 // DO SOMETHING 00579 } 00580 00581 QString KAction::group() const 00582 { 00583 return d->m_group; 00584 } 00585 00586 bool KAction::isEnabled() const 00587 { 00588 return d->isEnabled(); 00589 } 00590 00591 bool KAction::isShortcutConfigurable() const 00592 { 00593 return d->m_configurable; 00594 } 00595 00596 void KAction::setToolTip( const QString& tt ) 00597 { 00598 d->setToolTip( tt ); 00599 00600 int len = containerCount(); 00601 for( int i = 0; i < len; ++i ) 00602 updateToolTip( i ); 00603 } 00604 00605 void KAction::updateToolTip( int i ) 00606 { 00607 QWidget *w = container( i ); 00608 00609 if ( ::qt_cast<KToolBar *>( w ) ) 00610 QToolTip::add( static_cast<KToolBar*>(w)->getWidget( itemId( i ) ), d->toolTip() ); 00611 } 00612 00613 QString KAction::toolTip() const 00614 { 00615 return d->toolTip(); 00616 } 00617 00618 int KAction::plug( QWidget *w, int index ) 00619 { 00620 //kdDebug(129) << "KAction::plug( " << w << ", " << index << " )" << endl; 00621 if (!w ) { 00622 kdWarning(129) << "KAction::plug called with 0 argument\n"; 00623 return -1; 00624 } 00625 00626 // Ellis: print warning if there is a shortcut, but no KAccel available (often due to no widget available in the actioncollection) 00627 // David: Well, it doesn't matter much, things still work (e.g. Undo in koffice) via QAccel. 00628 // We should probably re-enable the warning for things that only KAccel can do, though - e.g. WIN key (mapped to Meta). 00629 #if 0 //ndef NDEBUG 00630 KAccel* kaccel = kaccelCurrent(); 00631 if( !d->m_cut.isNull() && !kaccel ) { 00632 kdDebug(129) << "KAction::plug(): has no KAccel object; this = " << this << " name = " << name() << " parentCollection = " << m_parentCollection << endl; // ellis 00633 } 00634 #endif 00635 00636 // Check if action is permitted 00637 if (kapp && !kapp->authorizeKAction(name())) 00638 return -1; 00639 00640 plugShortcut(); 00641 00642 if ( ::qt_cast<QPopupMenu *>( w ) ) 00643 { 00644 QPopupMenu* menu = static_cast<QPopupMenu*>( w ); 00645 int id; 00646 // Don't insert shortcut into menu if it's already in a KAccel object. 00647 int keyQt = (d->m_kaccelList.count() || d->m_kaccel) ? 0 : d->m_cut.keyCodeQt(); 00648 00649 if ( d->hasIcon() ) 00650 { 00651 KInstance *instance; 00652 if ( m_parentCollection ) 00653 instance = m_parentCollection->instance(); 00654 else 00655 instance = KGlobal::instance(); 00656 id = menu->insertItem( d->iconSet( KIcon::Small, 0, instance ), d->text(), this,//dsweet 00657 SLOT( slotPopupActivated() ), keyQt, 00658 -1, index ); 00659 } 00660 else 00661 id = menu->insertItem( d->text(), this, 00662 SLOT( slotPopupActivated() ), 00663 keyQt, -1, index ); 00664 00665 // If the shortcut is already in a KAccel object, then 00666 // we need to set the menu item's shortcut text. 00667 if ( d->m_kaccelList.count() || d->m_kaccel ) 00668 updateShortcut( menu, id ); 00669 00670 // call setItemEnabled only if the item really should be disabled, 00671 // because that method is slow and the item is per default enabled 00672 if ( !d->isEnabled() ) 00673 menu->setItemEnabled( id, false ); 00674 00675 if ( !d->whatsThis().isEmpty() ) 00676 menu->setWhatsThis( id, whatsThisWithIcon() ); 00677 00678 addContainer( menu, id ); 00679 connect( menu, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) ); 00680 00681 if ( m_parentCollection ) 00682 m_parentCollection->connectHighlight( menu, this ); 00683 00684 return d->m_containers.count() - 1; 00685 } 00686 else if ( ::qt_cast<KToolBar *>( w ) ) 00687 { 00688 KToolBar *bar = static_cast<KToolBar *>( w ); 00689 00690 int id_ = getToolButtonID(); 00691 KInstance *instance; 00692 if ( m_parentCollection ) 00693 instance = m_parentCollection->instance(); 00694 else 00695 instance = KGlobal::instance(); 00696 00697 if ( icon().isEmpty() && !iconSet().pixmap().isNull() ) // old code using QIconSet directly 00698 { 00699 bar->insertButton( iconSet().pixmap(), id_, SIGNAL( buttonClicked(int, Qt::ButtonState) ), this, 00700 SLOT( slotButtonClicked(int, Qt::ButtonState) ), 00701 d->isEnabled(), d->plainText(), index ); 00702 } 00703 else 00704 { 00705 QString icon = d->iconName(); 00706 if ( icon.isEmpty() ) 00707 icon = "unknown"; 00708 bar->insertButton( icon, id_, SIGNAL( buttonClicked(int, Qt::ButtonState) ), this, 00709 SLOT( slotButtonClicked(int, Qt::ButtonState) ), 00710 d->isEnabled(), d->plainText(), index, instance ); 00711 } 00712 00713 KToolBarButton* ktb = bar->getButton(id_); 00714 ktb->setName( QCString("toolbutton_")+name() ); 00715 00716 if ( !d->whatsThis().isEmpty() ) 00717 QWhatsThis::add( bar->getButton(id_), whatsThisWithIcon() ); 00718 00719 if ( !d->toolTip().isEmpty() ) 00720 QToolTip::add( bar->getButton(id_), d->toolTip() ); 00721 00722 addContainer( bar, id_ ); 00723 00724 connect( bar, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) ); 00725 00726 if ( m_parentCollection ) 00727 m_parentCollection->connectHighlight( bar, this ); 00728 00729 return containerCount() - 1; 00730 } 00731 00732 return -1; 00733 } 00734 00735 void KAction::unplug( QWidget *w ) 00736 { 00737 int i = findContainer( w ); 00738 if ( i == -1 ) 00739 return; 00740 int id = itemId( i ); 00741 00742 if ( ::qt_cast<QPopupMenu *>( w ) ) 00743 { 00744 QPopupMenu *menu = static_cast<QPopupMenu *>( w ); 00745 menu->removeItem( id ); 00746 } 00747 else if ( ::qt_cast<KToolBar *>( w ) ) 00748 { 00749 KToolBar *bar = static_cast<KToolBar *>( w ); 00750 bar->removeItemDelayed( id ); 00751 } 00752 else if ( ::qt_cast<QMenuBar *>( w ) ) 00753 { 00754 QMenuBar *bar = static_cast<QMenuBar *>( w ); 00755 bar->removeItem( id ); 00756 } 00757 00758 removeContainer( i ); 00759 if ( m_parentCollection ) 00760 m_parentCollection->disconnectHighlight( w, this ); 00761 } 00762 00763 void KAction::plugAccel(KAccel *kacc, bool configurable) 00764 { 00765 kdWarning(129) << "KAction::plugAccel(): call to deprecated action." << endl; 00766 kdDebug(129) << kdBacktrace() << endl; 00767 //kdDebug(129) << "KAction::plugAccel( kacc = " << kacc << " ): name \"" << name() << "\"" << endl; 00768 if ( d->m_kaccel ) 00769 unplugAccel(); 00770 00771 // If the parent collection's accel ptr isn't set yet 00772 //if ( m_parentCollection && !m_parentCollection->accel() ) 00773 // m_parentCollection->setAccel( kacc ); 00774 00775 // We can only plug this action into the given KAccel object 00776 // if it does not already contain an action with the same name. 00777 if ( !kacc->actions().actionPtr(name()) ) 00778 { 00779 d->m_kaccel = kacc; 00780 d->m_kaccel->insert(name(), d->plainText(), QString::null, 00781 KShortcut(d->m_cut), 00782 this, SLOT(slotActivated()), 00783 configurable, isEnabled()); 00784 connect(d->m_kaccel, SIGNAL(destroyed()), this, SLOT(slotDestroyed())); 00785 //connect(d->m_kaccel, SIGNAL(keycodeChanged()), this, SLOT(slotKeycodeChanged())); 00786 } 00787 else 00788 kdWarning(129) << "KAction::plugAccel( kacc = " << kacc << " ): KAccel object already contains an action name \"" << name() << "\"" << endl; // -- ellis 00789 } 00790 00791 void KAction::unplugAccel() 00792 { 00793 //kdDebug(129) << "KAction::unplugAccel() " << this << " " << name() << endl; 00794 if ( d->m_kaccel ) 00795 { 00796 d->m_kaccel->remove(name()); 00797 d->m_kaccel = 0; 00798 } 00799 } 00800 00801 void KAction::plugMainWindowAccel( QWidget *w ) 00802 { 00803 // Note: topLevelWidget() stops too early, we can't use it. 00804 QWidget * tl = w; 00805 QWidget * n; 00806 while ( !tl->isDialog() && ( n = tl->parentWidget() ) ) // lookup parent and store 00807 tl = n; 00808 00809 KMainWindow * mw = dynamic_cast<KMainWindow *>(tl); // try to see if it's a kmainwindow 00810 if (mw) 00811 plugAccel( mw->accel() ); 00812 else 00813 kdDebug(129) << "KAction::plugMainWindowAccel: Toplevel widget isn't a KMainWindow, can't plug accel. " << tl << endl; 00814 } 00815 00816 void KAction::setEnabled(bool enable) 00817 { 00818 //kdDebug(129) << "KAction::setEnabled( " << enable << " ): this = " << this << " d->m_kaccelList.count() = " << d->m_kaccelList.count() << endl; 00819 if ( enable == d->isEnabled() ) 00820 return; 00821 00822 #ifndef KDE_NO_COMPAT 00823 // KDE 4: remove 00824 if (d->m_kaccel) 00825 d->m_kaccel->setEnabled(name(), enable); 00826 #endif // KDE 4: remove end 00827 00828 const QValueList<KAccel*> & accelList = d->m_kaccelList; 00829 QValueList<KAccel*>::const_iterator itr = accelList.constBegin(); 00830 const QValueList<KAccel*>::const_iterator itrEnd = accelList.constEnd(); 00831 00832 const char * const namePtr = name(); 00833 00834 for ( ; itr != itrEnd; ++itr ) 00835 (*itr)->setEnabled( namePtr, enable ); 00836 00837 d->setEnabled( enable ); 00838 00839 int len = containerCount(); 00840 for( int i = 0; i < len; ++i ) 00841 updateEnabled( i ); 00842 00843 emit enabled( d->isEnabled() ); 00844 } 00845 00846 void KAction::updateEnabled( int i ) 00847 { 00848 QWidget *w = container( i ); 00849 00850 if ( ::qt_cast<QPopupMenu *>( w ) ) 00851 static_cast<QPopupMenu*>(w)->setItemEnabled( itemId( i ), d->isEnabled() ); 00852 else if ( ::qt_cast<QMenuBar *>( w ) ) 00853 static_cast<QMenuBar*>(w)->setItemEnabled( itemId( i ), d->isEnabled() ); 00854 else if ( ::qt_cast<KToolBar *>( w ) ) 00855 static_cast<KToolBar*>(w)->setItemEnabled( itemId( i ), d->isEnabled() ); 00856 } 00857 00858 void KAction::setShortcutConfigurable( bool b ) 00859 { 00860 d->m_configurable = b; 00861 } 00862 00863 void KAction::setText( const QString& text ) 00864 { 00865 #ifndef KDE_NO_COMPAT 00866 // KDE 4: remove 00867 if (d->m_kaccel) { 00868 KAccelAction* pAction = d->m_kaccel->actions().actionPtr(name()); 00869 if (pAction) 00870 pAction->setLabel( text ); 00871 } 00872 #endif // KDE 4: remove end 00873 const QValueList<KAccel*> & accelList = d->m_kaccelList; 00874 QValueList<KAccel*>::const_iterator itr = accelList.constBegin(); 00875 const QValueList<KAccel*>::const_iterator itrEnd = accelList.constEnd(); 00876 00877 const char * const namePtr = name(); 00878 00879 for( ; itr != itrEnd; ++itr ) { 00880 KAccelAction* const pAction = (*itr)->actions().actionPtr(namePtr); 00881 if (pAction) 00882 pAction->setLabel( text ); 00883 } 00884 00885 d->setText( text ); 00886 00887 int len = containerCount(); 00888 for( int i = 0; i < len; ++i ) 00889 updateText( i ); 00890 } 00891 00892 void KAction::updateText( int i ) 00893 { 00894 QWidget *w = container( i ); 00895 00896 if ( ::qt_cast<QPopupMenu *>( w ) ) { 00897 int id = itemId( i ); 00898 static_cast<QPopupMenu*>(w)->changeItem( id, d->text() ); 00899 if (!d->m_cut.isNull()) 00900 updateShortcut( static_cast<QPopupMenu*>(w), id ); 00901 } 00902 else if ( ::qt_cast<QMenuBar *>( w ) ) 00903 static_cast<QMenuBar*>(w)->changeItem( itemId( i ), d->text() ); 00904 else if ( ::qt_cast<KToolBar *>( w ) ) 00905 { 00906 QWidget *button = static_cast<KToolBar *>(w)->getWidget( itemId( i ) ); 00907 if ( ::qt_cast<KToolBarButton *>( button ) ) 00908 static_cast<KToolBarButton *>(button)->setText( d->plainText() ); 00909 } 00910 } 00911 00912 QString KAction::text() const 00913 { 00914 return d->text(); 00915 } 00916 00917 QString KAction::plainText() const 00918 { 00919 return d->plainText( ); 00920 } 00921 00922 void KAction::setIcon( const QString &icon ) 00923 { 00924 d->setIconName( icon ); 00925 00926 // now handle any toolbars 00927 int len = containerCount(); 00928 for ( int i = 0; i < len; ++i ) 00929 updateIcon( i ); 00930 } 00931 00932 void KAction::updateIcon( int id ) 00933 { 00934 QWidget* w = container( id ); 00935 00936 if ( ::qt_cast<QPopupMenu *>( w ) ) { 00937 int itemId_ = itemId( id ); 00938 static_cast<QPopupMenu*>(w)->changeItem( itemId_, d->iconSet( KIcon::Small ), d->text() ); 00939 if (!d->m_cut.isNull()) 00940 updateShortcut( static_cast<QPopupMenu*>(w), itemId_ ); 00941 } 00942 else if ( ::qt_cast<QMenuBar *>( w ) ) 00943 static_cast<QMenuBar*>(w)->changeItem( itemId( id ), d->iconSet( KIcon::Small ), d->text() ); 00944 else if ( ::qt_cast<KToolBar *>( w ) ) 00945 static_cast<KToolBar *>(w)->setButtonIcon( itemId( id ), d->iconName() ); 00946 } 00947 00948 QString KAction::icon() const 00949 { 00950 return d->iconName( ); 00951 } 00952 00953 void KAction::setIconSet( const QIconSet &iconset ) 00954 { 00955 d->setIconSet( iconset ); 00956 00957 int len = containerCount(); 00958 for( int i = 0; i < len; ++i ) 00959 updateIconSet( i ); 00960 } 00961 00962 00963 void KAction::updateIconSet( int id ) 00964 { 00965 QWidget *w = container( id ); 00966 00967 if ( ::qt_cast<QPopupMenu *>( w ) ) 00968 { 00969 int itemId_ = itemId( id ); 00970 static_cast<QPopupMenu*>(w)->changeItem( itemId_, d->iconSet(), d->text() ); 00971 if (!d->m_cut.isNull()) 00972 updateShortcut( static_cast<QPopupMenu*>(w), itemId_ ); 00973 } 00974 else if ( ::qt_cast<QMenuBar *>( w ) ) 00975 static_cast<QMenuBar*>(w)->changeItem( itemId( id ), d->iconSet(), d->text() ); 00976 else if ( ::qt_cast<KToolBar *>( w ) ) 00977 { 00978 if ( icon().isEmpty() && d->hasIcon() ) // only if there is no named icon ( scales better ) 00979 static_cast<KToolBar *>(w)->setButtonIconSet( itemId( id ), d->iconSet() ); 00980 else 00981 static_cast<KToolBar *>(w)->setButtonIconSet( itemId( id ), d->iconSet( KIcon::Small ) ); 00982 } 00983 } 00984 00985 QIconSet KAction::iconSet( KIcon::Group group, int size ) const 00986 { 00987 return d->iconSet( group, size ); 00988 } 00989 00990 bool KAction::hasIcon() const 00991 { 00992 return d->hasIcon(); 00993 } 00994 00995 void KAction::setWhatsThis( const QString& text ) 00996 { 00997 d->setWhatsThis( text ); 00998 00999 int len = containerCount(); 01000 for( int i = 0; i < len; ++i ) 01001 updateWhatsThis( i ); 01002 } 01003 01004 void KAction::updateWhatsThis( int i ) 01005 { 01006 QPopupMenu* pm = popupMenu( i ); 01007 if ( pm ) 01008 { 01009 pm->setWhatsThis( itemId( i ), d->whatsThis() ); 01010 return; 01011 } 01012 01013 KToolBar *tb = toolBar( i ); 01014 if ( tb ) 01015 { 01016 QWidget *w = tb->getButton( itemId( i ) ); 01017 QWhatsThis::remove( w ); 01018 QWhatsThis::add( w, d->whatsThis() ); 01019 return; 01020 } 01021 } 01022 01023 QString KAction::whatsThis() const 01024 { 01025 return d->whatsThis(); 01026 } 01027 01028 QString KAction::whatsThisWithIcon() const 01029 { 01030 QString text = whatsThis(); 01031 if (!d->iconName().isEmpty()) 01032 return QString::fromLatin1("<img source=\"small|%1\"> %2").arg(d->iconName() ).arg(text); 01033 return text; 01034 } 01035 01036 QWidget* KAction::container( int index ) const 01037 { 01038 assert( index < containerCount() ); 01039 return d->m_containers[ index ].m_container; 01040 } 01041 01042 KToolBar* KAction::toolBar( int index ) const 01043 { 01044 return dynamic_cast<KToolBar *>( d->m_containers[ index ].m_container ); 01045 } 01046 01047 QPopupMenu* KAction::popupMenu( int index ) const 01048 { 01049 return dynamic_cast<QPopupMenu *>( d->m_containers[ index ].m_container ); 01050 } 01051 01052 QWidget* KAction::representative( int index ) const 01053 { 01054 return d->m_containers[ index ].m_representative; 01055 } 01056 01057 int KAction::itemId( int index ) const 01058 { 01059 return d->m_containers[ index ].m_id; 01060 } 01061 01062 int KAction::containerCount() const 01063 { 01064 return d->m_containers.count(); 01065 } 01066 01067 uint KAction::kaccelCount() const 01068 { 01069 return d->m_kaccelList.count(); 01070 } 01071 01072 void KAction::addContainer( QWidget* c, int id ) 01073 { 01074 KActionPrivate::Container p; 01075 p.m_container = c; 01076 p.m_id = id; 01077 d->m_containers.append( p ); 01078 } 01079 01080 void KAction::addContainer( QWidget* c, QWidget* w ) 01081 { 01082 KActionPrivate::Container p; 01083 p.m_container = c; 01084 p.m_representative = w; 01085 d->m_containers.append( p ); 01086 } 01087 01088 void KAction::activate() 01089 { 01090 emit activated( KAction::EmulatedActivation, Qt::NoButton ); 01091 slotActivated(); 01092 } 01093 01094 void KAction::slotActivated() 01095 { 01096 const QObject *senderObj = sender(); 01097 if ( senderObj ) 01098 { 01099 if ( ::qt_cast<KAccelPrivate *>( senderObj ) ) 01100 emit activated( KAction::AccelActivation, Qt::NoButton ); 01101 } 01102 emit activated(); 01103 } 01104 01105 // This catches signals emitted by KActions inserted into QPopupMenu 01106 // We do crude things inside it, because we need to know which 01107 // QPopupMenu emitted the signal. We need to be sure that it is 01108 // only called by QPopupMenus, we plugged us in. 01109 void KAction::slotPopupActivated() 01110 { 01111 if( ::qt_cast<QSignal *>(sender())) 01112 { 01113 int id = dynamic_cast<const QSignal *>(sender())->value().toInt(); 01114 int pos = findContainer(id); 01115 if(pos != -1) 01116 { 01117 QPopupMenu* qpm = dynamic_cast<QPopupMenu *>( container(pos) ); 01118 if(qpm) 01119 { 01120 KPopupMenu* kpm = dynamic_cast<KPopupMenu *>( qpm ); 01121 Qt::ButtonState state; 01122 if ( kpm ) // KPopupMenu? Nice, it stores the state. 01123 state = kpm->state(); 01124 else { // just a QPopupMenu? We'll ask for the state now then (small race condition?) 01125 kdDebug(129) << "KAction::slotPopupActivated not a KPopupMenu -> using keyboardMouseState()" << endl; 01126 state = KApplication::keyboardMouseState(); 01127 } 01128 emit activated( KAction::PopupMenuActivation, state ); 01129 slotActivated(); 01130 return; 01131 } 01132 } 01133 } 01134 01135 kdWarning(129)<<"Don't connect KAction::slotPopupActivated() to anything, expect into QPopupMenus which are in containers. Use slotActivated instead."<<endl; 01136 emit activated( KAction::PopupMenuActivation, Qt::NoButton ); 01137 slotActivated(); 01138 } 01139 01140 void KAction::slotButtonClicked( int, Qt::ButtonState state ) 01141 { 01142 kdDebug(129) << "slotButtonClicked() state=" << state << endl; 01143 emit activated( KAction::ToolBarActivation, state ); 01144 01145 // RightButton isn't really an activation 01146 if ( ( state & LeftButton ) || ( state & MidButton ) ) 01147 slotActivated(); 01148 } 01149 01150 01151 void KAction::slotDestroyed() 01152 { 01153 kdDebug(129) << "KAction::slotDestroyed(): this = " << this << ", name = \"" << name() << "\", sender = " << sender() << endl; 01154 const QObject* const o = sender(); 01155 01156 #ifndef KDE_NO_COMPAT // KDE 4: remove 01157 if ( o == d->m_kaccel ) 01158 { 01159 d->m_kaccel = 0; 01160 return; 01161 } 01162 #endif // KDE 4: remove end 01163 QValueList<KAccel*> & accelList = d->m_kaccelList; 01164 QValueList<KAccel*>::iterator itr = accelList.begin(); 01165 const QValueList<KAccel*>::iterator itrEnd = accelList.end(); 01166 01167 for( ; itr != itrEnd; ++itr) 01168 { 01169 if ( o == *itr ) 01170 { 01171 disconnect( *itr, SIGNAL(destroyed()), this, SLOT(slotDestroyed()) ); 01172 accelList.remove(itr); 01173 return; 01174 } 01175 } 01176 01177 int i; 01178 do 01179 { 01180 i = findContainer( static_cast<const QWidget*>( o ) ); 01181 if ( i != -1 ) 01182 removeContainer( i ); 01183 } while ( i != -1 ); 01184 } 01185 01186 int KAction::findContainer( const QWidget* widget ) const 01187 { 01188 int pos = 0; 01189 01190 const QValueList<KActionPrivate::Container> & containers = d->m_containers; 01191 01192 QValueList<KActionPrivate::Container>::ConstIterator it = containers.constBegin(); 01193 const QValueList<KActionPrivate::Container>::ConstIterator itEnd = containers.constEnd(); 01194 01195 while( it != itEnd ) 01196 { 01197 if ( (*it).m_representative == widget || (*it).m_container == widget ) 01198 return pos; 01199 ++it; 01200 ++pos; 01201 } 01202 01203 return -1; 01204 } 01205 01206 int KAction::findContainer( const int id ) const 01207 { 01208 int pos = 0; 01209 01210 const QValueList<KActionPrivate::Container> & containers = d->m_containers; 01211 01212 QValueList<KActionPrivate::Container>::ConstIterator it = containers.constBegin(); 01213 const QValueList<KActionPrivate::Container>::ConstIterator itEnd = containers.constEnd(); 01214 01215 while( it != itEnd ) 01216 { 01217 if ( (*it).m_id == id ) 01218 return pos; 01219 ++it; 01220 ++pos; 01221 } 01222 01223 return -1; 01224 } 01225 01226 void KAction::removeContainer( int index ) 01227 { 01228 int i = 0; 01229 01230 QValueList<KActionPrivate::Container> & containers = d->m_containers; 01231 01232 QValueList<KActionPrivate::Container>::Iterator it = containers.begin(); 01233 const QValueList<KActionPrivate::Container>::Iterator itEnd = containers.end(); 01234 01235 while( it != itEnd ) 01236 { 01237 if ( i == index ) 01238 { 01239 containers.remove( it ); 01240 return; 01241 } 01242 ++it; 01243 ++i; 01244 } 01245 } 01246 01247 // FIXME: Remove this (ellis) 01248 void KAction::slotKeycodeChanged() 01249 { 01250 kdDebug(129) << "KAction::slotKeycodeChanged()" << endl; // -- ellis 01251 KAccelAction* pAction = d->m_kaccel->actions().actionPtr(name()); 01252 if( pAction ) 01253 setShortcut(pAction->shortcut()); 01254 } 01255 01256 KActionCollection *KAction::parentCollection() const 01257 { 01258 return m_parentCollection; 01259 } 01260 01261 void KAction::unplugAll() 01262 { 01263 while ( containerCount() != 0 ) 01264 unplug( container( 0 ) ); 01265 } 01266 01267 const KGuiItem& KAction::guiItem() const 01268 { 01269 return *d; 01270 } 01271 01272 void KAction::virtual_hook( int, void* ) 01273 { /*BASE::virtual_hook( id, data );*/ } 01274 01275 /* vim: et sw=2 ts=2 01276 */ 01277 01278 #include "kaction.moc"