Set current time on creation of MessageEvents if no other time is given
[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   // special handling for numeric IrcEvents
197   if((type & ~IrcEventNumericMask) == IrcEventNumeric) {
198     ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event);
199     if(!numEvent)
200       qWarning() << "Invalid event type for IrcEventNumeric!";
201     else {
202       int num = numEvent->number();
203       if(num > 0) {
204         insertHandlers(registeredHandlers().value(type + num), handlers);
205         insertFilters(registeredFilters().value(type + num), filters);
206       }
207     }
208   }
209
210   // exact type
211   insertHandlers(registeredHandlers().value(type), handlers);
212   insertFilters(registeredFilters().value(type), filters);
213
214   // check if we have a generic handler for the event group
215   if((type & EventGroupMask) != type) {
216     insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers);
217     insertFilters(registeredFilters().value(type & EventGroupMask), filters);
218   }
219
220   // now dispatch the event
221   QList<Handler>::const_iterator it;
222   for(it = handlers.begin(); it != handlers.end() && !event->isStopped(); ++it) {
223     QObject *obj = it->object;
224
225     if(ignored.contains(obj)) // we only deliver an event once to any given object
226       continue;
227
228     ignored.insert(obj);
229
230     if(filters.contains(obj)) { // we have a filter, so let's check if we want to deliver the event
231       Handler filter = filters.value(obj);
232       bool result = false;
233       void *param[] = {Q_RETURN_ARG(bool, result).data(), Q_ARG(Event *, event).data() };
234       obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
235       if(!result)
236         continue; // mmmh, event filter told us to not accept
237     }
238
239     // finally, deliverance!
240     void *param[] = {0, Q_ARG(Event *, event).data() };
241     obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
242   }
243
244   // that's it
245   delete event;
246 }
247
248 void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing) {
249   foreach(const Handler &handler, newHandlers) {
250     if(existing.isEmpty())
251       existing.append(handler);
252     else {
253       // need to insert it at the proper position
254       QList<Handler>::iterator it = existing.begin();
255       while(it != existing.end()) {
256         if(handler.priority > it->priority)
257           break;
258         ++it;
259       }
260       existing.insert(it, handler);
261     }
262   }
263 }
264
265 // priority is ignored, and only the first (should be most specialized) filter is being used
266 // fun things could happen if you used the registerEventFilter() methods in the wrong order though
267 void EventManager::insertFilters(const QList<Handler> &newFilters, QHash<QObject *, Handler> &existing) {
268   foreach(const Handler &filter, newFilters) {
269     if(!existing.contains(filter.object))
270       existing[filter.object] = filter;
271   }
272 }