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// ============================================================
33class QueuedQuasselEvent : public QEvent
34{
35public:
36 QueuedQuasselEvent(Event *event)
37 : QEvent(QEvent::User), event(event) {}
38 Event *event;
39};
40
41
42// ============================================================
43// EventManager
44// ============================================================
45EventManager::EventManager(QObject *parent)
46 : QObject(parent)
47{
48}
49
50
51QMetaEnum 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
63EventManager::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
70EventManager::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
77QString EventManager::enumName(EventType type)
78{
79 return eventEnum().valueToKey(type);
80}
81
82
83QString EventManager::enumName(int type)
84{
85 return eventEnum().valueToKey(type);
86}
87
88
89Event *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
112int 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
148void 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
173void EventManager::registerEventFilter(EventType event, QObject *object, const char *slot)
174{
175 registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
176}
177
178
179void EventManager::registerEventFilter(QList<EventType> events, QObject *object, const char *slot)
180{
181 registerEventHandler(events, object, slot, NormalPriority, true);
182}
183
184
185void 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
191void 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
212void 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
228void 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
238void 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
250void 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
318void 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
347void 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
356QMetaEnum EventManager::_enum;
357