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() 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());
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);
62 EventManager::EventType EventManager::eventGroupByName(const QString &name) const {
63 EventType type = eventTypeByName(name);
64 return type == Invalid? Invalid : static_cast<EventType>(type & EventGroupMask);
67 QString EventManager::enumName(EventType type) const {
68 return eventEnum().valueToKey(type);
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!
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.
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...
85 int EventManager::findEventType(const QString &methodSignature_, const QString &methodPrefix) const {
86 if(!methodSignature_.startsWith(methodPrefix))
89 QString methodSignature = methodSignature_;
91 methodSignature = methodSignature.section('(',0,0); // chop the attribute list
92 methodSignature = methodSignature.mid(methodPrefix.length()); // strip prefix
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();
100 QString numericSig = methodSignature.left(methodSignature.length() - 3) + "Numeric";
101 eventType = eventEnum().keyToValue(numericSig.toAscii());
103 qWarning() << Q_FUNC_INFO << "Could not find EventType" << numericSig << "for handling" << methodSignature;
111 eventType = eventEnum().keyToValue(methodSignature.toAscii());
113 qWarning() << Q_FUNC_INFO << "Could not find EventType" << methodSignature;
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());
123 int eventType = findEventType(methodSignature, methodPrefix);
125 Handler handler(object, i, priority);
126 registeredHandlers()[eventType].append(handler);
127 //qDebug() << "Registered event handler for" << methodSignature << "in" << object;
129 eventType = findEventType(methodSignature, filterPrefix);
131 Handler handler(object, i, priority);
132 registeredFilters()[eventType].append(handler);
133 //qDebug() << "Registered event filterer for" << methodSignature << "in" << object;
138 void EventManager::registerEventFilter(EventType event, QObject *object, const char *slot) {
139 registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
142 void EventManager::registerEventFilter(QList<EventType> events, QObject *object, const char *slot) {
143 registerEventHandler(events, object, slot, NormalPriority, true);
146 void EventManager::registerEventHandler(EventType event, QObject *object, const char *slot, Priority priority, bool isFilter) {
147 registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter);
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());
156 Handler handler(object, methodIndex, priority);
157 foreach(EventType event, events) {
159 registeredFilters()[event].append(handler);
160 qDebug() << "Registered event filter for" << event << "in" << object;
162 registeredHandlers()[event].append(handler);
163 qDebug() << "Registered event handler for" << event << "in" << object;
168 void EventManager::postEvent(Event *event) {
169 if(sender() && sender()->thread() != this->thread()) {
170 QueuedQuasselEvent *queuedEvent = new QueuedQuasselEvent(event);
171 QCoreApplication::postEvent(this, queuedEvent);
173 if(_eventQueue.isEmpty())
174 // we're currently not processing events
177 _eventQueue.append(event);
181 void EventManager::customEvent(QEvent *event) {
182 if(event->type() == QEvent::User) {
183 QueuedQuasselEvent *queuedEvent = static_cast<QueuedQuasselEvent *>(event);
184 processEvent(queuedEvent->event);
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();
199 void EventManager::dispatchEvent(Event *event) {
200 //qDebug() << "Dispatching" << event;
202 // we try handlers from specialized to generic by masking the enum
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();
210 bool checkDupes = false;
212 // special handling for numeric IrcEvents
213 if((type & ~IrcEventNumericMask) == IrcEventNumeric) {
214 ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event);
216 qWarning() << "Invalid event type for IrcEventNumeric!";
218 int num = numEvent->number();
220 insertHandlers(registeredHandlers().value(type + num), handlers, false);
221 insertFilters(registeredFilters().value(type + num), filters);
228 insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
229 insertFilters(registeredFilters().value(type), filters);
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);
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;
242 if(ignored.contains(obj)) // object has filtered the event
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);
248 void *param[] = {Q_RETURN_ARG(bool, result).data(), Q_ARG(Event *, event).data() };
249 obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
252 continue; // mmmh, event filter told us to not accept
256 // finally, deliverance!
257 void *param[] = {0, Q_ARG(Event *, event).data() };
258 obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
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);
270 // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
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) {
279 if(insertpos == existing.end() && handler.priority > it->priority)
285 existing.insert(it, handler);
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;