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 return addAction(name, action);
00356 }
00357
00358 KAction *KActionCollection::addAction(const QString &name, const QObject *receiver, const char *member)
00359 {
00360 KAction *a = new KAction(this);
00361 if (receiver && member)
00362 connect(a, SIGNAL(triggered(bool)), receiver, member);
00363 return addAction(name, a);
00364 }
00365
00366 QString KActionCollection::configGroup( ) const
00367 {
00368 return d->configGroup;
00369 }
00370
00371 void KActionCollection::setConfigGroup( const QString & group )
00372 {
00373 d->configGroup = group;
00374 }
00375
00376 bool KActionCollection::configIsGlobal() const
00377 {
00378 return d->configIsGlobal;
00379 }
00380
00381 void KActionCollection::setConfigGlobal( bool global )
00382 {
00383 d->configIsGlobal = global;
00384 }
00385
00386 void KActionCollection::importGlobalShortcuts( KConfigGroup* config )
00387 {
00388 Q_ASSERT(config);
00389 if( !config || !config->exists()) {
00390 return;
00391 }
00392
00393 for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
00394 it != d->actionByName.constEnd(); ++it) {
00395 KAction *kaction = qobject_cast<KAction*>(it.value());
00396 if (!kaction)
00397 continue;
00398
00399 QString actionName = it.key();
00400
00401 if( kaction->isShortcutConfigurable() ) {
00402 QString entry = config->readEntry(actionName, QString());
00403 if( !entry.isEmpty() ) {
00404 kaction->setGlobalShortcut( KShortcut(entry), KAction::ActiveShortcut, KAction::NoAutoloading );
00405 } else {
00406 kaction->setGlobalShortcut( kaction->shortcut(KAction::DefaultShortcut), KAction::ActiveShortcut, KAction::NoAutoloading );
00407 }
00408 }
00409 }
00410 }
00411
00412
00413 void KActionCollection::readSettings( KConfigGroup* config )
00414 {
00415 KConfigGroup cg( KGlobal::config(), configGroup() );
00416 if( !config )
00417 config = &cg;
00418
00419 if( !config->exists()) {
00420 return;
00421 }
00422
00423 for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
00424 it != d->actionByName.constEnd(); ++it) {
00425 KAction *kaction = qobject_cast<KAction*>(it.value());
00426 if (!kaction)
00427 continue;
00428
00429
00430 if( kaction->isShortcutConfigurable() ) {
00431 QString actionName = it.key();
00432 QString entry = config->readEntry(actionName, QString());
00433 if( !entry.isEmpty() ) {
00434 kaction->setShortcut( KShortcut(entry), KAction::ActiveShortcut );
00435 } else {
00436 kaction->setShortcut( kaction->shortcut(KAction::DefaultShortcut) );
00437 }
00438 }
00439 }
00440
00441
00442 }
00443
00444 void KActionCollection::exportGlobalShortcuts( KConfigGroup* config, bool writeAll ) const
00445 {
00446 Q_ASSERT(config);
00447 if (!config) {
00448 return;
00449 }
00450
00451 QList<QAction*> writeActions = actions();
00452
00453 for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
00454 it != d->actionByName.constEnd(); ++it) {
00455
00456 KAction *kaction = qobject_cast<KAction*>(it.value());
00457 if (!kaction)
00458 continue;
00459 QString actionName = it.key();
00460
00461
00462
00463 if (actionName.startsWith("unnamed-")) {
00464 kError() << "Skipped exporting Shortcut for action without name " << kaction->text() << "!";
00465 continue;
00466 }
00467
00468 if( kaction->isShortcutConfigurable() && kaction->isGlobalShortcutEnabled() ) {
00469 bool bConfigHasAction = !config->readEntry( actionName, QString() ).isEmpty();
00470 bool bSameAsDefault = (kaction->globalShortcut() == kaction->globalShortcut(KAction::DefaultShortcut));
00471
00472
00473 KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent;
00474 if (configIsGlobal())
00475 flags |= KConfigGroup::Global;
00476 if( writeAll || !bSameAsDefault ) {
00477 QString s = kaction->globalShortcut().toString();
00478 if( s.isEmpty() )
00479 s = "none";
00480 kDebug(125) << "\twriting " << actionName << " = " << s;
00481 config->writeEntry( actionName, s, flags );
00482 }
00483
00484
00485 else if( bConfigHasAction ) {
00486 kDebug(125) << "\tremoving " << actionName << " because == default";
00487 config->deleteEntry( actionName, flags );
00488 }
00489 }
00490 }
00491
00492 config->sync();
00493 }
00494
00495
00496 bool KActionCollectionPrivate::writeKXMLGUIConfigFile()
00497 {
00498 const KXMLGUIClient *kxmlguiClient = q->parentGUIClient();
00499
00500 if (!kxmlguiClient || kxmlguiClient->xmlFile().isEmpty()) {
00501 return false;
00502 }
00503
00504 kDebug(129) << "xmlFile=" << kxmlguiClient->xmlFile();
00505
00506 QString attrShortcut = QLatin1String("shortcut");
00507
00508
00509 QString sXml(KXMLGUIFactory::readConfigFile(kxmlguiClient->xmlFile(), q->componentData()));
00510 QDomDocument doc;
00511 doc.setContent( sXml );
00512
00513
00514
00515
00516 QDomElement elem = KXMLGUIFactory::actionPropertiesElement( doc );
00517
00518
00519 for (QMap<QString, QAction *>::ConstIterator it = actionByName.constBegin();
00520 it != actionByName.constEnd(); ++it) {
00521 KAction *kaction = qobject_cast<KAction*>(it.value());
00522 if (!kaction) {
00523 continue;
00524 }
00525
00526 QString actionName = it.key();
00527
00528
00529
00530 if (actionName.startsWith("unnamed-")) {
00531 kError() << "Skipped writing shortcut for action " << actionName << "(" << kaction->text() << ")!";
00532 continue;
00533 }
00534
00535 bool bSameAsDefault = (kaction->shortcut() == kaction->shortcut(KAction::DefaultShortcut));
00536 kDebug(129) << "name = " << actionName
00537 << " shortcut = " << kaction->shortcut(KAction::ActiveShortcut).toString()
00538 << " globalshortcut = " << kaction->globalShortcut(KAction::ActiveShortcut).toString()
00539 << " def = " << kaction->shortcut(KAction::DefaultShortcut).toString();
00540
00541
00542
00543 QDomElement act_elem = KXMLGUIFactory::findActionByName( elem, actionName, !bSameAsDefault );
00544 if ( act_elem.isNull() )
00545 continue;
00546
00547 if( bSameAsDefault ) {
00548 act_elem.removeAttribute( attrShortcut );
00549
00550 if( act_elem.attributes().count() == 1 )
00551 elem.removeChild( act_elem );
00552 } else {
00553 act_elem.setAttribute( attrShortcut, kaction->shortcut().toString() );
00554 }
00555 }
00556
00557
00558 KXMLGUIFactory::saveConfigFile(doc, kxmlguiClient->xmlFile(), q->componentData());
00559 return true;
00560 }
00561
00562
00563 void KActionCollection::writeSettings( KConfigGroup* config, bool writeAll, QAction* oneAction ) const
00564 {
00565
00566
00567 if (config==0 && d->writeKXMLGUIConfigFile() ) {
00568 return;
00569 }
00570
00571 KConfigGroup cg(KGlobal::config() , configGroup() );
00572 if (!config) {
00573 config = &cg;
00574 }
00575
00576 QList<QAction*> writeActions;
00577 if (oneAction) {
00578 writeActions.append(oneAction);
00579 } else {
00580 writeActions = actions();
00581 }
00582
00583
00584 for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
00585 it != d->actionByName.constEnd(); ++it) {
00586
00587
00588 KAction *kaction = qobject_cast<KAction*>(it.value());
00589 if (!kaction) {
00590 continue;
00591 }
00592
00593 QString actionName = it.key();
00594
00595
00596
00597 if (actionName.startsWith("unnamed-")) {
00598 kError() << "Skipped saving Shortcut for action without name " << kaction->text() << "!";
00599 continue;
00600 }
00601
00602
00603 if( kaction->isShortcutConfigurable() ) {
00604 bool bConfigHasAction = !config->readEntry( actionName, QString() ).isEmpty();
00605 bool bSameAsDefault = (kaction->shortcut() == kaction->shortcut(KAction::DefaultShortcut));
00606
00607
00608 KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent;
00609
00610
00611 if (configIsGlobal()) {
00612 flags |= KConfigGroup::Global;
00613 }
00614
00615 if( writeAll || !bSameAsDefault ) {
00616
00617
00618 QString s = kaction->shortcut().toString();
00619 if( s.isEmpty() )
00620 s = "none";
00621 kDebug(125) << "\twriting " << actionName << " = " << s;
00622 config->writeEntry( actionName, s, flags );
00623
00624 } else if( bConfigHasAction ) {
00625
00626
00627 kDebug(125) << "\tremoving " << actionName << " because == default";
00628 config->deleteEntry( actionName, flags );
00629 }
00630 }
00631 }
00632
00633 config->sync();
00634 }
00635
00636 void KActionCollection::slotActionTriggered( )
00637 {
00638 QAction* action = qobject_cast<QAction*>(sender());
00639 if (action)
00640 emit actionTriggered(action);
00641 }
00642
00643 void KActionCollection::slotActionHighlighted( )
00644 {
00645 slotActionHovered();
00646 }
00647
00648 void KActionCollection::slotActionHovered( )
00649 {
00650 QAction* action = qobject_cast<QAction*>(sender());
00651 if (action) {
00652 emit actionHighlighted(action);
00653 emit actionHovered(action);
00654 }
00655 }
00656
00657
00658 void KActionCollectionPrivate::_k_actionDestroyed( QObject *obj )
00659 {
00660
00661
00662 QAction *action = static_cast<QAction*>(obj);
00663
00664 if (!unlistAction(action))
00665 return;
00666
00667
00668 emit q->removed(action);
00669 }
00670
00671 void KActionCollection::connectNotify ( const char * signal )
00672 {
00673 if (d->connectHovered && d->connectTriggered)
00674 return;
00675
00676 if (QMetaObject::normalizedSignature(SIGNAL(actionHighlighted(QAction*))) == signal ||
00677 QMetaObject::normalizedSignature(SIGNAL(actionHovered(QAction*))) == signal) {
00678 if (!d->connectHovered) {
00679 d->connectHovered = true;
00680 foreach (QAction* action, actions())
00681 connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
00682 }
00683
00684 } else if (QMetaObject::normalizedSignature(SIGNAL(actionTriggered(QAction*))) == signal) {
00685 if (!d->connectTriggered) {
00686 d->connectTriggered = true;
00687 foreach (QAction* action, actions())
00688 connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
00689 }
00690 }
00691
00692 QObject::connectNotify(signal);
00693 }
00694
00695 const QList< KActionCollection * >& KActionCollection::allCollections( )
00696 {
00697 return KActionCollectionPrivate::s_allCollections;
00698 }
00699
00700 void KActionCollection::associateWidget(QWidget* widget) const
00701 {
00702 foreach (QAction* action, actions()) {
00703 if (!widget->actions().contains(action))
00704 widget->addAction(action);
00705 }
00706 }
00707
00708 void KActionCollection::addAssociatedWidget(QWidget * widget)
00709 {
00710 if (!d->associatedWidgets.contains(widget)) {
00711 widget->addActions(actions());
00712
00713 d->associatedWidgets.append(widget);
00714 connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*)));
00715 }
00716 }
00717
00718 void KActionCollection::removeAssociatedWidget(QWidget * widget)
00719 {
00720 foreach (QAction* action, actions())
00721 widget->removeAction(action);
00722
00723 d->associatedWidgets.removeAll(widget);
00724 disconnect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*)));
00725 }
00726
00727
00728 QAction *KActionCollectionPrivate::unlistAction(QAction* action)
00729 {
00730
00731
00732
00733
00734
00735
00736 int index = actions.indexOf(action);
00737
00738
00739 if (index==-1) return NULL;
00740
00741
00742 Q_ASSERT(actions.indexOf(action,index+1)==-1);
00743
00744
00745 const QString name = action->objectName();
00746
00747
00748 actionByName.remove(name);
00749 actions.removeAt(index);
00750
00751
00752 QList<KActionCategory*> categories = q->findChildren<KActionCategory*>();
00753 foreach (KActionCategory *category, categories) {
00754 category->unlistAction(action);
00755 }
00756
00757 return action;
00758 }
00759
00760
00761 QList< QWidget * > KActionCollection::associatedWidgets() const
00762 {
00763 return d->associatedWidgets;
00764 }
00765
00766 void KActionCollection::clearAssociatedWidgets()
00767 {
00768 foreach (QWidget* widget, d->associatedWidgets)
00769 foreach (QAction* action, actions())
00770 widget->removeAction(action);
00771
00772 d->associatedWidgets.clear();
00773 }
00774
00775 void KActionCollectionPrivate::_k_associatedWidgetDestroyed(QObject *obj)
00776 {
00777 associatedWidgets.removeAll(static_cast<QWidget*>(obj));
00778 }
00779
00780
00781
00782
00783 #include "kactioncollection.moc"