1 /***************************************************************************
2 * Copyright (C) 2005-2019 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)
43 // ============================================================
45 // ============================================================
46 EventManager::EventManager(QObject* parent)
50 QMetaEnum EventManager::eventEnum()
52 if (!_enum.isValid()) {
53 int eventEnumIndex = staticMetaObject.indexOfEnumerator("EventType");
54 Q_ASSERT(eventEnumIndex >= 0);
55 _enum = staticMetaObject.enumerator(eventEnumIndex);
56 Q_ASSERT(_enum.isValid());
61 EventManager::EventType EventManager::eventTypeByName(const QString& name)
63 int val = eventEnum().keyToValue(name.toLatin1());
64 return (val == -1) ? Invalid : static_cast<EventType>(val);
67 EventManager::EventType EventManager::eventGroupByName(const QString& name)
69 EventType type = eventTypeByName(name);
70 return type == Invalid ? Invalid : static_cast<EventType>(type & EventGroupMask);
73 QString EventManager::enumName(EventType type)
75 return eventEnum().valueToKey(type);
78 QString EventManager::enumName(int type)
80 return eventEnum().valueToKey(type);
83 Event* EventManager::createEvent(const QVariantMap& map)
87 Network* net = networkById(m.take("network").toInt());
88 return Event::fromVariantMap(m, net);
92 Registering and calling handlers works fine even if they specify a subclass of Event as their parameter.
93 However, this most probably is a result from a reinterpret_cast somewhere deep inside Qt, so there is *no*
94 type safety. If the event sent is of the wrong class type, you'll get a neat segfault!
95 Thus, we need to make sure that events are of the correct class type when sending!
97 We might add a registration-time check later, which will require matching the enum base name (e.g. "IrcEvent") with
98 the type the handler claims to support. This still won't protect us from someone sending an IrcEvent object
99 with an enum type "NetworkIncoming", for example.
101 Another way would be to add a check into the various Event subclasses, such that the ctor matches the given event type
102 with the actual class. Possibly (optionally) using rtti...
105 int EventManager::findEventType(const QString& methodSignature_, const QString& methodPrefix) const
107 if (!methodSignature_.startsWith(methodPrefix))
110 QString methodSignature = methodSignature_;
112 methodSignature = methodSignature.section('(', 0, 0); // chop the attribute list
113 methodSignature = methodSignature.mid(methodPrefix.length()); // strip prefix
117 // special handling for numeric IrcEvents: IrcEvent042 gets mapped to IrcEventNumeric + 42
118 if (methodSignature.length() == 8 + 3 && methodSignature.startsWith("IrcEvent")) {
119 int num = methodSignature.right(3).toUInt();
121 QString numericSig = methodSignature.left(methodSignature.length() - 3) + "Numeric";
122 eventType = eventEnum().keyToValue(numericSig.toLatin1());
124 qWarning() << Q_FUNC_INFO << "Could not find EventType" << numericSig << "for handling" << methodSignature;
132 eventType = eventEnum().keyToValue(methodSignature.toLatin1());
134 qWarning() << Q_FUNC_INFO << "Could not find EventType" << methodSignature;
140 void EventManager::registerObject(QObject* object, Priority priority, const QString& methodPrefix, const QString& filterPrefix)
142 for (int i = object->metaObject()->methodOffset(); i < object->metaObject()->methodCount(); i++) {
143 QString methodSignature = object->metaObject()->method(i).methodSignature();
144 int eventType = findEventType(methodSignature, methodPrefix);
146 Handler handler(object, i, priority);
147 registeredHandlers()[eventType].append(handler);
148 // qDebug() << "Registered event handler for" << methodSignature << "in" << object;
150 eventType = findEventType(methodSignature, filterPrefix);
152 Handler handler(object, i, priority);
153 registeredFilters()[eventType].append(handler);
154 // qDebug() << "Registered event filterer for" << methodSignature << "in" << object;
159 void EventManager::registerEventFilter(EventType event, QObject* object, const char* slot)
161 registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
164 void EventManager::registerEventFilter(QList<EventType> events, QObject* object, const char* slot)
166 registerEventHandler(events, object, slot, NormalPriority, true);
169 void EventManager::registerEventHandler(EventType event, QObject* object, const char* slot, Priority priority, bool isFilter)
171 registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter);
174 void EventManager::registerEventHandler(QList<EventType> events, QObject* object, const char* slot, Priority priority, bool isFilter)
176 int methodIndex = object->metaObject()->indexOfMethod(slot);
177 if (methodIndex < 0) {
178 qWarning() << Q_FUNC_INFO << QString("Slot %1 not found in object %2").arg(slot).arg(object->objectName());
181 Handler handler(object, methodIndex, priority);
182 foreach (EventType event, events) {
184 registeredFilters()[event].append(handler);
185 qDebug() << "Registered event filter for" << event << "in" << object;
188 registeredHandlers()[event].append(handler);
189 qDebug() << "Registered event handler for" << event << "in" << object;
194 void EventManager::postEvent(Event* event)
196 if (sender() && sender()->thread() != this->thread()) {
197 auto* queuedEvent = new QueuedQuasselEvent(event);
198 QCoreApplication::postEvent(this, queuedEvent);
201 if (_eventQueue.isEmpty())
202 // we're currently not processing events
205 _eventQueue.append(event);
209 void EventManager::customEvent(QEvent* event)
211 if (event->type() == QEvent::User) {
212 auto* queuedEvent = static_cast<QueuedQuasselEvent*>(event);
213 processEvent(queuedEvent->event);
218 void EventManager::processEvent(Event* event)
220 Q_ASSERT(_eventQueue.isEmpty());
221 dispatchEvent(event);
222 // dispatching the event might cause new events to be generated. we process those afterwards.
223 while (!_eventQueue.isEmpty()) {
224 dispatchEvent(_eventQueue.first());
225 _eventQueue.removeFirst();
229 void EventManager::dispatchEvent(Event* event)
231 // qDebug() << "Dispatching" << event;
233 // we try handlers from specialized to generic by masking the enum
235 // build a list sorted by priorities that contains all eligible handlers
236 QList<Handler> handlers;
237 QHash<QObject*, Handler> filters;
238 QSet<QObject*> ignored;
239 uint type = event->type();
241 bool checkDupes = false;
243 // special handling for numeric IrcEvents
244 if ((type & ~IrcEventNumericMask) == IrcEventNumeric) {
245 auto* numEvent = static_cast<::IrcEventNumeric*>(event);
247 qWarning() << "Invalid event type for IrcEventNumeric!";
249 int num = numEvent->number();
251 insertHandlers(registeredHandlers().value(type + num), handlers, false);
252 insertFilters(registeredFilters().value(type + num), filters);
259 insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
260 insertFilters(registeredFilters().value(type), filters);
262 // check if we have a generic handler for the event group
263 if ((type & EventGroupMask) != type) {
264 insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers, true);
265 insertFilters(registeredFilters().value(type & EventGroupMask), filters);
268 // now dispatch the event
269 QList<Handler>::const_iterator it;
270 for (it = handlers.begin(); it != handlers.end() && !event->isStopped(); ++it) {
271 QObject* obj = it->object;
273 if (ignored.contains(obj)) // object has filtered the event
276 if (filters.contains(obj)) { // we have a filter, so let's check if we want to deliver the event
277 Handler filter = filters.value(obj);
279 void* param[] = {Q_RETURN_ARG(bool, result).data(), Q_ARG(Event*, event).data()};
280 obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
283 continue; // mmmh, event filter told us to not accept
287 // finally, deliverance!
288 void* param[] = {nullptr, Q_ARG(Event*, event).data()};
289 obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
296 void EventManager::insertHandlers(const QList<Handler>& newHandlers, QList<Handler>& existing, bool checkDupes)
298 foreach (const Handler& handler, newHandlers) {
299 if (existing.isEmpty())
300 existing.append(handler);
302 // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
304 QList<Handler>::iterator insertpos = existing.end();
305 QList<Handler>::iterator it = existing.begin();
306 while (it != existing.end()) {
307 if (checkDupes && handler.object == it->object) {
311 if (insertpos == existing.end() && handler.priority > it->priority)
317 existing.insert(it, handler);
322 // priority is ignored, and only the first (should be most specialized) filter is being used
323 // fun things could happen if you used the registerEventFilter() methods in the wrong order though
324 void EventManager::insertFilters(const QList<Handler>& newFilters, QHash<QObject*, Handler>& existing)
326 foreach (const Handler& filter, newFilters) {
327 if (!existing.contains(filter.object))
328 existing[filter.object] = filter;
332 QMetaEnum EventManager::_enum;