Add more Event specializations (IrcEvent*, MessageEvent)
[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 <QDebug>
24
25 #include "event.h"
26
27 EventManager::EventManager(QObject *parent) : QObject(parent) {
28
29 }
30
31 QMetaEnum EventManager::eventEnum() const {
32   if(!_enum.isValid()) {
33     int eventEnumIndex = metaObject()->indexOfEnumerator("EventType");
34     Q_ASSERT(eventEnumIndex >= 0);
35     _enum = metaObject()->enumerator(eventEnumIndex);
36     Q_ASSERT(_enum.isValid());
37   }
38   return _enum;
39 }
40
41 EventManager::EventType EventManager::eventTypeByName(const QString &name) const {
42   int val = eventEnum().keyToValue(name.toLatin1());
43   return (val == -1) ? Invalid : static_cast<EventType>(val);
44 }
45
46 EventManager::EventType EventManager::eventGroupByName(const QString &name) const {
47   EventType type = eventTypeByName(name);
48   return type == Invalid? Invalid : static_cast<EventType>(type & EventGroupMask);
49 }
50
51 /* NOTE:
52    Registering and calling handlers works fine even if they specify a subclass of Event as their parameter.
53    However, this most probably is a result from a reinterpret_cast somewhere deep inside Qt, so there is *no*
54    type safety. If the event sent is of the wrong class type, you'll get a neat segfault!
55    Thus, we need to make sure that events are of the correct class type when sending!
56
57    We might add a registration-time check later, which will require matching the enum base name (e.g. "IrcEvent") with
58    the type the handler claims to support. This still won't protect us from someone sending an IrcEvent object
59    with an enum type "NetworkIncoming", for example.
60
61    Another way would be to add a check into the various Event subclasses, such that the ctor matches the given event type
62    with the actual class. Possibly (optionally) using rtti...
63 */
64
65 void EventManager::registerObject(QObject *object, Priority priority, const QString &methodPrefix) {
66   for(int i = object->metaObject()->methodOffset(); i < object->metaObject()->methodCount(); i++) {
67     QString methodSignature(object->metaObject()->method(i).signature());
68
69     if(!methodSignature.startsWith(methodPrefix))
70       continue;
71
72     methodSignature = methodSignature.section('(',0,0);  // chop the attribute list
73     methodSignature = methodSignature.mid(methodPrefix.length()); // strip prefix
74
75     int eventType = eventEnum().keyToValue(methodSignature.toAscii());
76     if(eventType < 0) {
77       qWarning() << Q_FUNC_INFO << QString("Could not find EventType %1").arg(methodSignature);
78       continue;
79     }
80     Handler handler(object, i, priority);
81     registeredHandlers()[static_cast<EventType>(eventType)].append(handler);
82     qDebug() << "Registered event handler for" << methodSignature << "in" << object;
83   }
84 }
85
86 void EventManager::registerEventHandler(EventType event, QObject *object, const char *slot, Priority priority) {
87   registerEventHandler(QList<EventType>() << event, object, slot, priority);
88 }
89
90 void EventManager::registerEventHandler(QList<EventType> events, QObject *object, const char *slot, Priority priority) {
91   int methodIndex = object->metaObject()->indexOfMethod(slot);
92   if(methodIndex < 0) {
93     qWarning() << Q_FUNC_INFO << QString("Slot %1 not found in object %2").arg(slot).arg(object->objectName());
94     return;
95   }
96   Handler handler(object, methodIndex, priority);
97   foreach(EventType event, events) {
98     registeredHandlers()[event].append(handler);
99     qDebug() << "Registered event handler for" << event << "in" << object;
100   }
101 }
102
103 // not threadsafe! if we should want that, we need to add a mutexed queue somewhere in this general area.
104 void EventManager::sendEvent(Event *event) {
105   dispatchEvent(event);
106 }
107
108 void EventManager::dispatchEvent(Event *event) {
109   // we try handlers from specialized to generic by masking the enum
110
111   // build a list sorted by priorities that contains all eligible handlers
112   QList<Handler> handlers;
113   EventType type = event->type();
114   insertHandlers(registeredHandlers().value(type), handlers);
115
116   // check if we have a generic handler for the event group
117   if((type & EventGroupMask) != type)
118     insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers);
119
120   // now dispatch the event
121   QList<Handler>::const_iterator it = handlers.begin();
122   while(it != handlers.end()) {
123
124     // TODO: check event flags here!
125
126     void *param[] = {0, Q_ARG(Event *, event).data() };
127     it->object->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
128
129     ++it;
130   }
131
132   // finally, delete it
133   delete event;
134 }
135
136 void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing) {
137   foreach(Handler handler, newHandlers) {
138     if(existing.isEmpty())
139       existing.append(handler);
140     else {
141       // need to insert it at the proper position
142       QList<Handler>::iterator it = existing.begin();
143       while(it != existing.end()) {
144         if(handler.priority > it->priority)
145           break;
146         ++it;
147       }
148       existing.insert(it, handler);
149     }
150   }
151 }