From bfc967d449565c7435aecfb007e2df6fab1f9188 Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Sat, 20 Sep 2008 02:21:45 +0200 Subject: [PATCH] Add ActionCollection to group and manage (Q)Actions. This is based on the corresponding KActionCollection (though it does only implement a subset of that API). --- src/uisupport/CMakeLists.txt | 2 + src/uisupport/actioncollection.cpp | 203 +++++++++++++++++++++++++++++ src/uisupport/actioncollection.h | 96 ++++++++++++++ 3 files changed, 301 insertions(+) diff --git a/src/uisupport/CMakeLists.txt b/src/uisupport/CMakeLists.txt index 8ce01907..f60c8cae 100644 --- a/src/uisupport/CMakeLists.txt +++ b/src/uisupport/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES abstractbuffercontainer.cpp abstractitemview.cpp action.cpp + actioncollection.cpp bufferview.cpp bufferviewfilter.cpp clearablelineedit.cpp @@ -25,6 +26,7 @@ set(MOC_HDRS abstractbuffercontainer.h abstractitemview.h action.h + actioncollection.h bufferview.h bufferviewfilter.h clearablelineedit.h diff --git a/src/uisupport/actioncollection.cpp b/src/uisupport/actioncollection.cpp index c98c321b..a20d9183 100644 --- a/src/uisupport/actioncollection.cpp +++ b/src/uisupport/actioncollection.cpp @@ -16,4 +16,207 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + *************************************************************************** + * Parts of this implementation are based on KDE's KActionCollection. * ***************************************************************************/ + +#include + +#include "actioncollection.h" + +#include "action.h" + +ActionCollection::ActionCollection(QObject *parent) : QObject(parent) { + _connectTriggered = _connectHovered = false; +} + +ActionCollection::~ActionCollection() { + +} + +void ActionCollection::clear() { + _actionByName.clear(); + qDeleteAll(_actions); + _actions.clear(); +} + +QAction *ActionCollection::action(const QString &name) const { + return _actionByName.value(name, 0); +} + +QList ActionCollection::actions() const { + return _actions; +} + +Action *ActionCollection::addAction(const QString &name, Action *action) { + QAction *act = addAction(name, action); + Q_ASSERT(act == action); + return action; +} + +Action *ActionCollection::addAction(const QString &name, const QObject *receiver, const char *member) { + Action *a = new Action(this); + if(receiver && member) + connect(a, SIGNAL(triggered(bool)), receiver, member); + return addAction(name, a); +} + +QAction *ActionCollection::addAction(const QString &name, QAction *action) { + if(!action) + return action; + + const QString origName = action->objectName(); + QString indexName = name; + + if(indexName.isEmpty()) + indexName = action->objectName(); + else + action->setObjectName(indexName); + if(indexName.isEmpty()) + indexName = QString("unnamed-%1").arg((int)action, 16); + + // do we already have this action? + if(_actionByName.value(indexName, 0) == action) + return action; + // or maybe another action under this name? + if(QAction *oldAction = _actionByName.value(indexName)) + takeAction(oldAction); + + // do we already have this action under a different name? + int oldIndex = _actions.indexOf(action); + if(oldIndex != -1) { + _actionByName.remove(origName); + _actions.removeAt(oldIndex); + } + + // add action + _actionByName.insert(indexName, action); + _actions.append(action); + + foreach(QWidget *widget, _associatedWidgets) { + widget->addAction(action); + } + + connect(action, SIGNAL(destroyed(QObject *)), SLOT(actionDestroyed(QObject *))); + if(_connectHovered) + connect(action, SIGNAL(hovered()), SLOT(slotActionHovered())); + if(_connectTriggered) + connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered())); + + emit inserted(action); + return action; +} + +void ActionCollection::removeAction(QAction *action) { + delete takeAction(action); +} + +QAction *ActionCollection::takeAction(QAction *action) { + if(!unlistAction(action)) + return 0; + + foreach(QWidget *widget, _associatedWidgets) { + widget->removeAction(action); + } + + action->disconnect(this); + return action; +} + +void ActionCollection::readSettings() { + +} + +void ActionCollection::writeSettings() const { + + +} + +void ActionCollection::slotActionTriggered() { + QAction *action = qobject_cast(sender()); + if(action) + emit actionTriggered(action); +} + +void ActionCollection::slotActionHovered() { + QAction *action = qobject_cast(sender()); + if(action) + emit actionHovered(action); +} + +void ActionCollection::actionDestroyed(QObject *obj) { + // remember that this is not an QAction anymore at this point + QAction *action = static_cast(obj); + + unlistAction(action); +} + +void ActionCollection::connectNotify(const char *signal) { + if(_connectHovered && _connectTriggered) + return; + + if(QMetaObject::normalizedSignature(SIGNAL(actionHovered(QAction*))) == signal) { + if(!_connectHovered) { + _connectHovered = true; + foreach (QAction* action, actions()) + connect(action, SIGNAL(hovered()), SLOT(slotActionHovered())); + } + } else if(QMetaObject::normalizedSignature(SIGNAL(actionTriggered(QAction*))) == signal) { + if(!_connectTriggered) { + _connectTriggered = true; + foreach (QAction* action, actions()) + connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered())); + } + } + + QObject::connectNotify(signal); +} + +void ActionCollection::associateWidget(QWidget *widget) const { + foreach(QAction *action, actions()) { + if(!widget->actions().contains(action)) + widget->addAction(action); + } +} + +void ActionCollection::addAssociatedWidget(QWidget *widget) { + if(!_associatedWidgets.contains(widget)) { + widget->addActions(actions()); + _associatedWidgets.append(widget); + connect(widget, SIGNAL(destroyed(QObject *)), SLOT(associatedWidgetDestroyed(QObject *))); + } +} + +void ActionCollection::removeAssociatedWidget(QWidget *widget) { + foreach(QAction *action, actions()) + widget->removeAction(action); + _associatedWidgets.removeAll(widget); + disconnect(widget, SIGNAL(destroyed(QObject *)), this, SLOT(associatedWidgetDestroyed(QObject *))); +} + +QList ActionCollection::associatedWidgets() const { + return _associatedWidgets; +} + +void ActionCollection::clearAssociatedWidgets() { + foreach(QWidget *widget, _associatedWidgets) + foreach(QAction *action, actions()) + widget->removeAction(action); + + _associatedWidgets.clear(); +} + +bool ActionCollection::unlistAction(QAction *action) { + // This might be called with a partly destroyed QAction! + + int index = _actions.indexOf(action); + if(index == -1) return false; + + QString name = action->objectName(); + _actionByName.remove(name); + _actions.removeAt(index); + + // TODO: remove from ActionCategory if we ever get that + + return true; +} diff --git a/src/uisupport/actioncollection.h b/src/uisupport/actioncollection.h index d73b9732..41bc044a 100644 --- a/src/uisupport/actioncollection.h +++ b/src/uisupport/actioncollection.h @@ -16,5 +16,101 @@ * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + *************************************************************************** + * This is a subset of the API of KDE's KActionCollection. * ***************************************************************************/ +#ifndef ACTIONCOLLECTION_H_ +#define ACTIONCOLLECTION_H_ + +#include +#include +#include +#include + +class QWidget; + +class Action; +class QAction; + +class ActionCollection : public QObject { + Q_OBJECT + + public: + explicit ActionCollection(QObject *parent); + virtual ~ActionCollection(); + + /// Clears the entire action collection, deleting all actions. + void clear(); + + /// Associate all action in this collection to the given \a widget. + /** Not that this only adds all current actions in the collection to that widget; + * subsequently added actions won't be added automatically. + */ + void associateWidget(QWidget *widget) const; + + /// Associate all actions in this collection to the given \a widget. + /** Subsequently added actions will be automagically associated with this widget as well. + */ + void addAssociatedWidget(QWidget *widget); + + void removeAssociatedWidget(QWidget *widget); + QList associatedWidgets() const; + void clearAssociatedWidgets(); + + void readSettings(); + void writeSettings() const; + + inline int count() const; + inline bool isEmpty() const; + + QAction *action(int index) const; + QAction *action(const QString &name) const; + QList actions() const; + + QAction *addAction(const QString &name, QAction *action); + Action *addAction(const QString &name, Action *action); + Action *addAction(const QString &name, const QObject *receiver = 0, const char *member = 0); + void removeAction(QAction *action); + QAction *takeAction(QAction *action); + + /// Create new action under the given name, add it to the collection and connect its triggered(bool) signal to the specified receiver. + template + ActionType *add(const QString &name, const QObject *receiver = 0, const char *member = 0) { + ActionType *a = new ActionType(this); + if(receiver && member) + connect(a, SIGNAL(triggered(bool)), receiver, member); + addAction(name, a); + return a; + } + + signals: + void inserted(QAction *action); + void actionHovered(QAction *action); + void actionTriggered(QAction *action); + + protected slots: + virtual void connectNotify(const char *signal); + virtual void slotActionTriggered(); + + private slots: + void slotActionHovered(); + void actionDestroyed(QObject *); + void associatedWidgetDestroyed(QObject *); + + private: + bool unlistAction(QAction *); + + QMap _actionByName; + QList _actions; + QList _associatedWidgets; + + bool _connectHovered; + bool _connectTriggered; +}; + +int ActionCollection::count() const { return actions().count(); } +bool ActionCollection::isEmpty() const { return actions().count(); } + + +#endif -- 2.20.1