Add ActionCollection to group and manage (Q)Actions.
[quassel.git] / src / uisupport / actioncollection.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel Project                          *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************
20  * Parts of this implementation are based on KDE's KActionCollection.      *
21  ***************************************************************************/
22
23 #include <QAction>
24
25 #include "actioncollection.h"
26
27 #include "action.h"
28
29 ActionCollection::ActionCollection(QObject *parent) : QObject(parent) {
30   _connectTriggered = _connectHovered = false;
31 }
32
33 ActionCollection::~ActionCollection() {
34
35 }
36
37 void ActionCollection::clear() {
38   _actionByName.clear();
39   qDeleteAll(_actions);
40   _actions.clear();
41 }
42
43 QAction *ActionCollection::action(const QString &name) const {
44   return _actionByName.value(name, 0);
45 }
46
47 QList<QAction *> ActionCollection::actions() const {
48   return _actions;
49 }
50
51 Action *ActionCollection::addAction(const QString &name, Action *action) {
52   QAction *act = addAction(name, action);
53   Q_ASSERT(act == action);
54   return action;
55 }
56
57 Action *ActionCollection::addAction(const QString &name, const QObject *receiver, const char *member) {
58   Action *a = new Action(this);
59   if(receiver && member)
60     connect(a, SIGNAL(triggered(bool)), receiver, member);
61   return addAction(name, a);
62 }
63
64 QAction *ActionCollection::addAction(const QString &name, QAction *action) {
65   if(!action)
66     return action;
67
68   const QString origName = action->objectName();
69   QString indexName = name;
70
71   if(indexName.isEmpty())
72     indexName = action->objectName();
73   else
74     action->setObjectName(indexName);
75   if(indexName.isEmpty())
76     indexName = QString("unnamed-%1").arg((int)action, 16);
77
78   // do we already have this action?
79   if(_actionByName.value(indexName, 0) == action)
80     return action;
81   // or maybe another action under this name?
82   if(QAction *oldAction = _actionByName.value(indexName))
83     takeAction(oldAction);
84
85   // do we already have this action under a different name?
86   int oldIndex = _actions.indexOf(action);
87   if(oldIndex != -1) {
88     _actionByName.remove(origName);
89     _actions.removeAt(oldIndex);
90   }
91
92   // add action
93   _actionByName.insert(indexName, action);
94   _actions.append(action);
95
96   foreach(QWidget *widget, _associatedWidgets) {
97     widget->addAction(action);
98   }
99
100   connect(action, SIGNAL(destroyed(QObject *)), SLOT(actionDestroyed(QObject *)));
101   if(_connectHovered)
102     connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
103   if(_connectTriggered)
104     connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
105
106   emit inserted(action);
107   return action;
108 }
109
110 void ActionCollection::removeAction(QAction *action) {
111   delete takeAction(action);
112 }
113
114 QAction *ActionCollection::takeAction(QAction *action) {
115   if(!unlistAction(action))
116     return 0;
117
118   foreach(QWidget *widget, _associatedWidgets) {
119     widget->removeAction(action);
120   }
121
122   action->disconnect(this);
123   return action;
124 }
125
126 void ActionCollection::readSettings() {
127
128 }
129
130 void ActionCollection::writeSettings() const {
131
132
133 }
134
135 void ActionCollection::slotActionTriggered() {
136   QAction *action = qobject_cast<QAction *>(sender());
137   if(action)
138     emit actionTriggered(action);
139 }
140
141 void ActionCollection::slotActionHovered() {
142   QAction *action = qobject_cast<QAction *>(sender());
143   if(action)
144     emit actionHovered(action);
145 }
146
147 void ActionCollection::actionDestroyed(QObject *obj) {
148   // remember that this is not an QAction anymore at this point
149   QAction *action = static_cast<QAction *>(obj);
150
151   unlistAction(action);
152 }
153
154 void ActionCollection::connectNotify(const char *signal) {
155   if(_connectHovered && _connectTriggered)
156     return;
157
158   if(QMetaObject::normalizedSignature(SIGNAL(actionHovered(QAction*))) == signal) {
159     if(!_connectHovered) {
160       _connectHovered = true;
161       foreach (QAction* action, actions())
162         connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
163     }
164   } else if(QMetaObject::normalizedSignature(SIGNAL(actionTriggered(QAction*))) == signal) {
165     if(!_connectTriggered) {
166       _connectTriggered = true;
167       foreach (QAction* action, actions())
168         connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
169     }
170   }
171
172   QObject::connectNotify(signal);
173 }
174
175 void ActionCollection::associateWidget(QWidget *widget) const {
176   foreach(QAction *action, actions()) {
177     if(!widget->actions().contains(action))
178       widget->addAction(action);
179   }
180 }
181
182 void ActionCollection::addAssociatedWidget(QWidget *widget) {
183   if(!_associatedWidgets.contains(widget)) {
184     widget->addActions(actions());
185     _associatedWidgets.append(widget);
186     connect(widget, SIGNAL(destroyed(QObject *)), SLOT(associatedWidgetDestroyed(QObject *)));
187   }
188 }
189
190 void ActionCollection::removeAssociatedWidget(QWidget *widget) {
191   foreach(QAction *action, actions())
192     widget->removeAction(action);
193   _associatedWidgets.removeAll(widget);
194   disconnect(widget, SIGNAL(destroyed(QObject *)), this, SLOT(associatedWidgetDestroyed(QObject *)));
195 }
196
197 QList<QWidget *> ActionCollection::associatedWidgets() const {
198   return _associatedWidgets;
199 }
200
201 void ActionCollection::clearAssociatedWidgets() {
202   foreach(QWidget *widget, _associatedWidgets)
203     foreach(QAction *action, actions())
204       widget->removeAction(action);
205
206   _associatedWidgets.clear();
207 }
208
209 bool ActionCollection::unlistAction(QAction *action) {
210   // This might be called with a partly destroyed QAction!
211
212   int index = _actions.indexOf(action);
213   if(index == -1) return false;
214
215   QString name = action->objectName();
216   _actionByName.remove(name);
217   _actions.removeAt(index);
218
219   // TODO: remove from ActionCategory if we ever get that
220
221   return true;
222 }