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