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