src: Yearly copyright bump
[quassel.git] / src / common / eventmanager.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2020 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  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include "eventmanager.h"
22
23 #include <QCoreApplication>
24 #include <QDebug>
25 #include <QEvent>
26
27 #include "event.h"
28 #include "ircevent.h"
29
30 // ============================================================
31 //  QueuedEvent
32 // ============================================================
33 class QueuedQuasselEvent : public QEvent
34 {
35 public:
36     QueuedQuasselEvent(Event* event)
37         : QEvent(QEvent::User)
38         , event(event)
39     {}
40     Event* event;
41 };
42
43 // ============================================================
44 //  EventManager
45 // ============================================================
46 EventManager::EventManager(QObject* parent)
47     : QObject(parent)
48 {}
49
50 QMetaEnum EventManager::eventEnum()
51 {
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());
57     }
58     return _enum;
59 }
60
61 EventManager::EventType EventManager::eventTypeByName(const QString& name)
62 {
63     int val = eventEnum().keyToValue(name.toLatin1());
64     return (val == -1) ? Invalid : static_cast<EventType>(val);
65 }
66
67 EventManager::EventType EventManager::eventGroupByName(const QString& name)
68 {
69     EventType type = eventTypeByName(name);
70     return type == Invalid ? Invalid : static_cast<EventType>(type & EventGroupMask);
71 }
72
73 QString EventManager::enumName(EventType type)
74 {
75     return eventEnum().valueToKey(type);
76 }
77
78 QString EventManager::enumName(int type)
79 {
80     return eventEnum().valueToKey(type);
81 }
82
83 Event* EventManager::createEvent(const QVariantMap& map)
84 {
85     QVariantMap m = map;
86
87     Network* net = networkById(m.take("network").toInt());
88     return Event::fromVariantMap(m, net);
89 }
90
91 /* NOTE:
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!
96
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.
100
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...
103 */
104
105 int EventManager::findEventType(const QString& methodSignature_, const QString& methodPrefix) const
106 {
107     if (!methodSignature_.startsWith(methodPrefix))
108         return -1;
109
110     QString methodSignature = methodSignature_;
111
112     methodSignature = methodSignature.section('(', 0, 0);          // chop the attribute list
113     methodSignature = methodSignature.mid(methodPrefix.length());  // strip prefix
114
115     int eventType = -1;
116
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();
120         if (num > 0) {
121             QString numericSig = methodSignature.left(methodSignature.length() - 3) + "Numeric";
122             eventType = eventEnum().keyToValue(numericSig.toLatin1());
123             if (eventType < 0) {
124                 qWarning() << Q_FUNC_INFO << "Could not find EventType" << numericSig << "for handling" << methodSignature;
125                 return -1;
126             }
127             eventType += num;
128         }
129     }
130
131     if (eventType < 0)
132         eventType = eventEnum().keyToValue(methodSignature.toLatin1());
133     if (eventType < 0) {
134         qWarning() << Q_FUNC_INFO << "Could not find EventType" << methodSignature;
135         return -1;
136     }
137     return eventType;
138 }
139
140 void EventManager::registerObject(QObject* object, Priority priority, const QString& methodPrefix, const QString& filterPrefix)
141 {
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);
145         if (eventType > 0) {
146             Handler handler(object, i, priority);
147             registeredHandlers()[eventType].append(handler);
148             // qDebug() << "Registered event handler for" << methodSignature << "in" << object;
149         }
150         eventType = findEventType(methodSignature, filterPrefix);
151         if (eventType > 0) {
152             Handler handler(object, i, priority);
153             registeredFilters()[eventType].append(handler);
154             // qDebug() << "Registered event filterer for" << methodSignature << "in" << object;
155         }
156     }
157 }
158
159 void EventManager::registerEventFilter(EventType event, QObject* object, const char* slot)
160 {
161     registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
162 }
163
164 void EventManager::registerEventFilter(QList<EventType> events, QObject* object, const char* slot)
165 {
166     registerEventHandler(events, object, slot, NormalPriority, true);
167 }
168
169 void EventManager::registerEventHandler(EventType event, QObject* object, const char* slot, Priority priority, bool isFilter)
170 {
171     registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter);
172 }
173
174 void EventManager::registerEventHandler(QList<EventType> events, QObject* object, const char* slot, Priority priority, bool isFilter)
175 {
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());
179         return;
180     }
181     Handler handler(object, methodIndex, priority);
182     foreach (EventType event, events) {
183         if (isFilter) {
184             registeredFilters()[event].append(handler);
185             qDebug() << "Registered event filter for" << event << "in" << object;
186         }
187         else {
188             registeredHandlers()[event].append(handler);
189             qDebug() << "Registered event handler for" << event << "in" << object;
190         }
191     }
192 }
193
194 void EventManager::postEvent(Event* event)
195 {
196     if (sender() && sender()->thread() != this->thread()) {
197         auto* queuedEvent = new QueuedQuasselEvent(event);
198         QCoreApplication::postEvent(this, queuedEvent);
199     }
200     else {
201         if (_eventQueue.isEmpty())
202             // we're currently not processing events
203             processEvent(event);
204         else
205             _eventQueue.append(event);
206     }
207 }
208
209 void EventManager::customEvent(QEvent* event)
210 {
211     if (event->type() == QEvent::User) {
212         auto* queuedEvent = static_cast<QueuedQuasselEvent*>(event);
213         processEvent(queuedEvent->event);
214         event->accept();
215     }
216 }
217
218 void EventManager::processEvent(Event* event)
219 {
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();
226     }
227 }
228
229 void EventManager::dispatchEvent(Event* event)
230 {
231     // qDebug() << "Dispatching" << event;
232
233     // we try handlers from specialized to generic by masking the enum
234
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();
240
241     bool checkDupes = false;
242
243     // special handling for numeric IrcEvents
244     if ((type & ~IrcEventNumericMask) == IrcEventNumeric) {
245         auto* numEvent = static_cast<::IrcEventNumeric*>(event);
246         if (!numEvent)
247             qWarning() << "Invalid event type for IrcEventNumeric!";
248         else {
249             int num = numEvent->number();
250             if (num > 0) {
251                 insertHandlers(registeredHandlers().value(type + num), handlers, false);
252                 insertFilters(registeredFilters().value(type + num), filters);
253                 checkDupes = true;
254             }
255         }
256     }
257
258     // exact type
259     insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
260     insertFilters(registeredFilters().value(type), filters);
261
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);
266     }
267
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;
272
273         if (ignored.contains(obj))  // object has filtered the event
274             continue;
275
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);
278             bool result = false;
279             void* param[] = {Q_RETURN_ARG(bool, result).data(), Q_ARG(Event*, event).data()};
280             obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
281             if (!result) {
282                 ignored.insert(obj);
283                 continue;  // mmmh, event filter told us to not accept
284             }
285         }
286
287         // finally, deliverance!
288         void* param[] = {nullptr, Q_ARG(Event*, event).data()};
289         obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
290     }
291
292     // that's it
293     delete event;
294 }
295
296 void EventManager::insertHandlers(const QList<Handler>& newHandlers, QList<Handler>& existing, bool checkDupes)
297 {
298     foreach (const Handler& handler, newHandlers) {
299         if (existing.isEmpty())
300             existing.append(handler);
301         else {
302             // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
303             bool insert = true;
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) {
308                     insert = false;
309                     break;
310                 }
311                 if (insertpos == existing.end() && handler.priority > it->priority)
312                     insertpos = it;
313
314                 ++it;
315             }
316             if (insert)
317                 existing.insert(it, handler);
318         }
319     }
320 }
321
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)
325 {
326     foreach (const Handler& filter, newFilters) {
327         if (!existing.contains(filter.object))
328             existing[filter.object] = filter;
329     }
330 }
331
332 QMetaEnum EventManager::_enum;