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) version 3. * |
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 | |
21 | #include "eventmanager.h" |
22 | |
23 | #include <QCoreApplication> |
24 | #include <QEvent> |
25 | #include <QDebug> |
26 | |
27 | #include "event.h" |
28 | #include "ircevent.h" |
29 | |
30 | // ============================================================ |
31 | // QueuedEvent |
32 | // ============================================================ |
33 | class QueuedQuasselEvent : public QEvent |
34 | { |
35 | public: |
36 | QueuedQuasselEvent(Event *event) |
37 | : QEvent(QEvent::User), event(event) {} |
38 | Event *event; |
39 | }; |
40 | |
41 | |
42 | // ============================================================ |
43 | // EventManager |
44 | // ============================================================ |
45 | EventManager::EventManager(QObject *parent) |
46 | : QObject(parent) |
47 | { |
48 | } |
49 | |
50 | |
51 | QMetaEnum EventManager::eventEnum() |
52 | { |
53 | if (!_enum.isValid()) { |
54 | int eventEnumIndex = staticMetaObject.indexOfEnumerator("EventType" ); |
55 | Q_ASSERT(eventEnumIndex >= 0); |
56 | _enum = staticMetaObject.enumerator(eventEnumIndex); |
57 | Q_ASSERT(_enum.isValid()); |
58 | } |
59 | return _enum; |
60 | } |
61 | |
62 | |
63 | EventManager::EventType EventManager::eventTypeByName(const QString &name) |
64 | { |
65 | int val = eventEnum().keyToValue(name.toLatin1()); |
66 | return (val == -1) ? Invalid : static_cast<EventType>(val); |
67 | } |
68 | |
69 | |
70 | EventManager::EventType EventManager::eventGroupByName(const QString &name) |
71 | { |
72 | EventType type = eventTypeByName(name); |
73 | return type == Invalid ? Invalid : static_cast<EventType>(type & EventGroupMask); |
74 | } |
75 | |
76 | |
77 | QString EventManager::enumName(EventType type) |
78 | { |
79 | return eventEnum().valueToKey(type); |
80 | } |
81 | |
82 | |
83 | QString EventManager::enumName(int type) |
84 | { |
85 | return eventEnum().valueToKey(type); |
86 | } |
87 | |
88 | |
89 | Event *EventManager::createEvent(const QVariantMap &map) |
90 | { |
91 | QVariantMap m = map; |
92 | |
93 | Network *net = networkById(m.take("network" ).toInt()); |
94 | return Event::fromVariantMap(m, net); |
95 | } |
96 | |
97 | |
98 | /* NOTE: |
99 | Registering and calling handlers works fine even if they specify a subclass of Event as their parameter. |
100 | However, this most probably is a result from a reinterpret_cast somewhere deep inside Qt, so there is *no* |
101 | type safety. If the event sent is of the wrong class type, you'll get a neat segfault! |
102 | Thus, we need to make sure that events are of the correct class type when sending! |
103 | |
104 | We might add a registration-time check later, which will require matching the enum base name (e.g. "IrcEvent") with |
105 | the type the handler claims to support. This still won't protect us from someone sending an IrcEvent object |
106 | with an enum type "NetworkIncoming", for example. |
107 | |
108 | Another way would be to add a check into the various Event subclasses, such that the ctor matches the given event type |
109 | with the actual class. Possibly (optionally) using rtti... |
110 | */ |
111 | |
112 | int EventManager::findEventType(const QString &methodSignature_, const QString &methodPrefix) const |
113 | { |
114 | if (!methodSignature_.startsWith(methodPrefix)) |
115 | return -1; |
116 | |
117 | QString methodSignature = methodSignature_; |
118 | |
119 | methodSignature = methodSignature.section('(', 0, 0); // chop the attribute list |
120 | methodSignature = methodSignature.mid(methodPrefix.length()); // strip prefix |
121 | |
122 | int eventType = -1; |
123 | |
124 | // special handling for numeric IrcEvents: IrcEvent042 gets mapped to IrcEventNumeric + 42 |
125 | if (methodSignature.length() == 8+3 && methodSignature.startsWith("IrcEvent" )) { |
126 | int num = methodSignature.right(3).toUInt(); |
127 | if (num > 0) { |
128 | QString numericSig = methodSignature.left(methodSignature.length() - 3) + "Numeric" ; |
129 | eventType = eventEnum().keyToValue(numericSig.toLatin1()); |
130 | if (eventType < 0) { |
131 | qWarning() << Q_FUNC_INFO << "Could not find EventType" << numericSig << "for handling" << methodSignature; |
132 | return -1; |
133 | } |
134 | eventType += num; |
135 | } |
136 | } |
137 | |
138 | if (eventType < 0) |
139 | eventType = eventEnum().keyToValue(methodSignature.toLatin1()); |
140 | if (eventType < 0) { |
141 | qWarning() << Q_FUNC_INFO << "Could not find EventType" << methodSignature; |
142 | return -1; |
143 | } |
144 | return eventType; |
145 | } |
146 | |
147 | |
148 | void EventManager::registerObject(QObject *object, Priority priority, const QString &methodPrefix, const QString &filterPrefix) |
149 | { |
150 | for (int i = object->metaObject()->methodOffset(); i < object->metaObject()->methodCount(); i++) { |
151 | #if QT_VERSION >= 0x050000 |
152 | QString methodSignature = object->metaObject()->method(i).methodSignature(); |
153 | #else |
154 | QString methodSignature = object->metaObject()->method(i).signature(); |
155 | #endif |
156 | |
157 | int eventType = findEventType(methodSignature, methodPrefix); |
158 | if (eventType > 0) { |
159 | Handler handler(object, i, priority); |
160 | registeredHandlers()[eventType].append(handler); |
161 | //qDebug() << "Registered event handler for" << methodSignature << "in" << object; |
162 | } |
163 | eventType = findEventType(methodSignature, filterPrefix); |
164 | if (eventType > 0) { |
165 | Handler handler(object, i, priority); |
166 | registeredFilters()[eventType].append(handler); |
167 | //qDebug() << "Registered event filterer for" << methodSignature << "in" << object; |
168 | } |
169 | } |
170 | } |
171 | |
172 | |
173 | void EventManager::registerEventFilter(EventType event, QObject *object, const char *slot) |
174 | { |
175 | registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true); |
176 | } |
177 | |
178 | |
179 | void EventManager::registerEventFilter(QList<EventType> events, QObject *object, const char *slot) |
180 | { |
181 | registerEventHandler(events, object, slot, NormalPriority, true); |
182 | } |
183 | |
184 | |
185 | void EventManager::registerEventHandler(EventType event, QObject *object, const char *slot, Priority priority, bool isFilter) |
186 | { |
187 | registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter); |
188 | } |
189 | |
190 | |
191 | void EventManager::registerEventHandler(QList<EventType> events, QObject *object, const char *slot, Priority priority, bool isFilter) |
192 | { |
193 | int methodIndex = object->metaObject()->indexOfMethod(slot); |
194 | if (methodIndex < 0) { |
195 | qWarning() << Q_FUNC_INFO << QString("Slot %1 not found in object %2" ).arg(slot).arg(object->objectName()); |
196 | return; |
197 | } |
198 | Handler handler(object, methodIndex, priority); |
199 | foreach(EventType event, events) { |
200 | if (isFilter) { |
201 | registeredFilters()[event].append(handler); |
202 | qDebug() << "Registered event filter for" << event << "in" << object; |
203 | } |
204 | else { |
205 | registeredHandlers()[event].append(handler); |
206 | qDebug() << "Registered event handler for" << event << "in" << object; |
207 | } |
208 | } |
209 | } |
210 | |
211 | |
212 | void EventManager::postEvent(Event *event) |
213 | { |
214 | if (sender() && sender()->thread() != this->thread()) { |
215 | QueuedQuasselEvent *queuedEvent = new QueuedQuasselEvent(event); |
216 | QCoreApplication::postEvent(this, queuedEvent); |
217 | } |
218 | else { |
219 | if (_eventQueue.isEmpty()) |
220 | // we're currently not processing events |
221 | processEvent(event); |
222 | else |
223 | _eventQueue.append(event); |
224 | } |
225 | } |
226 | |
227 | |
228 | void EventManager::customEvent(QEvent *event) |
229 | { |
230 | if (event->type() == QEvent::User) { |
231 | QueuedQuasselEvent *queuedEvent = static_cast<QueuedQuasselEvent *>(event); |
232 | processEvent(queuedEvent->event); |
233 | event->accept(); |
234 | } |
235 | } |
236 | |
237 | |
238 | void EventManager::processEvent(Event *event) |
239 | { |
240 | Q_ASSERT(_eventQueue.isEmpty()); |
241 | dispatchEvent(event); |
242 | // dispatching the event might cause new events to be generated. we process those afterwards. |
243 | while (!_eventQueue.isEmpty()) { |
244 | dispatchEvent(_eventQueue.first()); |
245 | _eventQueue.removeFirst(); |
246 | } |
247 | } |
248 | |
249 | |
250 | void EventManager::dispatchEvent(Event *event) |
251 | { |
252 | //qDebug() << "Dispatching" << event; |
253 | |
254 | // we try handlers from specialized to generic by masking the enum |
255 | |
256 | // build a list sorted by priorities that contains all eligible handlers |
257 | QList<Handler> handlers; |
258 | QHash<QObject *, Handler> filters; |
259 | QSet<QObject *> ignored; |
260 | uint type = event->type(); |
261 | |
262 | bool checkDupes = false; |
263 | |
264 | // special handling for numeric IrcEvents |
265 | if ((type & ~IrcEventNumericMask) == IrcEventNumeric) { |
266 | ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event); |
267 | if (!numEvent) |
268 | qWarning() << "Invalid event type for IrcEventNumeric!" ; |
269 | else { |
270 | int num = numEvent->number(); |
271 | if (num > 0) { |
272 | insertHandlers(registeredHandlers().value(type + num), handlers, false); |
273 | insertFilters(registeredFilters().value(type + num), filters); |
274 | checkDupes = true; |
275 | } |
276 | } |
277 | } |
278 | |
279 | // exact type |
280 | insertHandlers(registeredHandlers().value(type), handlers, checkDupes); |
281 | insertFilters(registeredFilters().value(type), filters); |
282 | |
283 | // check if we have a generic handler for the event group |
284 | if ((type & EventGroupMask) != type) { |
285 | insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers, true); |
286 | insertFilters(registeredFilters().value(type & EventGroupMask), filters); |
287 | } |
288 | |
289 | // now dispatch the event |
290 | QList<Handler>::const_iterator it; |
291 | for (it = handlers.begin(); it != handlers.end() && !event->isStopped(); ++it) { |
292 | QObject *obj = it->object; |
293 | |
294 | if (ignored.contains(obj)) // object has filtered the event |
295 | continue; |
296 | |
297 | if (filters.contains(obj)) { // we have a filter, so let's check if we want to deliver the event |
298 | Handler filter = filters.value(obj); |
299 | bool result = false; |
300 | void *param[] = { Q_RETURN_ARG(bool, result).data(), Q_ARG(Event *, event).data() }; |
301 | obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param); |
302 | if (!result) { |
303 | ignored.insert(obj); |
304 | continue; // mmmh, event filter told us to not accept |
305 | } |
306 | } |
307 | |
308 | // finally, deliverance! |
309 | void *param[] = { 0, Q_ARG(Event *, event).data() }; |
310 | obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param); |
311 | } |
312 | |
313 | // that's it |
314 | delete event; |
315 | } |
316 | |
317 | |
318 | void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing, bool checkDupes) |
319 | { |
320 | foreach(const Handler &handler, newHandlers) { |
321 | if (existing.isEmpty()) |
322 | existing.append(handler); |
323 | else { |
324 | // need to insert it at the proper position, but only if we don't yet have a handler for this event and object! |
325 | bool insert = true; |
326 | QList<Handler>::iterator insertpos = existing.end(); |
327 | QList<Handler>::iterator it = existing.begin(); |
328 | while (it != existing.end()) { |
329 | if (checkDupes && handler.object == it->object) { |
330 | insert = false; |
331 | break; |
332 | } |
333 | if (insertpos == existing.end() && handler.priority > it->priority) |
334 | insertpos = it; |
335 | |
336 | ++it; |
337 | } |
338 | if (insert) |
339 | existing.insert(it, handler); |
340 | } |
341 | } |
342 | } |
343 | |
344 | |
345 | // priority is ignored, and only the first (should be most specialized) filter is being used |
346 | // fun things could happen if you used the registerEventFilter() methods in the wrong order though |
347 | void EventManager::insertFilters(const QList<Handler> &newFilters, QHash<QObject *, Handler> &existing) |
348 | { |
349 | foreach(const Handler &filter, newFilters) { |
350 | if (!existing.contains(filter.object)) |
351 | existing[filter.object] = filter; |
352 | } |
353 | } |
354 | |
355 | |
356 | QMetaEnum EventManager::_enum; |
357 | |