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