Bulk-insert messages into the db (again)
[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   else
184     emit eventQueueEmptied();
185 }
186
187 void EventManager::dispatchEvent(Event *event) {
188   //qDebug() << "Dispatching" << event;
189
190   // we try handlers from specialized to generic by masking the enum
191
192   // build a list sorted by priorities that contains all eligible handlers
193   QList<Handler> handlers;
194   QHash<QObject *, Handler> filters;
195   QSet<QObject *> ignored;
196   uint type = event->type();
197
198   bool checkDupes = false;
199
200   // special handling for numeric IrcEvents
201   if((type & ~IrcEventNumericMask) == IrcEventNumeric) {
202     ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event);
203     if(!numEvent)
204       qWarning() << "Invalid event type for IrcEventNumeric!";
205     else {
206       int num = numEvent->number();
207       if(num > 0) {
208         insertHandlers(registeredHandlers().value(type + num), handlers, false);
209         insertFilters(registeredFilters().value(type + num), filters);
210         checkDupes = true;
211       }
212     }
213   }
214
215   // exact type
216   insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
217   insertFilters(registeredFilters().value(type), filters);
218
219   // check if we have a generic handler for the event group
220   if((type & EventGroupMask) != type) {
221     insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers, true);
222     insertFilters(registeredFilters().value(type & EventGroupMask), filters);
223   }
224
225   // now dispatch the event
226   QList<Handler>::const_iterator it;
227   for(it = handlers.begin(); it != handlers.end() && !event->isStopped(); ++it) {
228     QObject *obj = it->object;
229
230     if(ignored.contains(obj)) // object has filtered the event
231       continue;
232
233     if(filters.contains(obj)) { // we have a filter, so let's check if we want to deliver the event
234       Handler filter = filters.value(obj);
235       bool result = false;
236       void *param[] = {Q_RETURN_ARG(bool, result).data(), Q_ARG(Event *, event).data() };
237       obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
238       if(!result) {
239         ignored.insert(obj);
240         continue; // mmmh, event filter told us to not accept
241       }
242     }
243
244     // finally, deliverance!
245     void *param[] = {0, Q_ARG(Event *, event).data() };
246     obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
247   }
248
249   // that's it
250   delete event;
251 }
252
253 void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing, bool checkDupes) {
254   foreach(const Handler &handler, newHandlers) {
255     if(existing.isEmpty())
256       existing.append(handler);
257     else {
258       // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
259       bool insert = true;
260       QList<Handler>::iterator insertpos = existing.end();
261       QList<Handler>::iterator it = existing.begin();
262       while(it != existing.end()) {
263         if(checkDupes && handler.object == it->object) {
264           insert = false;
265           break;
266         }
267         if(insertpos == existing.end() && handler.priority > it->priority)
268           insertpos = it;
269
270         ++it;
271       }
272       if(insert)
273         existing.insert(it, handler);
274     }
275   }
276 }
277
278 // priority is ignored, and only the first (should be most specialized) filter is being used
279 // fun things could happen if you used the registerEventFilter() methods in the wrong order though
280 void EventManager::insertFilters(const QList<Handler> &newFilters, QHash<QObject *, Handler> &existing) {
281   foreach(const Handler &filter, newFilters) {
282     if(!existing.contains(filter.object))
283       existing[filter.object] = filter;
284   }
285 }