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