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