Save Main ToolBar state when not built against KDE. Fixes #1116
[quassel.git] / src / uisupport / actioncollection.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2014 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
206 void ActionCollection::connectNotify(const char *signal)
207 {
208     if (_connectHovered && _connectTriggered)
209         return;
210
211     if (QMetaObject::normalizedSignature(SIGNAL(actionHovered(QAction *))) == signal) {
212         if (!_connectHovered) {
213             _connectHovered = true;
214             foreach(QAction* action, actions())
215             connect(action, SIGNAL(hovered()), SLOT(slotActionHovered()));
216         }
217     }
218     else if (QMetaObject::normalizedSignature(SIGNAL(actionTriggered(QAction *))) == 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 */