Don't crash on Windows (again)
[quassel.git] / src / uisupport / actioncollection.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-09 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 #ifndef HAVE_KDE
24
25 #include <QAction>
26 #include <QDebug>
27
28 #include "actioncollection.h"
29
30 #include "action.h"
31
32 ActionCollection::ActionCollection(QObject *parent) : QObject(parent) {
33   _connectTriggered = _connectHovered = false;
34 }
35
36 ActionCollection::~ActionCollection() {
37
38 }
39
40 void ActionCollection::clear() {
41   _actionByName.clear();
42   qDeleteAll(_actions);
43   _actions.clear();
44 }
45
46 QAction *ActionCollection::action(const QString &name) const {
47   return _actionByName.value(name, 0);
48 }
49
50 QList<QAction *> ActionCollection::actions() const {
51   return _actions;
52 }
53
54 Action *ActionCollection::addAction(const QString &name, Action *action) {
55   QAction *act = addAction(name, static_cast<QAction *>(action));
56   Q_UNUSED(act);
57   Q_ASSERT(act == action);
58   return action;
59 }
60
61 Action *ActionCollection::addAction(const QString &name, const QObject *receiver, const char *member) {
62   Action *a = new Action(this);
63   if(receiver && member)
64     connect(a, SIGNAL(triggered(bool)), receiver, member);
65   return addAction(name, a);
66 }
67
68 QAction *ActionCollection::addAction(const QString &name, QAction *action) {
69   if(!action)
70     return action;
71
72   const QString origName = action->objectName();
73   QString indexName = name;
74
75   if(indexName.isEmpty())
76     indexName = action->objectName();
77   else
78     action->setObjectName(indexName);
79   if(indexName.isEmpty())
80     indexName = indexName.sprintf("unnamed-%p", (void *)action);
81
82   // do we already have this action?
83   if(_actionByName.value(indexName, 0) == action)
84     return action;
85   // or maybe another action under this name?
86   if(QAction *oldAction = _actionByName.value(indexName))
87     takeAction(oldAction);
88
89   // do we already have this action under a different name?
90   int oldIndex = _actions.indexOf(action);
91   if(oldIndex != -1) {
92     _actionByName.remove(origName);
93     _actions.removeAt(oldIndex);
94   }
95
96   // add action
97   _actionByName.insert(indexName, action);
98   _actions.append(action);
99
100   foreach(QWidget *widget, _associatedWidgets) {
101     widget->addAction(action);
102   }
103
104   connect(action, SIGNAL(destroyed(QObject *)), SLOT(actionDestroyed(QObject *)));
105   if(_connectHovered)
106     connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
107   if(_connectTriggered)
108     connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
109
110   emit inserted(action);
111   return action;
112 }
113
114 void ActionCollection::removeAction(QAction *action) {
115   delete takeAction(action);
116 }
117
118 QAction *ActionCollection::takeAction(QAction *action) {
119   if(!unlistAction(action))
120     return 0;
121
122   foreach(QWidget *widget, _associatedWidgets) {
123     widget->removeAction(action);
124   }
125
126   action->disconnect(this);
127   return action;
128 }
129
130 void ActionCollection::readSettings() {
131
132 }
133
134 void ActionCollection::writeSettings() const {
135
136
137 }
138
139 void ActionCollection::slotActionTriggered() {
140   QAction *action = qobject_cast<QAction *>(sender());
141   if(action)
142     emit actionTriggered(action);
143 }
144
145 void ActionCollection::slotActionHovered() {
146   QAction *action = qobject_cast<QAction *>(sender());
147   if(action)
148     emit actionHovered(action);
149 }
150
151 void ActionCollection::actionDestroyed(QObject *obj) {
152   // remember that this is not an QAction anymore at this point
153   QAction *action = static_cast<QAction *>(obj);
154
155   unlistAction(action);
156 }
157
158 void ActionCollection::connectNotify(const char *signal) {
159   if(_connectHovered && _connectTriggered)
160     return;
161
162   if(QMetaObject::normalizedSignature(SIGNAL(actionHovered(QAction*))) == signal) {
163     if(!_connectHovered) {
164       _connectHovered = true;
165       foreach (QAction* action, actions())
166         connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
167     }
168   } else if(QMetaObject::normalizedSignature(SIGNAL(actionTriggered(QAction*))) == signal) {
169     if(!_connectTriggered) {
170       _connectTriggered = true;
171       foreach (QAction* action, actions())
172         connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
173     }
174   }
175
176   QObject::connectNotify(signal);
177 }
178
179 void ActionCollection::associateWidget(QWidget *widget) const {
180   foreach(QAction *action, actions()) {
181     if(!widget->actions().contains(action))
182       widget->addAction(action);
183   }
184 }
185
186 void ActionCollection::addAssociatedWidget(QWidget *widget) {
187   if(!_associatedWidgets.contains(widget)) {
188     widget->addActions(actions());
189     _associatedWidgets.append(widget);
190     connect(widget, SIGNAL(destroyed(QObject *)), SLOT(associatedWidgetDestroyed(QObject *)));
191   }
192 }
193
194 void ActionCollection::removeAssociatedWidget(QWidget *widget) {
195   foreach(QAction *action, actions())
196     widget->removeAction(action);
197   _associatedWidgets.removeAll(widget);
198   disconnect(widget, SIGNAL(destroyed(QObject *)), this, SLOT(associatedWidgetDestroyed(QObject *)));
199 }
200
201 QList<QWidget *> ActionCollection::associatedWidgets() const {
202   return _associatedWidgets;
203 }
204
205 void ActionCollection::clearAssociatedWidgets() {
206   foreach(QWidget *widget, _associatedWidgets)
207     foreach(QAction *action, actions())
208       widget->removeAction(action);
209
210   _associatedWidgets.clear();
211 }
212
213 void ActionCollection::associatedWidgetDestroyed(QObject *obj) {
214   _associatedWidgets.removeAll(static_cast<QWidget *>(obj));
215 }
216
217 bool ActionCollection::unlistAction(QAction *action) {
218   // This might be called with a partly destroyed QAction!
219
220   int index = _actions.indexOf(action);
221   if(index == -1) return false;
222
223   QString name = action->objectName();
224   _actionByName.remove(name);
225   _actions.removeAt(index);
226
227   // TODO: remove from ActionCategory if we ever get that
228
229   return true;
230 }
231
232 #endif /* HAVE_KDE */