modernize: Pass arguments by value and move in constructors
[quassel.git] / src / common / eventmanager.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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 <QEvent>
25 #include <QDebug>
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), event(event) {}
38     Event *event;
39 };
40
41
42 // ============================================================
43 //  EventManager
44 // ============================================================
45 EventManager::EventManager(QObject *parent)
46     : QObject(parent)
47 {
48 }
49
50
51 QMetaEnum EventManager::eventEnum()
52 {
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());
58     }
59     return _enum;
60 }
61
62
63 EventManager::EventType EventManager::eventTypeByName(const QString &name)
64 {
65     int val = eventEnum().keyToValue(name.toLatin1());
66     return (val == -1) ? Invalid : static_cast<EventType>(val);
67 }
68
69
70 EventManager::EventType EventManager::eventGroupByName(const QString &name)
71 {
72     EventType type = eventTypeByName(name);
73     return type == Invalid ? Invalid : static_cast<EventType>(type & EventGroupMask);
74 }
75
76
77 QString EventManager::enumName(EventType type)
78 {
79     return eventEnum().valueToKey(type);
80 }
81
82
83 QString EventManager::enumName(int type)
84 {
85     return eventEnum().valueToKey(type);
86 }
87
88
89 Event *EventManager::createEvent(const QVariantMap &map)
90 {
91     QVariantMap m = map;
92
93     Network *net = networkById(m.take("network").toInt());
94     return Event::fromVariantMap(m, net);
95 }
96
97
98 /* NOTE:
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!
103
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.
107
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...
110 */
111
112 int EventManager::findEventType(const QString &methodSignature_, const QString &methodPrefix) const
113 {
114     if (!methodSignature_.startsWith(methodPrefix))
115         return -1;
116
117     QString methodSignature = methodSignature_;
118
119     methodSignature = methodSignature.section('(', 0, 0); // chop the attribute list
120     methodSignature = methodSignature.mid(methodPrefix.length()); // strip prefix
121
122     int eventType = -1;
123
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();
127         if (num > 0) {
128             QString numericSig = methodSignature.left(methodSignature.length() - 3) + "Numeric";
129             eventType = eventEnum().keyToValue(numericSig.toLatin1());
130             if (eventType < 0) {
131                 qWarning() << Q_FUNC_INFO << "Could not find EventType" << numericSig << "for handling" << methodSignature;
132                 return -1;
133             }
134             eventType += num;
135         }
136     }
137
138     if (eventType < 0)
139         eventType = eventEnum().keyToValue(methodSignature.toLatin1());
140     if (eventType < 0) {
141         qWarning() << Q_FUNC_INFO << "Could not find EventType" << methodSignature;
142         return -1;
143     }
144     return eventType;
145 }
146
147
148 void EventManager::registerObject(QObject *object, Priority priority, const QString &methodPrefix, const QString &filterPrefix)
149 {
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);
153         if (eventType > 0) {
154             Handler handler(object, i, priority);
155             registeredHandlers()[eventType].append(handler);
156             //qDebug() << "Registered event handler for" << methodSignature << "in" << object;
157         }
158         eventType = findEventType(methodSignature, filterPrefix);
159         if (eventType > 0) {
160             Handler handler(object, i, priority);
161             registeredFilters()[eventType].append(handler);
162             //qDebug() << "Registered event filterer for" << methodSignature << "in" << object;
163         }
164     }
165 }
166
167
168 void EventManager::registerEventFilter(EventType event, QObject *object, const char *slot)
169 {
170     registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
171 }
172
173
174 void EventManager::registerEventFilter(QList<EventType> events, QObject *object, const char *slot)
175 {
176     registerEventHandler(events, object, slot, NormalPriority, true);
177 }
178
179
180 void EventManager::registerEventHandler(EventType event, QObject *object, const char *slot, Priority priority, bool isFilter)
181 {
182     registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter);
183 }
184
185
186 void EventManager::registerEventHandler(QList<EventType> events, QObject *object, const char *slot, Priority priority, bool isFilter)
187 {
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());
191         return;
192     }
193     Handler handler(object, methodIndex, priority);
194     foreach(EventType event, events) {
195         if (isFilter) {
196             registeredFilters()[event].append(handler);
197             qDebug() << "Registered event filter for" << event << "in" << object;
198         }
199         else {
200             registeredHandlers()[event].append(handler);
201             qDebug() << "Registered event handler for" << event << "in" << object;
202         }
203     }
204 }
205
206
207 void EventManager::postEvent(Event *event)
208 {
209     if (sender() && sender()->thread() != this->thread()) {
210         QueuedQuasselEvent *queuedEvent = new QueuedQuasselEvent(event);
211         QCoreApplication::postEvent(this, queuedEvent);
212     }
213     else {
214         if (_eventQueue.isEmpty())
215             // we're currently not processing events
216             processEvent(event);
217         else
218             _eventQueue.append(event);
219     }
220 }
221
222
223 void EventManager::customEvent(QEvent *event)
224 {
225     if (event->type() == QEvent::User) {
226         QueuedQuasselEvent *queuedEvent = static_cast<QueuedQuasselEvent *>(event);
227         processEvent(queuedEvent->event);
228         event->accept();
229     }
230 }
231
232
233 void EventManager::processEvent(Event *event)
234 {
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();
241     }
242 }
243
244
245 void EventManager::dispatchEvent(Event *event)
246 {
247     //qDebug() << "Dispatching" << event;
248
249     // we try handlers from specialized to generic by masking the enum
250
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();
256
257     bool checkDupes = false;
258
259     // special handling for numeric IrcEvents
260     if ((type & ~IrcEventNumericMask) == IrcEventNumeric) {
261         ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event);
262         if (!numEvent)
263             qWarning() << "Invalid event type for IrcEventNumeric!";
264         else {
265             int num = numEvent->number();
266             if (num > 0) {
267                 insertHandlers(registeredHandlers().value(type + num), handlers, false);
268                 insertFilters(registeredFilters().value(type + num), filters);
269                 checkDupes = true;
270             }
271         }
272     }
273
274     // exact type
275     insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
276     insertFilters(registeredFilters().value(type), filters);
277
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);
282     }
283
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;
288
289         if (ignored.contains(obj)) // object has filtered the event
290             continue;
291
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);
294             bool result = false;
295             void *param[] = { Q_RETURN_ARG(bool, result).data(), Q_ARG(Event *, event).data() };
296             obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
297             if (!result) {
298                 ignored.insert(obj);
299                 continue; // mmmh, event filter told us to not accept
300             }
301         }
302
303         // finally, deliverance!
304         void *param[] = { nullptr, Q_ARG(Event *, event).data() };
305         obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
306     }
307
308     // that's it
309     delete event;
310 }
311
312
313 void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing, bool checkDupes)
314 {
315     foreach(const Handler &handler, newHandlers) {
316         if (existing.isEmpty())
317             existing.append(handler);
318         else {
319             // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
320             bool insert = true;
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) {
325                     insert = false;
326                     break;
327                 }
328                 if (insertpos == existing.end() && handler.priority > it->priority)
329                     insertpos = it;
330
331                 ++it;
332             }
333             if (insert)
334                 existing.insert(it, handler);
335         }
336     }
337 }
338
339
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)
343 {
344     foreach(const Handler &filter, newFilters) {
345         if (!existing.contains(filter.object))
346             existing[filter.object] = filter;
347     }
348 }
349
350
351 QMetaEnum EventManager::_enum;