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