593b236e56896f70200040099316e3a5150e61ce
[quassel.git] / src / uisupport / actioncollection.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2015 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  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 #include "uisettings.h"
32
33 ActionCollection::ActionCollection(QObject *parent) : QObject(parent)
34 {
35     _connectTriggered = _connectHovered = false;
36 }
37
38
39 ActionCollection::~ActionCollection()
40 {
41 }
42
43
44 void ActionCollection::clear()
45 {
46     _actionByName.clear();
47     qDeleteAll(_actions);
48     _actions.clear();
49 }
50
51
52 QAction *ActionCollection::action(const QString &name) const
53 {
54     return _actionByName.value(name, 0);
55 }
56
57
58 QList<QAction *> ActionCollection::actions() const
59 {
60     return _actions;
61 }
62
63
64 Action *ActionCollection::addAction(const QString &name, Action *action)
65 {
66     QAction *act = addAction(name, static_cast<QAction *>(action));
67     Q_UNUSED(act);
68     Q_ASSERT(act == action);
69     return action;
70 }
71
72
73 Action *ActionCollection::addAction(const QString &name, const QObject *receiver, const char *member)
74 {
75     Action *a = new Action(this);
76     if (receiver && member)
77         connect(a, SIGNAL(triggered(bool)), receiver, member);
78     return addAction(name, a);
79 }
80
81
82 QAction *ActionCollection::addAction(const QString &name, QAction *action)
83 {
84     if (!action)
85         return action;
86
87     const QString origName = action->objectName();
88     QString indexName = name;
89
90     if (indexName.isEmpty())
91         indexName = action->objectName();
92     else
93         action->setObjectName(indexName);
94     if (indexName.isEmpty())
95         indexName = indexName.sprintf("unnamed-%p", (void *)action);
96
97     // do we already have this action?
98     if (_actionByName.value(indexName, 0) == action)
99         return action;
100     // or maybe another action under this name?
101     if (QAction *oldAction = _actionByName.value(indexName))
102         takeAction(oldAction);
103
104     // do we already have this action under a different name?
105     int oldIndex = _actions.indexOf(action);
106     if (oldIndex != -1) {
107         _actionByName.remove(origName);
108         _actions.removeAt(oldIndex);
109     }
110
111     // add action
112     _actionByName.insert(indexName, action);
113     _actions.append(action);
114
115     foreach(QWidget *widget, _associatedWidgets) {
116         widget->addAction(action);
117     }
118
119     connect(action, SIGNAL(destroyed(QObject *)), SLOT(actionDestroyed(QObject *)));
120     if (_connectHovered)
121         connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
122     if (_connectTriggered)
123         connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
124
125     emit inserted(action);
126     return action;
127 }
128
129
130 void ActionCollection::removeAction(QAction *action)
131 {
132     delete takeAction(action);
133 }
134
135
136 QAction *ActionCollection::takeAction(QAction *action)
137 {
138     if (!unlistAction(action))
139         return 0;
140
141     foreach(QWidget *widget, _associatedWidgets) {
142         widget->removeAction(action);
143     }
144
145     action->disconnect(this);
146     return action;
147 }
148
149
150 void ActionCollection::readSettings()
151 {
152     ShortcutSettings s;
153     QStringList savedShortcuts = s.savedShortcuts();
154
155     foreach(const QString &name, _actionByName.keys()) {
156         if (!savedShortcuts.contains(name))
157             continue;
158         Action *action = qobject_cast<Action *>(_actionByName.value(name));
159         if (action)
160             action->setShortcut(s.loadShortcut(name), Action::ActiveShortcut);
161     }
162 }
163
164
165 void ActionCollection::writeSettings() const
166 {
167     ShortcutSettings s;
168     foreach(const QString &name, _actionByName.keys()) {
169         Action *action = qobject_cast<Action *>(_actionByName.value(name));
170         if (!action)
171             continue;
172         if (!action->isShortcutConfigurable())
173             continue;
174         if (action->shortcut(Action::ActiveShortcut) == action->shortcut(Action::DefaultShortcut))
175             continue;
176         s.saveShortcut(name, action->shortcut(Action::ActiveShortcut));
177     }
178 }
179
180
181 void ActionCollection::slotActionTriggered()
182 {
183     QAction *action = qobject_cast<QAction *>(sender());
184     if (action)
185         emit actionTriggered(action);
186 }
187
188
189 void ActionCollection::slotActionHovered()
190 {
191     QAction *action = qobject_cast<QAction *>(sender());
192     if (action)
193         emit actionHovered(action);
194 }
195
196
197 void ActionCollection::actionDestroyed(QObject *obj)
198 {
199     // remember that this is not an QAction anymore at this point
200     QAction *action = static_cast<QAction *>(obj);
201
202     unlistAction(action);
203 }
204
205 #if QT_VERSION >= 0x050000
206 void ActionCollection::connectNotify(const QMetaMethod &signal)
207 #else
208 void ActionCollection::connectNotify(const char *signal)
209 #endif
210 {
211     if (_connectHovered && _connectTriggered)
212         return;
213
214 #if QT_VERSION >= 0x050000
215     if (QMetaMethod::fromSignal(&ActionCollection::actionHovered) == signal) {
216 #else
217     if (QMetaObject::normalizedSignature(SIGNAL(actionHovered(QAction *))) == signal) {
218 #endif
219         if (!_connectHovered) {
220             _connectHovered = true;
221             foreach(QAction* action, actions())
222             connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
223         }
224     }
225 #if QT_VERSION >= 0x050000
226     else if (QMetaMethod::fromSignal(&ActionCollection::actionTriggered) == signal) {
227 #else
228     else if (QMetaObject::normalizedSignature(SIGNAL(actionTriggered(QAction *))) == signal) {
229 #endif
230         if (!_connectTriggered) {
231             _connectTriggered = true;
232             foreach(QAction* action, actions())
233             connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered()));
234         }
235     }
236
237     QObject::connectNotify(signal);
238 }
239
240
241 void ActionCollection::associateWidget(QWidget *widget) const
242 {
243     foreach(QAction *action, actions()) {
244         if (!widget->actions().contains(action))
245             widget->addAction(action);
246     }
247 }
248
249
250 void ActionCollection::addAssociatedWidget(QWidget *widget)
251 {
252     if (!_associatedWidgets.contains(widget)) {
253         widget->addActions(actions());
254         _associatedWidgets.append(widget);
255         connect(widget, SIGNAL(destroyed(QObject *)), SLOT(associatedWidgetDestroyed(QObject *)));
256     }
257 }
258
259
260 void ActionCollection::removeAssociatedWidget(QWidget *widget)
261 {
262     foreach(QAction *action, actions())
263     widget->removeAction(action);
264     _associatedWidgets.removeAll(widget);
265     disconnect(widget, SIGNAL(destroyed(QObject *)), this, SLOT(associatedWidgetDestroyed(QObject *)));
266 }
267
268
269 QList<QWidget *> ActionCollection::associatedWidgets() const
270 {
271     return _associatedWidgets;
272 }
273
274
275 void ActionCollection::clearAssociatedWidgets()
276 {
277     foreach(QWidget *widget, _associatedWidgets)
278     foreach(QAction *action, actions())
279     widget->removeAction(action);
280
281     _associatedWidgets.clear();
282 }
283
284
285 void ActionCollection::associatedWidgetDestroyed(QObject *obj)
286 {
287     _associatedWidgets.removeAll(static_cast<QWidget *>(obj));
288 }
289
290
291 bool ActionCollection::unlistAction(QAction *action)
292 {
293     // This might be called with a partly destroyed QAction!
294
295     int index = _actions.indexOf(action);
296     if (index == -1) return false;
297
298     QString name = action->objectName();
299     _actionByName.remove(name);
300     _actions.removeAt(index);
301
302     // TODO: remove from ActionCategory if we ever get that
303
304     return true;
305 }
306
307
308 #endif /* HAVE_KDE */