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