00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "kactioncollection.h"
00028 #include "kactioncategory.h"
00029 #include <kauthorized.h>
00030 #include "kxmlguiclient.h"
00031 #include "kxmlguifactory.h"
00032
00033 #include "kdebug.h"
00034 #include "kglobal.h"
00035 #include "kaction.h"
00036 #include "kaction_p.h"
00037
00038 #include <QtXml/QDomDocument>
00039 #include <QtCore/QSet>
00040 #include <QtCore/QMap>
00041 #include <QtCore/QList>
00042 #include <QtGui/QAction>
00043
00044 #include <stdio.h>
00045 #include "kcomponentdata.h"
00046 #include "kconfiggroup.h"
00047 #include "klocale.h"
00048
00049 class KActionCollectionPrivate
00050 {
00051 public:
00052 KActionCollectionPrivate()
00053 {
00054 q = 0;
00055 m_parentGUIClient = 0L;
00056
00057 configIsGlobal = false;
00058
00059 connectHovered = connectTriggered = false;
00060
00061 configGroup = "Shortcuts";
00062 }
00063
00064 void setComponentForAction(KAction *kaction)
00065 { kaction->d->maybeSetComponentData(m_componentData); }
00066
00067 static QList<KActionCollection*> s_allCollections;
00068
00069 void _k_associatedWidgetDestroyed(QObject *obj);
00070 void _k_actionDestroyed(QObject *obj);
00071
00072 bool writeKXMLGUIConfigFile();
00073
00074 KComponentData m_componentData;
00075
00078 QAction *unlistAction(QAction*);
00079
00080 QMap<QString, QAction*> actionByName;
00081 QList<QAction*> actions;
00082
00083 const KXMLGUIClient *m_parentGUIClient;
00084
00085 QString configGroup;
00086 bool configIsGlobal : 1;
00087
00088 bool connectTriggered : 1;
00089 bool connectHovered : 1;
00090
00091 KActionCollection *q;
00092
00093 QList<QWidget*> associatedWidgets;
00094 };
00095
00096 QList<KActionCollection*> KActionCollectionPrivate::s_allCollections;
00097
00098 KActionCollection::KActionCollection(QObject *parent, const KComponentData &cData)
00099 : QObject( parent )
00100 , d(new KActionCollectionPrivate)
00101 {
00102 d->q = this;
00103 KActionCollectionPrivate::s_allCollections.append(this);
00104
00105 setComponentData(cData);
00106 }
00107
00108 KActionCollection::KActionCollection( const KXMLGUIClient *parent )
00109 : QObject( 0 )
00110 , d(new KActionCollectionPrivate)
00111 {
00112 d->q = this;
00113 KActionCollectionPrivate::s_allCollections.append(this);
00114
00115 d->m_parentGUIClient=parent;
00116 d->m_componentData = parent->componentData();
00117 }
00118
00119 KActionCollection::~KActionCollection()
00120 {
00121 KActionCollectionPrivate::s_allCollections.removeAll(this);
00122
00123 delete d;
00124 }
00125
00126 void KActionCollection::clear()
00127 {
00128 d->actionByName.clear();
00129 qDeleteAll(d->actions);
00130 d->actions.clear();
00131 }
00132
00133 QAction* KActionCollection::action( const QString& name ) const
00134 {
00135 QAction* action = 0L;
00136
00137 if ( !name.isEmpty() )
00138 action = d->actionByName.value (name);
00139
00140 return action;
00141 }
00142
00143 QAction* KActionCollection::action( int index ) const
00144 {
00145
00146 return actions().value(index);
00147 }
00148
00149 int KActionCollection::count() const
00150 {
00151 return d->actions.count();
00152 }
00153
00154 bool KActionCollection::isEmpty() const
00155 {
00156 return count() == 0;
00157 }
00158
00159 void KActionCollection::setComponentData(const KComponentData &cData)
00160 {
00161 if (count() > 0) {
00162
00163
00164
00165
00166
00167
00168 kWarning(129) << "this does not work on a KActionCollection containing actions!";
00169 }
00170
00171 if (cData.isValid()) {
00172 d->m_componentData = cData;
00173 } else {
00174 d->m_componentData = KGlobal::mainComponent();
00175 }
00176 }
00177
00178 KComponentData KActionCollection::componentData() const
00179 {
00180 return d->m_componentData;
00181 }
00182
00183 const KXMLGUIClient *KActionCollection::parentGUIClient() const
00184 {
00185 return d->m_parentGUIClient;
00186 }
00187
00188 QList<QAction*> KActionCollection::actions() const
00189 {
00190 return d->actions;
00191 }
00192
00193 const QList< QAction* > KActionCollection::actionsWithoutGroup( ) const
00194 {
00195 QList<QAction*> ret;
00196 foreach (QAction* action, d->actions)
00197 if (!action->actionGroup())
00198 ret.append(action);
00199 return ret;
00200 }
00201
00202 const QList< QActionGroup * > KActionCollection::actionGroups( ) const
00203 {
00204 QSet<QActionGroup*> set;
00205 foreach (QAction* action, d->actions)
00206 if (action->actionGroup())
00207 set.insert(action->actionGroup());
00208 return set.toList();
00209 }
00210
00211 KAction *KActionCollection::addAction(const QString &name, KAction *action)
00212 {
00213 QAction* ret = addAction(name, static_cast<QAction*>(action));
00214 Q_ASSERT(ret == action);
00215 Q_UNUSED(ret);
00216 return action;
00217 }
00218
00219 QAction *KActionCollection::addAction(const QString &name, QAction *action)
00220 {
00221 if (!action)
00222 return action;
00223
00224 const QString objectName = action->objectName();
00225 QString indexName = name;
00226
00227 if (indexName.isEmpty()) {
00228
00229 indexName = objectName;
00230
00231 } else {
00232
00233
00234 if ((!objectName.isEmpty()) && (objectName != indexName)) {
00235
00236
00237
00238 KAction *kaction = qobject_cast<KAction*>(action);
00239 kDebug(125) << "Registering action " << objectName << " under new name " << indexName;
00240
00241 if (kaction && kaction->isGlobalShortcutEnabled()) {
00242
00243 Q_ASSERT(!kaction->isGlobalShortcutEnabled());
00244
00245 kError() << "Changing action name from " << objectName << " to " << indexName << "\nignored because of active global shortcut.";
00246 indexName = objectName;
00247 }
00248 }
00249
00250
00251 action->setObjectName(indexName);
00252 }
00253
00254
00255
00256 if( indexName.isEmpty() ) {
00257 indexName = indexName.sprintf("unnamed-%p", (void*)action);
00258 action->setObjectName(indexName);
00259 }
00260
00261
00262
00263 Q_ASSERT(!action->objectName().isEmpty());
00264
00265
00266 if (d->actionByName.value(indexName, 0) == action ) {
00267
00268 Q_ASSERT( d->actionByName.count(indexName)==1);
00269 return action;
00270 }
00271
00272 if (!KAuthorized::authorizeKAction(indexName)) {
00273
00274 action->setEnabled(false);
00275 action->setVisible(false);
00276 action->blockSignals(true);
00277 }
00278
00279
00280 if (QAction *oldAction = d->actionByName.value(indexName)) {
00281 takeAction(oldAction);
00282 }
00283
00284
00285
00286
00287 const int oldIndex = d->actions.indexOf(action);
00288 if (oldIndex != -1) {
00289 d->actionByName.remove(d->actionByName.key(action));
00290 d->actions.removeAt(oldIndex);
00291 }
00292
00293
00294 d->actionByName.insert(indexName, action);
00295 d->actions.append(action);
00296
00297 foreach (QWidget* widget, d->associatedWidgets) {
00298 widget->addAction(action);
00299 }
00300
00301 connect(action, SIGNAL(destroyed(QObject*)), SLOT(_k_actionDestroyed(QObject*)));
00302
00303
00304 if (KAction *kaction = dynamic_cast<KAction *>(action)) {
00305 d->setComponentForAction(kaction);
00306 }
00307
00308 if (d->connectHovered)
00309 connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
00310
00311 if (d->connectTriggered)
00312 connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
00313
00314 emit inserted( action );
00315 return action;
00316 }
00317
00318 void KActionCollection::removeAction( QAction* action )
00319 {
00320 delete takeAction( action );
00321 }
00322
00323 QAction* KActionCollection::takeAction(QAction *action)
00324 {
00325 if (!d->unlistAction(action))
00326 return NULL;
00327
00328
00329 foreach (QWidget* widget, d->associatedWidgets) {
00330 widget->removeAction(action);
00331 }
00332
00333 action->disconnect(this);
00334
00335 emit removed( action );
00336 return action;
00337 }
00338
00339 KAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QObject *receiver, const char *member)
00340 {
00341 KAction *action = KStandardAction::create(actionType, receiver, member, this);
00342 return action;
00343 }
00344
00345 KAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QString &name,
00346 const QObject *receiver, const char *member)
00347 {
00348
00349
00350
00351 KAction *action = KStandardAction::create(actionType, receiver, member, 0);
00352
00353 action->setParent(this);
00354
00355 action->setObjectName(name);
00356
00357 return addAction(name, action);
00358 }
00359
00360 KAction *KActionCollection::addAction(const QString &name, const QObject *receiver, const char *member)
00361 {
00362 KAction *a = new KAction(this);
00363 if (receiver && member)
00364 connect(a, SIGNAL(triggered(bool)), receiver, member);
00365 return addAction(name, a);
00366 }
00367
00368 QString KActionCollection::configGroup( ) const
00369 {
00370 return d->configGroup;
00371 }
00372
00373 void KActionCollection::setConfigGroup( const QString & group )
00374 {
00375 d->configGroup = group;
00376 }
00377
00378 bool KActionCollection::configIsGlobal() const
00379 {
00380 return d->configIsGlobal;
00381 }
00382
00383 void KActionCollection::setConfigGlobal( bool global )
00384 {
00385 d->configIsGlobal = global;
00386 }
00387
00388 void KActionCollection::importGlobalShortcuts( KConfigGroup* config )
00389 {
00390 Q_ASSERT(config);
00391 if( !config || !config->exists()) {
00392 return;
00393 }
00394
00395 for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
00396 it != d->actionByName.constEnd(); ++it) {
00397 KAction *kaction = qobject_cast<KAction*>(it.value());
00398 if (!kaction)
00399 continue;
00400
00401 QString actionName = it.key();
00402
00403 if( kaction->isShortcutConfigurable() ) {
00404 QString entry = config->readEntry(actionName, QString());
00405 if( !entry.isEmpty() ) {
00406 kaction->setGlobalShortcut( KShortcut(entry), KAction::ActiveShortcut, KAction::NoAutoloading );
00407 } else {
00408 kaction->setGlobalShortcut( kaction->shortcut(KAction::DefaultShortcut), KAction::ActiveShortcut, KAction::NoAutoloading );
00409 }
00410 }
00411 }
00412 }
00413
00414
00415 void KActionCollection::readSettings( KConfigGroup* config )
00416 {
00417 KConfigGroup cg( KGlobal::config(), configGroup() );
00418 if( !config )
00419 config = &cg;
00420
00421 if( !config->exists()) {
00422 return;
00423 }
00424
00425 for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
00426 it != d->actionByName.constEnd(); ++it) {
00427 KAction *kaction = qobject_cast<KAction*>(it.value());
00428 if (!kaction)
00429 continue;
00430
00431
00432 if( kaction->isShortcutConfigurable() ) {
00433 QString actionName = it.key();
00434 QString entry = config->readEntry(actionName, QString());
00435 if( !entry.isEmpty() ) {
00436 kaction->setShortcut( KShortcut(entry), KAction::ActiveShortcut );
00437 } else {
00438 kaction->setShortcut( kaction->shortcut(KAction::DefaultShortcut) );
00439 }
00440 }
00441 }
00442
00443
00444 }
00445
00446 void KActionCollection::exportGlobalShortcuts( KConfigGroup* config, bool writeAll ) const
00447 {
00448 Q_ASSERT(config);
00449 if (!config) {
00450 return;
00451 }
00452
00453 QList<QAction*> writeActions = actions();
00454
00455 for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
00456 it != d->actionByName.constEnd(); ++it) {
00457
00458 KAction *kaction = qobject_cast<KAction*>(it.value());
00459 if (!kaction)
00460 continue;
00461 QString actionName = it.key();
00462
00463
00464
00465 if (actionName.startsWith("unnamed-")) {
00466 kError() << "Skipped exporting Shortcut for action without name " << kaction->text() << "!";
00467 continue;
00468 }
00469
00470 if( kaction->isShortcutConfigurable() && kaction->isGlobalShortcutEnabled() ) {
00471 bool bConfigHasAction = !config->readEntry( actionName, QString() ).isEmpty();
00472 bool bSameAsDefault = (kaction->globalShortcut() == kaction->globalShortcut(KAction::DefaultShortcut));
00473
00474
00475 KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent;
00476 if (configIsGlobal())
00477 flags |= KConfigGroup::Global;
00478 if( writeAll || !bSameAsDefault ) {
00479 QString s = kaction->globalShortcut().toString();
00480 if( s.isEmpty() )
00481 s = "none";
00482 kDebug(125) << "\twriting " << actionName << " = " << s;
00483 config->writeEntry( actionName, s, flags );
00484 }
00485
00486
00487 else if( bConfigHasAction ) {
00488 kDebug(125) << "\tremoving " << actionName << " because == default";
00489 config->deleteEntry( actionName, flags );
00490 }
00491 }
00492 }
00493
00494 config->sync();
00495 }
00496
00497
00498 bool KActionCollectionPrivate::writeKXMLGUIConfigFile()
00499 {
00500 const KXMLGUIClient *kxmlguiClient = q->parentGUIClient();
00501
00502 if (!kxmlguiClient || kxmlguiClient->xmlFile().isEmpty()) {
00503 return false;
00504 }
00505
00506 kDebug(129) << "xmlFile=" << kxmlguiClient->xmlFile();
00507
00508 QString attrShortcut = QLatin1String("shortcut");
00509
00510
00511 QString sXml(KXMLGUIFactory::readConfigFile(kxmlguiClient->xmlFile(), q->componentData()));
00512 QDomDocument doc;
00513 doc.setContent( sXml );
00514
00515
00516
00517
00518 QDomElement elem = KXMLGUIFactory::actionPropertiesElement( doc );
00519
00520
00521 for (QMap<QString, QAction *>::ConstIterator it = actionByName.constBegin();
00522 it != actionByName.constEnd(); ++it) {
00523 KAction *kaction = qobject_cast<KAction*>(it.value());
00524 if (!kaction) {
00525 continue;
00526 }
00527
00528 QString actionName = it.key();
00529
00530
00531
00532 if (actionName.startsWith("unnamed-")) {
00533 kError() << "Skipped writing shortcut for action " << actionName << "(" << kaction->text() << ")!";
00534 continue;
00535 }
00536
00537 bool bSameAsDefault = (kaction->shortcut() == kaction->shortcut(KAction::DefaultShortcut));
00538 kDebug(129) << "name = " << actionName
00539 << " shortcut = " << kaction->shortcut(KAction::ActiveShortcut).toString()
00540 << " globalshortcut = " << kaction->globalShortcut(KAction::ActiveShortcut).toString()
00541 << " def = " << kaction->shortcut(KAction::DefaultShortcut).toString();
00542
00543
00544
00545 QDomElement act_elem = KXMLGUIFactory::findActionByName( elem, actionName, !bSameAsDefault );
00546 if ( act_elem.isNull() )
00547 continue;
00548
00549 if( bSameAsDefault ) {
00550 act_elem.removeAttribute( attrShortcut );
00551
00552 if( act_elem.attributes().count() == 1 )
00553 elem.removeChild( act_elem );
00554 } else {
00555 act_elem.setAttribute( attrShortcut, kaction->shortcut().toString() );
00556 }
00557 }
00558
00559
00560 KXMLGUIFactory::saveConfigFile(doc, kxmlguiClient->xmlFile(), q->componentData());
00561 return true;
00562 }
00563
00564
00565 void KActionCollection::writeSettings( KConfigGroup* config, bool writeAll, QAction* oneAction ) const
00566 {
00567
00568
00569 if (config==0 && d->writeKXMLGUIConfigFile() ) {
00570 return;
00571 }
00572
00573 KConfigGroup cg(KGlobal::config() , configGroup() );
00574 if (!config) {
00575 config = &cg;
00576 }
00577
00578 QList<QAction*> writeActions;
00579 if (oneAction) {
00580 writeActions.append(oneAction);
00581 } else {
00582 writeActions = actions();
00583 }
00584
00585
00586 for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
00587 it != d->actionByName.constEnd(); ++it) {
00588
00589
00590 KAction *kaction = qobject_cast<KAction*>(it.value());
00591 if (!kaction) {
00592 continue;
00593 }
00594
00595 QString actionName = it.key();
00596
00597
00598
00599 if (actionName.startsWith("unnamed-")) {
00600 kError() << "Skipped saving Shortcut for action without name " << kaction->text() << "!";
00601 continue;
00602 }
00603
00604
00605 if( kaction->isShortcutConfigurable() ) {
00606 bool bConfigHasAction = !config->readEntry( actionName, QString() ).isEmpty();
00607 bool bSameAsDefault = (kaction->shortcut() == kaction->shortcut(KAction::DefaultShortcut));
00608
00609
00610 KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent;
00611
00612
00613 if (configIsGlobal()) {
00614 flags |= KConfigGroup::Global;
00615 }
00616
00617 if( writeAll || !bSameAsDefault ) {
00618
00619
00620 QString s = kaction->shortcut().toString();
00621 if( s.isEmpty() )
00622 s = "none";
00623 kDebug(125) << "\twriting " << actionName << " = " << s;
00624 config->writeEntry( actionName, s, flags );
00625
00626 } else if( bConfigHasAction ) {
00627
00628
00629 kDebug(125) << "\tremoving " << actionName << " because == default";
00630 config->deleteEntry( actionName, flags );
00631 }
00632 }
00633 }
00634
00635 config->sync();
00636 }
00637
00638 void KActionCollection::slotActionTriggered( )
00639 {
00640 QAction* action = qobject_cast<QAction*>(sender());
00641 if (action)
00642 emit actionTriggered(action);
00643 }
00644
00645 void KActionCollection::slotActionHighlighted( )
00646 {
00647 slotActionHovered();
00648 }
00649
00650 void KActionCollection::slotActionHovered( )
00651 {
00652 QAction* action = qobject_cast<QAction*>(sender());
00653 if (action) {
00654 emit actionHighlighted(action);
00655 emit actionHovered(action);
00656 }
00657 }
00658
00659
00660 void KActionCollectionPrivate::_k_actionDestroyed( QObject *obj )
00661 {
00662
00663
00664 QAction *action = static_cast<QAction*>(obj);
00665
00666 if (!unlistAction(action))
00667 return;
00668
00669
00670 emit q->removed(action);
00671 }
00672
00673 void KActionCollection::connectNotify ( const char * signal )
00674 {
00675 if (d->connectHovered && d->connectTriggered)
00676 return;
00677
00678 if (QMetaObject::normalizedSignature(SIGNAL(actionHighlighted(QAction*))) == signal ||
00679 QMetaObject::normalizedSignature(SIGNAL(actionHovered(QAction*))) == signal) {
00680 if (!d->connectHovered) {
00681 d->connectHovered = true;
00682 foreach (QAction* action, actions())
00683 connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
00684 }
00685
00686 } else if (QMetaObject::normalizedSignature(SIGNAL(actionTriggered(QAction*))) == signal) {
00687 if (!d->connectTriggered) {
00688 d->connectTriggered = true;
00689 foreach (QAction* action, actions())
00690 connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
00691 }
00692 }
00693
00694 QObject::connectNotify(signal);
00695 }
00696
00697 const QList< KActionCollection * >& KActionCollection::allCollections( )
00698 {
00699 return KActionCollectionPrivate::s_allCollections;
00700 }
00701
00702 void KActionCollection::associateWidget(QWidget* widget) const
00703 {
00704 foreach (QAction* action, actions()) {
00705 if (!widget->actions().contains(action))
00706 widget->addAction(action);
00707 }
00708 }
00709
00710 void KActionCollection::addAssociatedWidget(QWidget * widget)
00711 {
00712 if (!d->associatedWidgets.contains(widget)) {
00713 widget->addActions(actions());
00714
00715 d->associatedWidgets.append(widget);
00716 connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*)));
00717 }
00718 }
00719
00720 void KActionCollection::removeAssociatedWidget(QWidget * widget)
00721 {
00722 foreach (QAction* action, actions())
00723 widget->removeAction(action);
00724
00725 d->associatedWidgets.removeAll(widget);
00726 disconnect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*)));
00727 }
00728
00729
00730 QAction *KActionCollectionPrivate::unlistAction(QAction* action)
00731 {
00732
00733
00734
00735
00736
00737
00738 int index = actions.indexOf(action);
00739
00740
00741 if (index==-1) return NULL;
00742
00743
00744 Q_ASSERT(actions.indexOf(action,index+1)==-1);
00745
00746
00747 const QString name = action->objectName();
00748
00749
00750 actionByName.remove(name);
00751 actions.removeAt(index);
00752
00753
00754 QList<KActionCategory*> categories = q->findChildren<KActionCategory*>();
00755 foreach (KActionCategory *category, categories) {
00756 category->unlistAction(action);
00757 }
00758
00759 return action;
00760 }
00761
00762
00763 QList< QWidget * > KActionCollection::associatedWidgets() const
00764 {
00765 return d->associatedWidgets;
00766 }
00767
00768 void KActionCollection::clearAssociatedWidgets()
00769 {
00770 foreach (QWidget* widget, d->associatedWidgets)
00771 foreach (QAction* action, actions())
00772 widget->removeAction(action);
00773
00774 d->associatedWidgets.clear();
00775 }
00776
00777 void KActionCollectionPrivate::_k_associatedWidgetDestroyed(QObject *obj)
00778 {
00779 associatedWidgets.removeAll(static_cast<QWidget*>(obj));
00780 }
00781
00782
00783
00784
00785 #include "kactioncollection.moc"