1 /***************************************************************************
2 * Copyright (C) 2005-2018 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 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include "eventmanager.h"
23 #include <QCoreApplication>
30 // ============================================================
32 // ============================================================
33 class QueuedQuasselEvent : public QEvent
36 QueuedQuasselEvent(Event *event)
37 : QEvent(QEvent::User), event(event) {}
42 // ============================================================
44 // ============================================================
45 EventManager::EventManager(QObject *parent)
51 QMetaEnum EventManager::eventEnum()
53 if (!_enum.isValid()) {
54 int eventEnumIndex = staticMetaObject.indexOfEnumerator("EventType");
55 Q_ASSERT(eventEnumIndex >= 0);
56 _enum = staticMetaObject.enumerator(eventEnumIndex);
57 Q_ASSERT(_enum.isValid());
63 EventManager::EventType EventManager::eventTypeByName(const QString &name)
65 int val = eventEnum().keyToValue(name.toLatin1());
66 return (val == -1) ? Invalid : static_cast<EventType>(val);
70 EventManager::EventType EventManager::eventGroupByName(const QString &name)
72 EventType type = eventTypeByName(name);
73 return type == Invalid ? Invalid : static_cast<EventType>(type & EventGroupMask);
77 QString EventManager::enumName(EventType type)
79 return eventEnum().valueToKey(type);
83 QString EventManager::enumName(int type)
85 return eventEnum().valueToKey(type);
89 Event *EventManager::createEvent(const QVariantMap &map)
93 Network *net = networkById(m.take("network").toInt());
94 return Event::fromVariantMap(m, net);
99 Registering and calling handlers works fine even if they specify a subclass of Event as their parameter.
100 However, this most probably is a result from a reinterpret_cast somewhere deep inside Qt, so there is *no*
101 type safety. If the event sent is of the wrong class type, you'll get a neat segfault!
102 Thus, we need to make sure that events are of the correct class type when sending!
104 We might add a registration-time check later, which will require matching the enum base name (e.g. "IrcEvent") with
105 the type the handler claims to support. This still won't protect us from someone sending an IrcEvent object
106 with an enum type "NetworkIncoming", for example.
108 Another way would be to add a check into the various Event subclasses, such that the ctor matches the given event type
109 with the actual class. Possibly (optionally) using rtti...
112 int EventManager::findEventType(const QString &methodSignature_, const QString &methodPrefix) const
114 if (!methodSignature_.startsWith(methodPrefix))
117 QString methodSignature = methodSignature_;
119 methodSignature = methodSignature.section('(', 0, 0); // chop the attribute list
120 methodSignature = methodSignature.mid(methodPrefix.length()); // strip prefix
124 // special handling for numeric IrcEvents: IrcEvent042 gets mapped to IrcEventNumeric + 42
125 if (methodSignature.length() == 8+3 && methodSignature.startsWith("IrcEvent")) {
126 int num = methodSignature.right(3).toUInt();
128 QString numericSig = methodSignature.left(methodSignature.length() - 3) + "Numeric";
129 eventType = eventEnum().keyToValue(numericSig.toLatin1());
131 qWarning() << Q_FUNC_INFO << "Could not find EventType" << numericSig << "for handling" << methodSignature;
139 eventType = eventEnum().keyToValue(methodSignature.toLatin1());
141 qWarning() << Q_FUNC_INFO << "Could not find EventType" << methodSignature;
148 void EventManager::registerObject(QObject *object, Priority priority, const QString &methodPrefix, const QString &filterPrefix)
150 for (int i = object->metaObject()->methodOffset(); i < object->metaObject()->methodCount(); i++) {
151 QString methodSignature = object->metaObject()->method(i).methodSignature();
152 int eventType = findEventType(methodSignature, methodPrefix);
154 Handler handler(object, i, priority);
155 registeredHandlers()[eventType].append(handler);
156 //qDebug() << "Registered event handler for" << methodSignature << "in" << object;
158 eventType = findEventType(methodSignature, filterPrefix);
160 Handler handler(object, i, priority);
161 registeredFilters()[eventType].append(handler);
162 //qDebug() << "Registered event filterer for" << methodSignature << "in" << object;
168 void EventManager::registerEventFilter(EventType event, QObject *object, const char *slot)
170 registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
174 void EventManager::registerEventFilter(QList<EventType> events, QObject *object, const char *slot)
176 registerEventHandler(events, object, slot, NormalPriority, true);
180 void EventManager::registerEventHandler(EventType event, QObject *object, const char *slot, Priority priority, bool isFilter)
182 registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter);
186 void EventManager::registerEventHandler(QList<EventType> events, QObject *object, const char *slot, Priority priority, bool isFilter)
188 int methodIndex = object->metaObject()->indexOfMethod(slot);
189 if (methodIndex < 0) {
190 qWarning() << Q_FUNC_INFO << QString("Slot %1 not found in object %2").arg(slot).arg(object->objectName());
193 Handler handler(object, methodIndex, priority);
194 foreach(EventType event, events) {
196 registeredFilters()[event].append(handler);
197 qDebug() << "Registered event filter for" << event << "in" << object;
200 registeredHandlers()[event].append(handler);
201 qDebug() << "Registered event handler for" << event << "in" << object;
207 void EventManager::postEvent(Event *event)
209 if (sender() && sender()->thread() != this->thread()) {
210 QueuedQuasselEvent *queuedEvent = new QueuedQuasselEvent(event);
211 QCoreApplication::postEvent(this, queuedEvent);
214 if (_eventQueue.isEmpty())
215 // we're currently not processing events
218 _eventQueue.append(event);
223 void EventManager::customEvent(QEvent *event)
225 if (event->type() == QEvent::User) {
226 QueuedQuasselEvent *queuedEvent = static_cast<QueuedQuasselEvent *>(event);
227 processEvent(queuedEvent->event);
233 void EventManager::processEvent(Event *event)
235 Q_ASSERT(_eventQueue.isEmpty());
236 dispatchEvent(event);
237 // dispatching the event might cause new events to be generated. we process those afterwards.
238 while (!_eventQueue.isEmpty()) {
239 dispatchEvent(_eventQueue.first());
240 _eventQueue.removeFirst();
245 void EventManager::dispatchEvent(Event *event)
247 //qDebug() << "Dispatching" << event;
249 // we try handlers from specialized to generic by masking the enum
251 // build a list sorted by priorities that contains all eligible handlers
252 QList<Handler> handlers;
253 QHash<QObject *, Handler> filters;
254 QSet<QObject *> ignored;
255 uint type = event->type();
257 bool checkDupes = false;
259 // special handling for numeric IrcEvents
260 if ((type & ~IrcEventNumericMask) == IrcEventNumeric) {
261 ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event);
263 qWarning() << "Invalid event type for IrcEventNumeric!";
265 int num = numEvent->number();
267 insertHandlers(registeredHandlers().value(type + num), handlers, false);
268 insertFilters(registeredFilters().value(type + num), filters);
275 insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
276 insertFilters(registeredFilters().value(type), filters);
278 // check if we have a generic handler for the event group
279 if ((type & EventGroupMask) != type) {
280 insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers, true);
281 insertFilters(registeredFilters().value(type & EventGroupMask), filters);
284 // now dispatch the event
285 QList<Handler>::const_iterator it;
286 for (it = handlers.begin(); it != handlers.end() && !event->isStopped(); ++it) {
287 QObject *obj = it->object;
289 if (ignored.contains(obj)) // object has filtered the event
292 if (filters.contains(obj)) { // we have a filter, so let's check if we want to deliver the event
293 Handler filter = filters.value(obj);
295 void *param[] = { Q_RETURN_ARG(bool, result).data(), Q_ARG(Event *, event).data() };
296 obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
299 continue; // mmmh, event filter told us to not accept
303 // finally, deliverance!
304 void *param[] = { nullptr, Q_ARG(Event *, event).data() };
305 obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
313 void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing, bool checkDupes)
315 foreach(const Handler &handler, newHandlers) {
316 if (existing.isEmpty())
317 existing.append(handler);
319 // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
321 QList<Handler>::iterator insertpos = existing.end();
322 QList<Handler>::iterator it = existing.begin();
323 while (it != existing.end()) {
324 if (checkDupes && handler.object == it->object) {
328 if (insertpos == existing.end() && handler.priority > it->priority)
334 existing.insert(it, handler);
340 // priority is ignored, and only the first (should be most specialized) filter is being used
341 // fun things could happen if you used the registerEventFilter() methods in the wrong order though
342 void EventManager::insertFilters(const QList<Handler> &newFilters, QHash<QObject *, Handler> &existing)
344 foreach(const Handler &filter, newFilters) {
345 if (!existing.contains(filter.object))
346 existing[filter.object] = filter;
351 QMetaEnum EventManager::_enum;