Properly handle numeric events
[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 void EventManager::registerObject(QObject *object, Priority priority, const QString &methodPrefix) {
78   for(int i = object->metaObject()->methodOffset(); i < object->metaObject()->methodCount(); i++) {
79     QString methodSignature(object->metaObject()->method(i).signature());
80
81     if(!methodSignature.startsWith(methodPrefix))
82       continue;
83
84     methodSignature = methodSignature.section('(',0,0);  // chop the attribute list
85     methodSignature = methodSignature.mid(methodPrefix.length()); // strip prefix
86
87     int eventType = -1;
88
89     // special handling for numeric IrcEvents: IrcEvent042 gets mapped to IrcEventNumeric + 42
90     if(methodSignature.length() == 8+3 && methodSignature.startsWith("IrcEvent")) {
91       int num = methodSignature.right(3).toUInt();
92       if(num > 0) {
93         QString numericSig = methodSignature.left(methodSignature.length() - 3) + "Numeric";
94         eventType = eventEnum().keyToValue(numericSig.toAscii());
95         if(eventType < 0) {
96           qWarning() << Q_FUNC_INFO << "Could not find EventType" << numericSig << "for handling" << methodSignature;
97           continue;
98         }
99         eventType += num;
100       }
101     }
102
103     if(eventType < 0)
104       eventType = eventEnum().keyToValue(methodSignature.toAscii());
105     if(eventType < 0) {
106       qWarning() << Q_FUNC_INFO << "Could not find EventType" << methodSignature;
107       continue;
108     }
109     Handler handler(object, i, priority);
110     registeredHandlers()[eventType].append(handler);
111     qDebug() << "Registered event handler for" << methodSignature << "in" << object;
112   }
113 }
114
115 void EventManager::registerEventHandler(EventType event, QObject *object, const char *slot, Priority priority) {
116   registerEventHandler(QList<EventType>() << event, object, slot, priority);
117 }
118
119 void EventManager::registerEventHandler(QList<EventType> events, QObject *object, const char *slot, Priority priority) {
120   int methodIndex = object->metaObject()->indexOfMethod(slot);
121   if(methodIndex < 0) {
122     qWarning() << Q_FUNC_INFO << QString("Slot %1 not found in object %2").arg(slot).arg(object->objectName());
123     return;
124   }
125   Handler handler(object, methodIndex, priority);
126   foreach(EventType event, events) {
127     registeredHandlers()[event].append(handler);
128     qDebug() << "Registered event handler for" << event << "in" << object;
129   }
130 }
131
132 // not threadsafe! if we should want that, we need to add a mutexed queue somewhere in this general area.
133 void EventManager::sendEvent(Event *event) {
134   // qDebug() << "Sending" << event;
135   _eventQueue.append(event);
136   if(_eventQueue.count() == 1) // we're not currently processing another event
137     processEvents();
138 }
139
140 void EventManager::customEvent(QEvent *event) {
141   if(event->type() == QEvent::User) {
142     processEvents();
143     event->accept();
144   }
145 }
146
147 void EventManager::processEvents() {
148   // we only process one event at a time for now, and let Qt's own event processing come in between
149   if(_eventQueue.isEmpty())
150     return;
151   dispatchEvent(_eventQueue.first());
152   _eventQueue.removeFirst();
153   if(_eventQueue.count())
154     QCoreApplication::postEvent(this, new QEvent(QEvent::User));
155 }
156
157 void EventManager::dispatchEvent(Event *event) {
158   //qDebug() << "Dispatching" << event;
159
160   // we try handlers from specialized to generic by masking the enum
161
162   // build a list sorted by priorities that contains all eligible handlers
163   QList<Handler> handlers;
164   uint type = event->type();
165
166   // special handling for numeric IrcEvents
167   if((type & ~IrcEventNumericMask) == IrcEventNumeric) {
168     ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event);
169     if(!numEvent)
170       qWarning() << "Invalid event type for IrcEventNumeric!";
171     else {
172       int num = numEvent->number();
173       if(num > 0)
174         insertHandlers(registeredHandlers().value(type + num), handlers);
175     }
176   }
177
178   // exact type
179   insertHandlers(registeredHandlers().value(type), handlers);
180
181   // check if we have a generic handler for the event group
182   if((type & EventGroupMask) != type)
183     insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers);
184
185   // now dispatch the event
186   QList<Handler>::const_iterator it = handlers.begin();
187   while(it != handlers.end()) {
188
189     // TODO: check event flags here!
190
191     void *param[] = {0, Q_ARG(Event *, event).data() };
192     it->object->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
193
194     ++it;
195   }
196
197   // finally, delete it
198   delete event;
199 }
200
201 void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing) {
202   foreach(Handler handler, newHandlers) {
203     if(existing.isEmpty())
204       existing.append(handler);
205     else {
206       // need to insert it at the proper position
207       QList<Handler>::iterator it = existing.begin();
208       while(it != existing.end()) {
209         if(handler.priority > it->priority)
210           break;
211         ++it;
212       }
213       existing.insert(it, handler);
214     }
215   }
216 }