Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
eventmanager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 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:
37  : QEvent(QEvent::User), event(event) {}
39 };
40 
41 
42 // ============================================================
43 // EventManager
44 // ============================================================
46  : QObject(parent)
47 {
48 }
49 
50 
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 #if QT_VERSION >= 0x050000
152  QString methodSignature = object->metaObject()->method(i).methodSignature();
153 #else
154  QString methodSignature = object->metaObject()->method(i).signature();
155 #endif
156 
157  int eventType = findEventType(methodSignature, methodPrefix);
158  if (eventType > 0) {
159  Handler handler(object, i, priority);
160  registeredHandlers()[eventType].append(handler);
161  //qDebug() << "Registered event handler for" << methodSignature << "in" << object;
162  }
163  eventType = findEventType(methodSignature, filterPrefix);
164  if (eventType > 0) {
165  Handler handler(object, i, priority);
166  registeredFilters()[eventType].append(handler);
167  //qDebug() << "Registered event filterer for" << methodSignature << "in" << object;
168  }
169  }
170 }
171 
172 
173 void EventManager::registerEventFilter(EventType event, QObject *object, const char *slot)
174 {
175  registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
176 }
177 
178 
179 void EventManager::registerEventFilter(QList<EventType> events, QObject *object, const char *slot)
180 {
181  registerEventHandler(events, object, slot, NormalPriority, true);
182 }
183 
184 
185 void EventManager::registerEventHandler(EventType event, QObject *object, const char *slot, Priority priority, bool isFilter)
186 {
187  registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter);
188 }
189 
190 
191 void EventManager::registerEventHandler(QList<EventType> events, QObject *object, const char *slot, Priority priority, bool isFilter)
192 {
193  int methodIndex = object->metaObject()->indexOfMethod(slot);
194  if (methodIndex < 0) {
195  qWarning() << Q_FUNC_INFO << QString("Slot %1 not found in object %2").arg(slot).arg(object->objectName());
196  return;
197  }
198  Handler handler(object, methodIndex, priority);
199  foreach(EventType event, events) {
200  if (isFilter) {
201  registeredFilters()[event].append(handler);
202  qDebug() << "Registered event filter for" << event << "in" << object;
203  }
204  else {
205  registeredHandlers()[event].append(handler);
206  qDebug() << "Registered event handler for" << event << "in" << object;
207  }
208  }
209 }
210 
211 
213 {
214  if (sender() && sender()->thread() != this->thread()) {
215  QueuedQuasselEvent *queuedEvent = new QueuedQuasselEvent(event);
216  QCoreApplication::postEvent(this, queuedEvent);
217  }
218  else {
219  if (_eventQueue.isEmpty())
220  // we're currently not processing events
221  processEvent(event);
222  else
223  _eventQueue.append(event);
224  }
225 }
226 
227 
228 void EventManager::customEvent(QEvent *event)
229 {
230  if (event->type() == QEvent::User) {
231  QueuedQuasselEvent *queuedEvent = static_cast<QueuedQuasselEvent *>(event);
232  processEvent(queuedEvent->event);
233  event->accept();
234  }
235 }
236 
237 
239 {
240  Q_ASSERT(_eventQueue.isEmpty());
241  dispatchEvent(event);
242  // dispatching the event might cause new events to be generated. we process those afterwards.
243  while (!_eventQueue.isEmpty()) {
244  dispatchEvent(_eventQueue.first());
245  _eventQueue.removeFirst();
246  }
247 }
248 
249 
251 {
252  //qDebug() << "Dispatching" << event;
253 
254  // we try handlers from specialized to generic by masking the enum
255 
256  // build a list sorted by priorities that contains all eligible handlers
257  QList<Handler> handlers;
258  QHash<QObject *, Handler> filters;
259  QSet<QObject *> ignored;
260  uint type = event->type();
261 
262  bool checkDupes = false;
263 
264  // special handling for numeric IrcEvents
265  if ((type & ~IrcEventNumericMask) == IrcEventNumeric) {
266  ::IrcEventNumeric *numEvent = static_cast< ::IrcEventNumeric *>(event);
267  if (!numEvent)
268  qWarning() << "Invalid event type for IrcEventNumeric!";
269  else {
270  int num = numEvent->number();
271  if (num > 0) {
272  insertHandlers(registeredHandlers().value(type + num), handlers, false);
273  insertFilters(registeredFilters().value(type + num), filters);
274  checkDupes = true;
275  }
276  }
277  }
278 
279  // exact type
280  insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
281  insertFilters(registeredFilters().value(type), filters);
282 
283  // check if we have a generic handler for the event group
284  if ((type & EventGroupMask) != type) {
285  insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers, true);
286  insertFilters(registeredFilters().value(type & EventGroupMask), filters);
287  }
288 
289  // now dispatch the event
290  QList<Handler>::const_iterator it;
291  for (it = handlers.begin(); it != handlers.end() && !event->isStopped(); ++it) {
292  QObject *obj = it->object;
293 
294  if (ignored.contains(obj)) // object has filtered the event
295  continue;
296 
297  if (filters.contains(obj)) { // we have a filter, so let's check if we want to deliver the event
298  Handler filter = filters.value(obj);
299  bool result = false;
300  void *param[] = { Q_RETURN_ARG(bool, result).data(), Q_ARG(Event *, event).data() };
301  obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
302  if (!result) {
303  ignored.insert(obj);
304  continue; // mmmh, event filter told us to not accept
305  }
306  }
307 
308  // finally, deliverance!
309  void *param[] = { 0, Q_ARG(Event *, event).data() };
310  obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
311  }
312 
313  // that's it
314  delete event;
315 }
316 
317 
318 void EventManager::insertHandlers(const QList<Handler> &newHandlers, QList<Handler> &existing, bool checkDupes)
319 {
320  foreach(const Handler &handler, newHandlers) {
321  if (existing.isEmpty())
322  existing.append(handler);
323  else {
324  // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
325  bool insert = true;
326  QList<Handler>::iterator insertpos = existing.end();
327  QList<Handler>::iterator it = existing.begin();
328  while (it != existing.end()) {
329  if (checkDupes && handler.object == it->object) {
330  insert = false;
331  break;
332  }
333  if (insertpos == existing.end() && handler.priority > it->priority)
334  insertpos = it;
335 
336  ++it;
337  }
338  if (insert)
339  existing.insert(it, handler);
340  }
341  }
342 }
343 
344 
345 // priority is ignored, and only the first (should be most specialized) filter is being used
346 // fun things could happen if you used the registerEventFilter() methods in the wrong order though
347 void EventManager::insertFilters(const QList<Handler> &newFilters, QHash<QObject *, Handler> &existing)
348 {
349  foreach(const Handler &filter, newFilters) {
350  if (!existing.contains(filter.object))
351  existing[filter.object] = filter;
352  }
353 }
354 
355 
356 QMetaEnum EventManager::_enum;