Add ActionCollection to group and manage (Q)Actions.
authorManuel Nickschas <sputnick@quassel-irc.org>
Sat, 20 Sep 2008 00:21:45 +0000 (02:21 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sat, 20 Sep 2008 02:16:07 +0000 (04:16 +0200)
This is based on the corresponding KActionCollection (though it does only
implement a subset of that API).

src/uisupport/CMakeLists.txt
src/uisupport/actioncollection.cpp
src/uisupport/actioncollection.h

index 8ce0190..f60c8ca 100644 (file)
@@ -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
index c98c321..a20d918 100644 (file)
  *   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 <QAction>
+
+#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<QAction *> 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<QAction *>(sender());
+  if(action)
+    emit actionTriggered(action);
+}
+
+void ActionCollection::slotActionHovered() {
+  QAction *action = qobject_cast<QAction *>(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<QAction *>(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<QWidget *> 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;
+}
index d73b973..41bc044 100644 (file)
  *   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 <QList>
+#include <QMap>
+#include <QObject>
+#include <QString>
+
+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<QWidget *> 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<QAction *> 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<class ActionType>
+    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<QString, QAction *> _actionByName;
+    QList<QAction *> _actions;
+    QList<QWidget *> _associatedWidgets;
+
+    bool _connectHovered;
+    bool _connectTriggered;
+};
+
+int ActionCollection::count() const { return actions().count(); }
+bool ActionCollection::isEmpty() const { return actions().count(); }
+
+
+#endif