7a4bde2c2cbf032ebf4dab0c28986227ceba74c0
[quassel.git] / src / common / eventmanager.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2016 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;