1 /***************************************************************************
2 * Copyright (C) 2005-2012 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include "eventmanager.h"
23 #include <QCoreApplication>
30 // ============================================================
32 // ============================================================
33 class QueuedQuasselEvent : public QEvent {
35 QueuedQuasselEvent(Event *event)
36 : QEvent(QEvent::User), event(event) {}
40 // ============================================================
42 // ============================================================
43 EventManager::EventManager(QObject *parent)
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());
57 EventManager::EventType EventManager::eventTypeByName(const QString &name) {
58 int val = eventEnum().keyToValue(name.toLatin1());
59 return (val == -1) ? Invalid : static_cast<EventType>(val);
62 EventManager::EventType EventManager::eventGroupByName(const QString &name) {
63 EventType type = eventTypeByName(name);
64 return type == Invalid? Invalid : static_cast<EventType>(type & EventGroupMask);
67 QString EventManager::enumName(EventType type) {
68 return eventEnum().valueToKey(type);
71 QString EventManager::enumName(int type) {
72 return eventEnum().valueToKey(type);
75 Event *EventManager::createEvent(const QVariantMap &map) {
78 Network *net = networkById(m.take("network").toInt());
79 return Event::fromVariantMap(m, net);
83 Registering and calling handlers works fine even if they specify a subclass of Event as their parameter.
84 However, this most probably is a result from a reinterpret_cast somewhere deep inside Qt, so there is *no*
85 type safety. If the event sent is of the wrong class type, you'll get a neat segfault!
86 Thus, we need to make sure that events are of the correct class type when sending!
88 We might add a registration-time check later, which will require matching the enum base name (e.g. "IrcEvent") with
89 the type the handler claims to support. This still won't protect us from someone sending an IrcEvent object
90 with an enum type "NetworkIncoming", for example.
92 Another way would be to add a check into the various Event subclasses, such that the ctor matches the given event type
93 with the actual class. Possibly (optionally) using rtti...
96 int EventManager::findEventType(const QString &methodSignature_, const QString &methodPrefix) const {
97 if(!methodSignature_.startsWith(methodPrefix))
100 QString methodSignature = methodSignature_;
102 methodSignature = methodSignature.section('(',0,0); // chop the attribute list
103 methodSignature = methodSignature.mid(methodPrefix.length()); // strip prefix
107 // special handling for numeric IrcEvents: IrcEvent042 gets mapped to IrcEventNumeric + 42
108 if(methodSignature.length() == 8+3 && methodSignature.startsWith("IrcEvent")) {
109 int num = methodSignature.right(3).toUInt();
111 QString numericSig = methodSignature.left(methodSignature.length() - 3) + "Numeric";
112 eventType = eventEnum().keyToValue(numericSig.toAscii());
114 qWarning() << Q_FUNC_INFO << "Could not find EventType" << numericSig << "for handling" << methodSignature;
122 eventType = eventEnum().keyToValue(methodSignature.toAscii());
124 qWarning() << Q_FUNC_INFO << "Could not find EventType" << methodSignature;
130 void EventManager::registerObject(QObject *object, Priority priority, const QString &methodPrefix, const QString &filterPrefix) {
131 for(int i = object->metaObject()->methodOffset(); i < object->metaObject()->methodCount(); i++) {
132 QString methodSignature(object->metaObject()->method(i).signature());
134 int eventType = findEventType(methodSignature, methodPrefix);
136 Handler handler(object, i, priority);
137 registeredHandlers()[eventType].append(handler);
138 //qDebug() << "Registered event handler for" << methodSignature << "in" << object;
140 eventType = findEventType(methodSignature, filterPrefix);
142 Handler handler(object, i, priority);
143 registeredFilters()[eventType].append(handler);
144 //qDebug() << "Registered event filterer for" << methodSignature << "in" << object;
149 void EventManager::registerEventFilter(EventType event, QObject *object, const char *slot) {
150 registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
153 void EventManager::registerEventFilter(QList<EventType> events, QObject *object, const char *slot) {
154 registerEventHandler(events, object, slot, NormalPriority, true);
157 void EventManager::registerEventHandler(EventType event, QObject *object, const char *slot, Priority priority, bool isFilter) {
158 registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter);
161 void EventManager::registerEventHandler(QList<EventType> events, QObject *object, const char *slot, Priority priority, bool isFilter) {
162 int methodIndex = object->metaObject()->indexOfMethod(slot);
163 if(methodIndex < 0) {
164 qWarning() << Q_FUNC_INFO << QString("Slot %1 not found in object %2").arg(slot).arg(object->objectName());
167 Handler handler(object, methodIndex, priority);
168 foreach(EventType event, events) {
170 registeredFilters()[event].append(handler);
171 qDebug() << "Registered event filter for" << event << "in" << object;
173 registeredHandlers()[event].append(handler);
174 qDebug() << "Registered event handler for" << event << "in" << object;
179 void EventManager::postEvent(Event *event) {
180 if(sender() && sender()->thread() != this->thread()) {
181 QueuedQuasselEvent *queuedEvent = new QueuedQuasselEvent(event);
182 QCoreApplication::postEvent(this, queuedEvent);
184 if(_eventQueue.isEmpty())
185 // we're currently not processing events
188 _eventQueue.append(event);
192 void EventManager::customEvent(QEvent *event) {
193 if(event->type() == QEvent::User) {
194 QueuedQuasselEvent *queuedEvent = static_cast<QueuedQuasselEvent *>(event);
195 processEvent(queuedEvent->event);
200 void EventManager::processEvent(Event *event) {
201 Q_ASSERT(_eventQueue.isEmpty());
202 dispatchEvent(event);
203 // dispatching the event might cause new events to be generated. we process those afterwards.
204 while(!_eventQueue.isEmpty()) {
205 dispatchEvent(_eventQueue.first());
206 _eventQueue.removeFirst();
210 void EventManager::dispatchEvent(Event *event) {
211 //qDebug() << "Dispatching" << event;
213 // we try handlers from specialized to generic by masking the enum
215 // build a list sorted by priorities that contains all eligible handlers
216 QList<Handler> handlers;
217 QHash<QObject *, Handler> filters;
218 QSet<QObject *> ignored;
219 uint type = event->type();
221 bool checkDupes = false;
223 // special handling for numeric IrcEvents
224 if((type & ~IrcEventNumericMask) == IrcEventNumeric) {
225 ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event);
227 qWarning() << "Invalid event type for IrcEventNumeric!";
229 int num = numEvent->number();
231 insertHandlers(registeredHandlers().value(type + num), handlers, false);
232 insertFilters(registeredFilters().value(type + num), filters);
239 insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
240 insertFilters(registeredFilters().value(type), filters);
242 // check if we have a generic handler for the event group
243 if((type & EventGroupMask) != type) {
244 insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers, true);
245 insertFilters(registeredFilters().value(type & EventGroupMask), filters);
248 // now dispatch the event
249 QList<Handler>::const_iterator it;
250 for(it = handlers.begin(); it != handlers.end() && !event->isStopped(); ++it) {
251 QObject *obj = it->object;
253 if(ignored.contains(obj)) // object has filtered the event
256 if(filters.contains(obj)) { // we have a filter, so let's check if we want to deliver the event
257 Handler filter = filters.value(obj);
259 void *param[] = {Q_RETURN_ARG(bool, result).data(), Q_ARG(Event *, event).data() };
260 obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
263 continue; // mmmh, event filter told us to not accept
267 // finally, deliverance!
268 void *param[] = {0, Q_ARG(Event *, event).data() };
269 obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
276 void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing, bool checkDupes) {
277 foreach(const Handler &handler, newHandlers) {
278 if(existing.isEmpty())
279 existing.append(handler);
281 // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
283 QList<Handler>::iterator insertpos = existing.end();
284 QList<Handler>::iterator it = existing.begin();
285 while(it != existing.end()) {
286 if(checkDupes && handler.object == it->object) {
290 if(insertpos == existing.end() && handler.priority > it->priority)
296 existing.insert(it, handler);
301 // priority is ignored, and only the first (should be most specialized) filter is being used
302 // fun things could happen if you used the registerEventFilter() methods in the wrong order though
303 void EventManager::insertFilters(const QList<Handler> &newFilters, QHash<QObject *, Handler> &existing) {
304 foreach(const Handler &filter, newFilters) {
305 if(!existing.contains(filter.object))
306 existing[filter.object] = filter;
310 QMetaEnum EventManager::_enum;