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