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