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