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 | #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 */ |
309 | |