DataStreamPeer: Optimize the InitData message
[quassel.git] / src / common / eventmanager.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) 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.toAscii());
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.toAscii());
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         QString methodSignature(object->metaObject()->method(i).signature());
152
153         int eventType = findEventType(methodSignature, methodPrefix);
154         if (eventType > 0) {
155             Handler handler(object, i, priority);
156             registeredHandlers()[eventType].append(handler);
157             //qDebug() << "Registered event handler for" << methodSignature << "in" << object;
158         }
159         eventType = findEventType(methodSignature, filterPrefix);
160         if (eventType > 0) {
161             Handler handler(object, i, priority);
162             registeredFilters()[eventType].append(handler);
163             //qDebug() << "Registered event filterer for" << methodSignature << "in" << object;
164         }
165     }
166 }
167
168
169 void EventManager::registerEventFilter(EventType event, QObject *object, const char *slot)
170 {
171     registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
172 }
173
174
175 void EventManager::registerEventFilter(QList<EventType> events, QObject *object, const char *slot)
176 {
177     registerEventHandler(events, object, slot, NormalPriority, true);
178 }
179
180
181 void EventManager::registerEventHandler(EventType event, QObject *object, const char *slot, Priority priority, bool isFilter)
182 {
183     registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter);
184 }
185
186
187 void EventManager::registerEventHandler(QList<EventType> events, QObject *object, const char *slot, Priority priority, bool isFilter)
188 {
189     int methodIndex = object->metaObject()->indexOfMethod(slot);
190     if (methodIndex < 0) {
191         qWarning() << Q_FUNC_INFO << QString("Slot %1 not found in object %2").arg(slot).arg(object->objectName());
192         return;
193     }
194     Handler handler(object, methodIndex, priority);
195     foreach(EventType event, events) {
196         if (isFilter) {
197             registeredFilters()[event].append(handler);
198             qDebug() << "Registered event filter for" << event << "in" << object;
199         }
200         else {
201             registeredHandlers()[event].append(handler);
202             qDebug() << "Registered event handler for" << event << "in" << object;
203         }
204     }
205 }
206
207
208 void EventManager::postEvent(Event *event)
209 {
210     if (sender() && sender()->thread() != this->thread()) {
211         QueuedQuasselEvent *queuedEvent = new QueuedQuasselEvent(event);
212         QCoreApplication::postEvent(this, queuedEvent);
213     }
214     else {
215         if (_eventQueue.isEmpty())
216             // we're currently not processing events
217             processEvent(event);
218         else
219             _eventQueue.append(event);
220     }
221 }
222
223
224 void EventManager::customEvent(QEvent *event)
225 {
226     if (event->type() == QEvent::User) {
227         QueuedQuasselEvent *queuedEvent = static_cast<QueuedQuasselEvent *>(event);
228         processEvent(queuedEvent->event);
229         event->accept();
230     }
231 }
232
233
234 void EventManager::processEvent(Event *event)
235 {
236     Q_ASSERT(_eventQueue.isEmpty());
237     dispatchEvent(event);
238     // dispatching the event might cause new events to be generated. we process those afterwards.
239     while (!_eventQueue.isEmpty()) {
240         dispatchEvent(_eventQueue.first());
241         _eventQueue.removeFirst();
242     }
243 }
244
245
246 void EventManager::dispatchEvent(Event *event)
247 {
248     //qDebug() << "Dispatching" << event;
249
250     // we try handlers from specialized to generic by masking the enum
251
252     // build a list sorted by priorities that contains all eligible handlers
253     QList<Handler> handlers;
254     QHash<QObject *, Handler> filters;
255     QSet<QObject *> ignored;
256     uint type = event->type();
257
258     bool checkDupes = false;
259
260     // special handling for numeric IrcEvents
261     if ((type & ~IrcEventNumericMask) == IrcEventNumeric) {
262         ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event);
263         if (!numEvent)
264             qWarning() << "Invalid event type for IrcEventNumeric!";
265         else {
266             int num = numEvent->number();
267             if (num > 0) {
268                 insertHandlers(registeredHandlers().value(type + num), handlers, false);
269                 insertFilters(registeredFilters().value(type + num), filters);
270                 checkDupes = true;
271             }
272         }
273     }
274
275     // exact type
276     insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
277     insertFilters(registeredFilters().value(type), filters);
278
279     // check if we have a generic handler for the event group
280     if ((type & EventGroupMask) != type) {
281         insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers, true);
282         insertFilters(registeredFilters().value(type & EventGroupMask), filters);
283     }
284
285     // now dispatch the event
286     QList<Handler>::const_iterator it;
287     for (it = handlers.begin(); it != handlers.end() && !event->isStopped(); ++it) {
288         QObject *obj = it->object;
289
290         if (ignored.contains(obj)) // object has filtered the event
291             continue;
292
293         if (filters.contains(obj)) { // we have a filter, so let's check if we want to deliver the event
294             Handler filter = filters.value(obj);
295             bool result = false;
296             void *param[] = { Q_RETURN_ARG(bool, result).data(), Q_ARG(Event *, event).data() };
297             obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
298             if (!result) {
299                 ignored.insert(obj);
300                 continue; // mmmh, event filter told us to not accept
301             }
302         }
303
304         // finally, deliverance!
305         void *param[] = { 0, Q_ARG(Event *, event).data() };
306         obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
307     }
308
309     // that's it
310     delete event;
311 }
312
313
314 void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing, bool checkDupes)
315 {
316     foreach(const Handler &handler, newHandlers) {
317         if (existing.isEmpty())
318             existing.append(handler);
319         else {
320             // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
321             bool insert = true;
322             QList<Handler>::iterator insertpos = existing.end();
323             QList<Handler>::iterator it = existing.begin();
324             while (it != existing.end()) {
325                 if (checkDupes && handler.object == it->object) {
326                     insert = false;
327                     break;
328                 }
329                 if (insertpos == existing.end() && handler.priority > it->priority)
330                     insertpos = it;
331
332                 ++it;
333             }
334             if (insert)
335                 existing.insert(it, handler);
336         }
337     }
338 }
339
340
341 // priority is ignored, and only the first (should be most specialized) filter is being used
342 // fun things could happen if you used the registerEventFilter() methods in the wrong order though
343 void EventManager::insertFilters(const QList<Handler> &newFilters, QHash<QObject *, Handler> &existing)
344 {
345     foreach(const Handler &filter, newFilters) {
346         if (!existing.contains(filter.object))
347             existing[filter.object] = filter;
348     }
349 }
350
351
352 QMetaEnum EventManager::_enum;