File indexing completed on 2024-12-22 04:14:02

0001 /* This file is part of the KDE libraries
0002     SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
0003     SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
0004     SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
0005     SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
0006     SPDX-FileCopyrightText: 2000 Michael Koch <koch@kde.org>
0007     SPDX-FileCopyrightText: 2001 Holger Freyther <freyther@kde.org>
0008     SPDX-FileCopyrightText: 2002 Ellis Whitehead <ellis@kde.org>
0009     SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
0010     SPDX-FileCopyrightText: 2005-2007 Hamish Rodda <rodda@kde.org>
0011 
0012     SPDX-License-Identifier: LGPL-2.0-only
0013 */
0014 
0015 #include "kactioncollection.h"
0016 #include "config-xmlgui.h"
0017 #include "kactioncategory.h"
0018 #include "kxmlguiclient.h"
0019 #include "kxmlguifactory.h"
0020 #include "kis_action_registry.h"
0021 
0022 #include <kconfiggroup.h>
0023 #include <ksharedconfig.h>
0024 
0025 #include <QDebug>
0026 #include <QDomDocument>
0027 #include <QSet>
0028 #include <QGuiApplication>
0029 #include <QMap>
0030 #include <QList>
0031 #include <QAction>
0032 #include <QMetaMethod>
0033 #include <QTextStream>
0034 #include <stdio.h>
0035 
0036 #if defined(KCONFIG_BEFORE_5_24)
0037 # define authorizeAction authorizeKAction
0038 #endif
0039 
0040 class KisKActionCollectionPrivate
0041 {
0042 public:
0043     KisKActionCollectionPrivate()
0044         : m_parentGUIClient(0L),
0045           configGroup(QStringLiteral("Shortcuts")),
0046           q(0)
0047 
0048     {
0049     }
0050 
0051     void setComponentForAction(QAction *action)
0052     {
0053         Q_UNUSED(action);
0054     }
0055 
0056     static QList<KisKActionCollection *> s_allCollections;
0057 
0058     void _k_associatedWidgetDestroyed(QObject *obj);
0059     void _k_actionDestroyed(QObject *obj);
0060 
0061     bool writeKisKXMLGUIConfigFile();
0062 
0063     QString m_componentName;
0064     QString m_componentDisplayName;
0065 
0066     //! Remove a action from our internal bookkeeping. Returns 0 if the
0067     //! action doesn't belong to us.
0068     QAction *unlistAction(QObject *);
0069 
0070     QMap<QString, QAction *> actionByName;
0071     QList<QAction *> actions;
0072 
0073     const KisKXMLGUIClient *m_parentGUIClient {nullptr};
0074 
0075     QString configGroup;
0076 
0077     bool connectTriggered {false};
0078     bool connectHovered {false};
0079 
0080     KisKActionCollection *q {nullptr};
0081 
0082     QList<QWidget *> associatedWidgets;
0083 };
0084 
0085 QList<KisKActionCollection *> KisKActionCollectionPrivate::s_allCollections;
0086 
0087 KisKActionCollection::KisKActionCollection(QObject *parent, const QString &cName)
0088     : QObject(parent)
0089     , d(new KisKActionCollectionPrivate)
0090 {
0091     d->q = this;
0092     KisKActionCollectionPrivate::s_allCollections.append(this);
0093 
0094     setComponentName(cName);
0095 }
0096 
0097 KisKActionCollection::KisKActionCollection(const KisKXMLGUIClient *parent)
0098     : QObject(0)
0099     , d(new KisKActionCollectionPrivate)
0100 {
0101     d->q = this;
0102     KisKActionCollectionPrivate::s_allCollections.append(this);
0103 
0104     d->m_parentGUIClient = parent;
0105     d->m_componentName = parent->componentName();
0106 }
0107 
0108 KisKActionCollection::~KisKActionCollection()
0109 {
0110     KisKActionCollectionPrivate::s_allCollections.removeAll(this);
0111 
0112     delete d;
0113 }
0114 
0115 
0116 QList<KisKActionCategory *> KisKActionCollection::categories() const
0117 {
0118     return this->findChildren<KisKActionCategory *>();
0119 }
0120 
0121 KisKActionCategory *KisKActionCollection::getCategory(const QString &name) {
0122     KisKActionCategory *category = 0;
0123     foreach (KisKActionCategory *c, categories()) {
0124         if (c->text() == name) {
0125             category = c;
0126         }
0127     }
0128 
0129     if (category == 0) {
0130         category = new KisKActionCategory(name, this);
0131     }
0132     return category;
0133 }
0134 
0135 
0136 void KisKActionCollection::clear()
0137 {
0138     d->actionByName.clear();
0139     qDeleteAll(d->actions);
0140     d->actions.clear();
0141 }
0142 
0143 QAction *KisKActionCollection::action(const QString &name) const
0144 {
0145     QAction *action = 0L;
0146 
0147     if (!name.isEmpty()) {
0148         action = d->actionByName.value(name);
0149     }
0150 
0151     return action;
0152 }
0153 
0154 QAction *KisKActionCollection::action(int index) const
0155 {
0156     // ### investigate if any apps use this at all
0157     return actions().value(index);
0158 }
0159 
0160 int KisKActionCollection::count() const
0161 {
0162     return d->actions.count();
0163 }
0164 
0165 bool KisKActionCollection::isEmpty() const
0166 {
0167     return count() == 0;
0168 }
0169 
0170 void KisKActionCollection::setComponentName(const QString &cName)
0171 {
0172     if (count() > 0) {
0173         // Its component name is part of an action's signature in the context of
0174         // global shortcuts and the semantics of changing an existing action's
0175         // signature are, as it seems, impossible to get right.
0176         // As of now this only matters for global shortcuts. We could
0177         // thus relax the requirement and only refuse to change the component data
0178         // if we have actions with global shortcuts in this collection.
0179         qWarning() << "this does not work on a KisKActionCollection containing actions!";
0180     }
0181 
0182     if (!cName.isEmpty()) {
0183         d->m_componentName = cName;
0184     } else {
0185         d->m_componentName = QCoreApplication::applicationName();
0186     }
0187 }
0188 
0189 QString KisKActionCollection::componentName() const
0190 {
0191     return d->m_componentName;
0192 }
0193 
0194 void KisKActionCollection::setComponentDisplayName(const QString &displayName)
0195 {
0196     d->m_componentDisplayName = displayName;
0197 }
0198 
0199 QString KisKActionCollection::componentDisplayName() const
0200 {
0201     if (!d->m_componentDisplayName.isEmpty()) {
0202         return d->m_componentDisplayName;
0203     }
0204     if (!QGuiApplication::applicationDisplayName().isEmpty()) {
0205         return QGuiApplication::applicationDisplayName();
0206     }
0207     return QCoreApplication::applicationName();
0208 }
0209 
0210 const KisKXMLGUIClient *KisKActionCollection::parentGUIClient() const
0211 {
0212     return d->m_parentGUIClient;
0213 }
0214 
0215 QList<QAction *> KisKActionCollection::actions() const
0216 {
0217     return d->actions;
0218 }
0219 
0220 const QList< QAction * > KisKActionCollection::actionsWithoutGroup() const
0221 {
0222     QList<QAction *> ret;
0223     Q_FOREACH (QAction *action, d->actions)
0224         if (!action->actionGroup()) {
0225             ret.append(action);
0226         }
0227     return ret;
0228 }
0229 
0230 const QList< QActionGroup * > KisKActionCollection::actionGroups() const
0231 {
0232     QSet<QActionGroup *> set;
0233     Q_FOREACH (QAction *action, d->actions)
0234         if (action->actionGroup()) {
0235             set.insert(action->actionGroup());
0236         }
0237 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
0238     return QList<QActionGroup*>(set.begin(), set.end());
0239 #else
0240     return QList<QActionGroup*>::fromSet(set);
0241 #endif
0242 }
0243 
0244 QAction *KisKActionCollection::addCategorizedAction(const QString &name, QAction *action, const QString &categoryName)
0245 {
0246     return getCategory(categoryName)->addAction(name, action);
0247 }
0248 
0249 QAction *KisKActionCollection::addAction(const QString &name, QAction *action)
0250 {
0251     if (!action) {
0252         return action;
0253     }
0254 
0255     const QString objectName = action->objectName();
0256     QString indexName = name;
0257 
0258     if (indexName.isEmpty()) {
0259         // No name provided. Use the objectName.
0260         indexName = objectName;
0261 
0262     } else {
0263         // Set the new name
0264         action->setObjectName(indexName);
0265     }
0266 
0267     // No name provided and the action had no name. Make one up. This will not
0268     // work when trying to save shortcuts.
0269     if (indexName.isEmpty()) {
0270         QTextStream(&indexName) << (void *)action;
0271 
0272         action->setObjectName(indexName);
0273     }
0274 
0275     // From now on the objectName has to have a value. Else we cannot safely
0276     // remove actions.
0277     Q_ASSERT(!action->objectName().isEmpty());
0278 
0279     // look if we already have THIS action under THIS name ;)
0280     if (d->actionByName.value(indexName, 0) == action) {
0281         // This is not a multi map!
0282         Q_ASSERT(d->actionByName.count(indexName) == 1);
0283         return action;
0284     }
0285 
0286     // Check if we have another action under this name
0287     if (QAction *oldAction = d->actionByName.value(indexName)) {
0288         takeAction(oldAction);
0289     }
0290 
0291     // Check if we have this action under a different name.
0292     // Not using takeAction because we don't want to remove it from categories,
0293     // and because it has the new name already.
0294     const int oldIndex = d->actions.indexOf(action);
0295     if (oldIndex != -1) {
0296         d->actionByName.remove(d->actionByName.key(action));
0297         d->actions.removeAt(oldIndex);
0298     }
0299 
0300     // Add action to our lists.
0301     d->actionByName.insert(indexName, action);
0302     d->actions.append(action);
0303 
0304     Q_FOREACH (QWidget *widget, d->associatedWidgets) {
0305         widget->addAction(action);
0306     }
0307 
0308     connect(action, SIGNAL(destroyed(QObject*)), SLOT(_k_actionDestroyed(QObject*)));
0309 
0310     d->setComponentForAction(action);
0311 
0312     if (d->connectHovered) {
0313         connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
0314     }
0315 
0316     if (d->connectTriggered) {
0317         connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
0318     }
0319 
0320     emit inserted(action);
0321     return action;
0322 }
0323 
0324 void KisKActionCollection::addActions(const QList<QAction *> &actions)
0325 {
0326     Q_FOREACH (QAction *action, actions) {
0327         addAction(action->objectName(), action);
0328     }
0329 }
0330 
0331 void KisKActionCollection::removeAction(QAction *action)
0332 {
0333     delete takeAction(action);
0334 }
0335 
0336 QAction *KisKActionCollection::takeAction(QAction *action)
0337 {
0338     if (!d->unlistAction(action)) {
0339         return 0;
0340     }
0341 
0342     // Remove the action from all widgets
0343     Q_FOREACH (QWidget *widget, d->associatedWidgets) {
0344         widget->removeAction(action);
0345     }
0346 
0347     action->disconnect(this);
0348 
0349     return action;
0350 }
0351 
0352 QAction *KisKActionCollection::addAction(KStandardAction::StandardAction actionType, const QObject *receiver, const char *member)
0353 {
0354     QAction *action = KStandardAction::create(actionType, receiver, member, this);
0355     return action;
0356 }
0357 
0358 QAction *KisKActionCollection::addAction(KStandardAction::StandardAction actionType, const QString &name,
0359                                       const QObject *receiver, const char *member)
0360 {
0361     // pass 0 as parent, because if the parent is a KisKActionCollection KStandardAction::create automatically
0362     // adds the action to it under the default name. We would trigger the
0363     // warning about renaming the action then.
0364     QAction *action = KStandardAction::create(actionType, receiver, member, 0);
0365     // Give it a parent for gc.
0366     action->setParent(this);
0367     // Remove the name to get rid of the "rename action" warning above
0368     action->setObjectName(name);
0369     // And now add it with the desired name.
0370     return addAction(name, action);
0371 }
0372 
0373 QAction *KisKActionCollection::addAction(const QString &name, const QObject *receiver, const char *member)
0374 {
0375     QAction *a = new QAction(this);
0376     if (receiver && member) {
0377         connect(a, SIGNAL(triggered(bool)), receiver, member);
0378     }
0379     return addAction(name, a);
0380 }
0381 
0382 QKeySequence KisKActionCollection::defaultShortcut(QAction *action) const
0383 {
0384     const QList<QKeySequence> shortcuts = defaultShortcuts(action);
0385     return shortcuts.isEmpty() ? QKeySequence() : shortcuts.first();
0386 }
0387 
0388 QList<QKeySequence> KisKActionCollection::defaultShortcuts(QAction *action) const
0389 {
0390     return action->property("defaultShortcuts").value<QList<QKeySequence> >();
0391 }
0392 
0393 void KisKActionCollection::setDefaultShortcut(QAction *action, const QKeySequence &shortcut)
0394 {
0395     setDefaultShortcuts(action, QList<QKeySequence>() << shortcut);
0396 }
0397 
0398 void KisKActionCollection::setDefaultShortcuts(QAction *action, const QList<QKeySequence> &shortcuts)
0399 {
0400     action->setShortcuts(shortcuts);
0401     action->setProperty("defaultShortcuts", QVariant::fromValue(shortcuts));
0402 }
0403 
0404 bool KisKActionCollection::isShortcutsConfigurable(QAction *action) const
0405 {
0406     // Considered as true by default
0407     const QVariant value = action->property("isShortcutConfigurable");
0408     return value.isValid() ? value.toBool() : true;
0409 }
0410 
0411 void KisKActionCollection::setShortcutsConfigurable(QAction *action, bool configurable)
0412 {
0413     action->setProperty("isShortcutConfigurable", configurable);
0414 }
0415 
0416 QString KisKActionCollection::configGroup() const
0417 {
0418     return d->configGroup;
0419 }
0420 
0421 void KisKActionCollection::setConfigGroup(const QString &group)
0422 {
0423     d->configGroup = group;
0424 }
0425 
0426 void KisKActionCollection::updateShortcuts()
0427 {
0428     auto actionRegistry = KisActionRegistry::instance();
0429 
0430     for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
0431          it != d->actionByName.constEnd(); ++it) {
0432         actionRegistry->updateShortcut(it.key(), it.value());
0433     }
0434 }
0435 
0436 
0437 void KisKActionCollection::readSettings()
0438 {
0439     auto ar = KisActionRegistry::instance();
0440     ar->loadCustomShortcuts();
0441 
0442     for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
0443             it != d->actionByName.constEnd(); ++it) {
0444         QAction *action = it.value();
0445         if (!action) {
0446             continue;
0447         }
0448 
0449         if (isShortcutsConfigurable(action)) {
0450             QString actionName = it.key();
0451             ar->updateShortcut(actionName, action);
0452         }
0453     }
0454 }
0455 
0456 
0457 bool KisKActionCollectionPrivate::writeKisKXMLGUIConfigFile()
0458 {
0459     const KisKXMLGUIClient *kxmlguiClient = q->parentGUIClient();
0460     // return false if there is no KisKXMLGUIClient
0461     if (!kxmlguiClient || kxmlguiClient->xmlFile().isEmpty()) {
0462         return false;
0463     }
0464 
0465 
0466     QString attrShortcut = QStringLiteral("shortcut");
0467 
0468     // Read XML file
0469     QString sXml(KisKXMLGUIFactory::readConfigFile(kxmlguiClient->xmlFile(), q->componentName()));
0470     QDomDocument doc;
0471     doc.setContent(sXml);
0472 
0473     // Process XML data
0474 
0475     // Get hold of ActionProperties tag
0476     QDomElement elem = KisKXMLGUIFactory::actionPropertiesElement(doc);
0477 
0478     // now, iterate through our actions
0479     for (QMap<QString, QAction *>::ConstIterator it = actionByName.constBegin();
0480             it != actionByName.constEnd(); ++it) {
0481         QAction *action = it.value();
0482         if (!action) {
0483             continue;
0484         }
0485 
0486         QString actionName = it.key();
0487 
0488         // If the action name starts with unnamed- spit out a warning and ignore
0489         // it. That name will change at will and will break loading writing
0490         if (actionName.startsWith(QLatin1String("unnamed-"))) {
0491             qCritical() << "Skipped writing shortcut for action " << actionName << "(" << action->text() << ")!";
0492             continue;
0493         }
0494 
0495         bool bSameAsDefault = (action->shortcuts() == q->defaultShortcuts(action));
0496 
0497         // now see if this element already exists
0498         // and create it if necessary (unless bSameAsDefault)
0499         QDomElement act_elem = KisKXMLGUIFactory::findActionByName(elem, actionName, !bSameAsDefault);
0500         if (act_elem.isNull()) {
0501             continue;
0502         }
0503 
0504         if (bSameAsDefault) {
0505             act_elem.removeAttribute(attrShortcut);
0506             if (act_elem.attributes().count() == 1) {
0507                 elem.removeChild(act_elem);
0508             }
0509         } else {
0510             act_elem.setAttribute(attrShortcut, QKeySequence::listToString(action->shortcuts()));
0511         }
0512     }
0513 
0514     // Write back to XML file
0515     KisKXMLGUIFactory::saveConfigFile(doc, kxmlguiClient->localXMLFile(), q->componentName());
0516     return true;
0517 }
0518 
0519 void KisKActionCollection::writeSettings(KConfigGroup *config,
0520                                       bool writeScheme,
0521                                       QAction *oneAction) const
0522 {
0523     // If the caller didn't provide a config group we try to save the KisKXMLGUI
0524     // Configuration file. (This will work if the parentGUI was set and has a
0525     // valid configuration file.)
0526     if (config == 0 && d->writeKisKXMLGUIConfigFile()) {
0527         return;
0528     }
0529 
0530 
0531     KConfigGroup cg(KSharedConfig::openConfig(), configGroup());
0532     if (!config) {
0533         config = &cg;
0534     }
0535 
0536     QList<QAction *> writeActions;
0537     if (oneAction) {
0538         writeActions.append(oneAction);
0539     } else {
0540         writeActions = actions();
0541     }
0542 
0543     for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin();
0544             it != d->actionByName.constEnd(); ++it) {
0545 
0546         QAction *action = it.value();
0547         if (!action) {
0548             continue;
0549         }
0550 
0551         QString actionName = it.key();
0552 
0553         // If the action name starts with unnamed- spit out a warning and ignore
0554         // it. That name will change at will and will break loading writing
0555         if (actionName.startsWith(QLatin1String("unnamed-"))) {
0556             qCritical() << "Skipped saving shortcut for action without name " \
0557                         << action->text() << "!";
0558             continue;
0559         }
0560 
0561         // Write the shortcut
0562         if (isShortcutsConfigurable(action)) {
0563             bool bConfigHasAction = !config->readEntry(actionName, QString()).isEmpty();
0564             bool bSameAsDefault = (action->shortcuts() == defaultShortcuts(action));
0565             // If the current shortcut differs from the default, we want to write.
0566 
0567             KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent;
0568 
0569             if (writeScheme || !bSameAsDefault) {
0570                 // We are instructed to write all shortcuts or the shortcut is
0571                 // not set to its default value. Write it
0572                 QString s = QKeySequence::listToString(action->shortcuts());
0573                 if (s.isEmpty()) {
0574                     s = QStringLiteral("none");
0575                 }
0576                 config->writeEntry(actionName, s, flags);
0577             } else if (bConfigHasAction) {
0578                 // This key is the same as default but exists in config file.
0579                 // Remove it.
0580                 config->deleteEntry(actionName, flags);
0581             }
0582         }
0583     }
0584 
0585     config->sync();
0586 }
0587 
0588 void KisKActionCollection::slotActionTriggered()
0589 {
0590     QAction *action = qobject_cast<QAction *>(sender());
0591     if (action) {
0592         emit actionTriggered(action);
0593     }
0594 }
0595 
0596 void KisKActionCollection::slotActionHighlighted()
0597 {
0598     slotActionHovered();
0599 }
0600 
0601 void KisKActionCollection::slotActionHovered()
0602 {
0603     QAction *action = qobject_cast<QAction *>(sender());
0604     if (action) {
0605         emit actionHighlighted(action);
0606         emit actionHovered(action);
0607     }
0608 }
0609 
0610 void KisKActionCollectionPrivate::_k_actionDestroyed(QObject *obj)
0611 {
0612     unlistAction(obj);
0613 }
0614 
0615 void KisKActionCollection::connectNotify(const QMetaMethod &signal)
0616 {
0617     if (d->connectHovered && d->connectTriggered) {
0618         return;
0619     }
0620 
0621     if (signal.methodSignature() == "actionHighlighted(QAction*)" ||
0622             signal.methodSignature() == "actionHovered(QAction*)") {
0623         if (!d->connectHovered) {
0624             d->connectHovered = true;
0625             Q_FOREACH (QAction *action, actions()) {
0626                 connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
0627             }
0628         }
0629 
0630     } else if (signal.methodSignature() == "actionTriggered(QAction*)") {
0631         if (!d->connectTriggered) {
0632             d->connectTriggered = true;
0633             Q_FOREACH (QAction *action, actions()) {
0634                 connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
0635             }
0636         }
0637     }
0638 
0639     QObject::connectNotify(signal);
0640 }
0641 
0642 const QList< KisKActionCollection * > &KisKActionCollection::allCollections()
0643 {
0644     return KisKActionCollectionPrivate::s_allCollections;
0645 }
0646 
0647 void KisKActionCollection::associateWidget(QWidget *widget) const
0648 {
0649     Q_FOREACH (QAction *action, actions()) {
0650         if (!widget->actions().contains(action)) {
0651             widget->addAction(action);
0652         }
0653     }
0654 }
0655 
0656 void KisKActionCollection::addAssociatedWidget(QWidget *widget)
0657 {
0658     if (!d->associatedWidgets.contains(widget)) {
0659         widget->addActions(actions());
0660 
0661         d->associatedWidgets.append(widget);
0662         connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*)));
0663     }
0664 }
0665 
0666 void KisKActionCollection::removeAssociatedWidget(QWidget *widget)
0667 {
0668     Q_FOREACH (QAction *action, actions()) {
0669         widget->removeAction(action);
0670     }
0671 
0672     d->associatedWidgets.removeAll(widget);
0673     disconnect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*)));
0674 }
0675 
0676 QAction *KisKActionCollectionPrivate::unlistAction(QObject *action)
0677 {
0678     // ATTENTION:
0679     //   This method is called with an QObject formerly known as a QAction
0680     //   during _k_actionDestroyed(). So don't do fancy stuff here that needs a
0681     //   real QAction!
0682 
0683     // Get the index for the action
0684     const auto indexOf = [&](const QObject *action, int from = 0) -> int {
0685         for (int i = from; i < actions.size(); i++) {
0686             if (actions[i] == action) {
0687                 return i;
0688             }
0689         }
0690         return -1;
0691     };
0692 
0693     int index = indexOf(action);
0694 
0695     // Action not found.
0696     if (index == -1) {
0697         return 0;
0698     }
0699 
0700     // An action collection can't have the same action twice.
0701     Q_ASSERT(indexOf(action, index + 1) == -1);
0702 
0703     // Get the actions name
0704     const QString name = action->objectName();
0705 
0706     // Remove the action
0707     actionByName.remove(name);
0708     // Retrieve the typed QObject* pointer
0709     // ATTENTION: THIS ACTION IS PARTIALLY DESTROYED!
0710     QAction *tmp = actions.at(index);
0711     actions.removeAt(index);
0712 
0713     // Remove the action from the categories. Should be only one
0714     QList<KisKActionCategory *> categories = q->findChildren<KisKActionCategory *>();
0715     Q_FOREACH (KisKActionCategory *category, categories) {
0716         category->unlistAction(tmp);
0717     }
0718 
0719     return tmp;
0720 }
0721 
0722 QList< QWidget * > KisKActionCollection::associatedWidgets() const
0723 {
0724     return d->associatedWidgets;
0725 }
0726 
0727 void KisKActionCollection::clearAssociatedWidgets()
0728 {
0729     Q_FOREACH (QWidget *widget, d->associatedWidgets)
0730         Q_FOREACH (QAction *action, actions()) {
0731             widget->removeAction(action);
0732         }
0733 
0734     d->associatedWidgets.clear();
0735 }
0736 
0737 void KisKActionCollectionPrivate::_k_associatedWidgetDestroyed(QObject *obj)
0738 {
0739     associatedWidgets.removeAll(static_cast<QWidget *>(obj));
0740 }
0741 
0742 #include "moc_kactioncollection.cpp"