1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "private/qgesturemanager_p.h"
41#include "private/qstandardgestures_p.h"
42#include "private/qwidget_p.h"
43#include "private/qgesture_p.h"
44#if QT_CONFIG(graphicsview)
45#include "private/qgraphicsitem_p.h"
46#include "qgraphicsitem.h"
47#endif
48#include "private/qevent_p.h"
49#include "private/qapplication_p.h"
50#include "private/qwidgetwindow_p.h"
51#include "qgesture.h"
52#include "qevent.h"
53
54#ifdef Q_OS_MACOS
55#include "qmacgesturerecognizer_p.h"
56#endif
57
58#include "qdebug.h"
59#include <QtCore/QLoggingCategory>
60
61#ifndef QT_NO_GESTURES
62
63QT_BEGIN_NAMESPACE
64
65Q_LOGGING_CATEGORY(lcGestureManager, "qt.widgets.gestures")
66
67#if !defined(Q_OS_MACOS)
68static inline int panTouchPoints()
69{
70 // Override by environment variable for testing.
71 static const char panTouchPointVariable[] = "QT_PAN_TOUCHPOINTS";
72 if (qEnvironmentVariableIsSet(panTouchPointVariable)) {
73 bool ok;
74 const int result = qEnvironmentVariableIntValue(panTouchPointVariable, &ok);
75 if (ok && result >= 1)
76 return result;
77 qWarning("Ignoring invalid value of %s", panTouchPointVariable);
78 }
79 // Pan should use 1 finger on a touch screen and 2 fingers on touch pads etc.
80 // where 1 finger movements are used for mouse event synthetization. For now,
81 // default to 2 until all classes inheriting QScrollArea are fixed to handle it
82 // correctly.
83 return 2;
84}
85#endif
86
87QGestureManager::QGestureManager(QObject *parent)
88 : QObject(parent), state(NotGesture), m_lastCustomGestureId(Qt::CustomGesture)
89{
90 qRegisterMetaType<Qt::GestureState>();
91
92#if defined(Q_OS_MACOS)
93 registerGestureRecognizer(new QMacSwipeGestureRecognizer);
94 registerGestureRecognizer(new QMacPinchGestureRecognizer);
95 registerGestureRecognizer(new QMacPanGestureRecognizer);
96#else
97 registerGestureRecognizer(new QPanGestureRecognizer(panTouchPoints()));
98 registerGestureRecognizer(new QPinchGestureRecognizer);
99 registerGestureRecognizer(new QSwipeGestureRecognizer);
100 registerGestureRecognizer(new QTapGestureRecognizer);
101#endif
102 registerGestureRecognizer(new QTapAndHoldGestureRecognizer);
103}
104
105QGestureManager::~QGestureManager()
106{
107 qDeleteAll(m_recognizers);
108 for (auto it = m_obsoleteGestures.cbegin(), end = m_obsoleteGestures.cend(); it != end; ++it) {
109 qDeleteAll(it.value());
110 delete it.key();
111 }
112}
113
114Qt::GestureType QGestureManager::registerGestureRecognizer(QGestureRecognizer *recognizer)
115{
116 const QScopedPointer<QGesture> dummy(recognizer->create(nullptr));
117 if (Q_UNLIKELY(!dummy)) {
118 qWarning("QGestureManager::registerGestureRecognizer: "
119 "the recognizer fails to create a gesture object, skipping registration.");
120 return Qt::GestureType(0);
121 }
122 Qt::GestureType type = dummy->gestureType();
123 if (type == Qt::CustomGesture) {
124 // generate a new custom gesture id
125 ++m_lastCustomGestureId;
126 type = Qt::GestureType(m_lastCustomGestureId);
127 }
128 m_recognizers.insert(type, recognizer);
129 return type;
130}
131
132void QGestureManager::unregisterGestureRecognizer(Qt::GestureType type)
133{
134 QList<QGestureRecognizer *> list = m_recognizers.values(type);
135 m_recognizers.remove(type);
136 foreach (QGesture *g, m_gestureToRecognizer.keys()) {
137 QGestureRecognizer *recognizer = m_gestureToRecognizer.value(g);
138 if (list.contains(recognizer)) {
139 m_deletedRecognizers.insert(g, recognizer);
140 }
141 }
142
143 QMap<ObjectGesture, QList<QGesture *> >::const_iterator iter = m_objectGestures.constBegin();
144 while (iter != m_objectGestures.constEnd()) {
145 ObjectGesture objectGesture = iter.key();
146 if (objectGesture.gesture == type) {
147 foreach (QGesture *g, iter.value()) {
148 if (QGestureRecognizer *recognizer = m_gestureToRecognizer.value(g)) {
149 m_gestureToRecognizer.remove(g);
150 m_obsoleteGestures[recognizer].insert(g);
151 }
152 }
153 }
154 ++iter;
155 }
156}
157
158void QGestureManager::cleanupCachedGestures(QObject *target, Qt::GestureType type)
159{
160 const auto iter = m_objectGestures.find({target, type});
161 if (iter == m_objectGestures.end())
162 return;
163
164 const QList<QGesture *> &gestures = iter.value();
165 for (auto &e : m_obsoleteGestures) {
166 for (QGesture *g : gestures)
167 e -= g;
168 }
169 for (QGesture *g : gestures) {
170 m_deletedRecognizers.remove(g);
171 m_gestureToRecognizer.remove(g);
172 m_maybeGestures.remove(g);
173 m_activeGestures.remove(g);
174 m_gestureOwners.remove(g);
175 m_gestureTargets.remove(g);
176 m_gesturesToDelete.insert(g);
177 }
178
179 m_objectGestures.erase(iter);
180}
181
182// get or create a QGesture object that will represent the state for a given object, used by the recognizer
183QGesture *QGestureManager::getState(QObject *object, QGestureRecognizer *recognizer, Qt::GestureType type)
184{
185 // if the widget is being deleted we should be careful not to
186 // create a new state, as it will create QWeakPointer which doesn't work
187 // from the destructor.
188 if (object->isWidgetType()) {
189 if (static_cast<QWidget *>(object)->d_func()->data.in_destructor)
190 return nullptr;
191 } else if (QGesture *g = qobject_cast<QGesture *>(object)) {
192 return g;
193#if QT_CONFIG(graphicsview)
194 } else {
195 Q_ASSERT(qobject_cast<QGraphicsObject *>(object));
196 QGraphicsObject *graphicsObject = static_cast<QGraphicsObject *>(object);
197 if (graphicsObject->QGraphicsItem::d_func()->inDestructor)
198 return nullptr;
199#endif
200 }
201
202 // check if the QGesture for this recognizer has already been created
203 const auto states = m_objectGestures.value(QGestureManager::ObjectGesture(object, type));
204 for (QGesture *state : states) {
205 if (m_gestureToRecognizer.value(state) == recognizer)
206 return state;
207 }
208
209 Q_ASSERT(recognizer);
210 QGesture *state = recognizer->create(object);
211 if (!state)
212 return nullptr;
213 state->setParent(this);
214 if (state->gestureType() == Qt::CustomGesture) {
215 // if the recognizer didn't fill in the gesture type, then this
216 // is a custom gesture with autogenerated id and we fill it.
217 state->d_func()->gestureType = type;
218 if (lcGestureManager().isDebugEnabled())
219 state->setObjectName(QString::number((int)type));
220 }
221 m_objectGestures[QGestureManager::ObjectGesture(object, type)].append(state);
222 m_gestureToRecognizer[state] = recognizer;
223 m_gestureOwners[state] = object;
224
225 return state;
226}
227
228static bool logIgnoredEvent(QEvent::Type t)
229{
230 bool result = false;
231 switch (t) {
232 case QEvent::MouseButtonPress:
233 case QEvent::MouseButtonRelease:
234 case QEvent::MouseButtonDblClick:
235 case QEvent::MouseMove:
236 case QEvent::TouchBegin:
237 case QEvent::TouchUpdate:
238 case QEvent::TouchCancel:
239 case QEvent::TouchEnd:
240 case QEvent::TabletEnterProximity:
241 case QEvent::TabletLeaveProximity:
242 case QEvent::TabletMove:
243 case QEvent::TabletPress:
244 case QEvent::TabletRelease:
245 case QEvent::GraphicsSceneMouseDoubleClick:
246 case QEvent::GraphicsSceneMousePress:
247 case QEvent::GraphicsSceneMouseRelease:
248 case QEvent::GraphicsSceneMouseMove:
249 result = true;
250 break;
251 default:
252 break;
253
254 }
255 return result;
256}
257
258bool QGestureManager::filterEventThroughContexts(const QMultiMap<QObject *,
259 Qt::GestureType> &contexts,
260 QEvent *event)
261{
262 QSet<QGesture *> triggeredGestures;
263 QSet<QGesture *> finishedGestures;
264 QSet<QGesture *> newMaybeGestures;
265 QSet<QGesture *> notGestures;
266
267 // TODO: sort contexts by the gesture type and check if one of the contexts
268 // is already active.
269
270 bool consumeEventHint = false;
271
272 // filter the event through recognizers
273 typedef QMultiMap<QObject *, Qt::GestureType>::const_iterator ContextIterator;
274 ContextIterator contextEnd = contexts.end();
275 for (ContextIterator context = contexts.begin(); context != contextEnd; ++context) {
276 Qt::GestureType gestureType = context.value();
277 const QMultiMap<Qt::GestureType, QGestureRecognizer *> &const_recognizers = m_recognizers;
278 QMultiMap<Qt::GestureType, QGestureRecognizer *>::const_iterator
279 typeToRecognizerIterator = const_recognizers.lowerBound(gestureType),
280 typeToRecognizerEnd = const_recognizers.upperBound(gestureType);
281 for (; typeToRecognizerIterator != typeToRecognizerEnd; ++typeToRecognizerIterator) {
282 QGestureRecognizer *recognizer = typeToRecognizerIterator.value();
283 QObject *target = context.key();
284 QGesture *state = getState(target, recognizer, gestureType);
285 if (!state)
286 continue;
287 QGestureRecognizer::Result recognizerResult = recognizer->recognize(state, target, event);
288 QGestureRecognizer::Result recognizerState = recognizerResult & QGestureRecognizer::ResultState_Mask;
289 QGestureRecognizer::Result resultHint = recognizerResult & QGestureRecognizer::ResultHint_Mask;
290 if (recognizerState == QGestureRecognizer::TriggerGesture) {
291 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: gesture triggered: " << state << event;
292 triggeredGestures << state;
293 } else if (recognizerState == QGestureRecognizer::FinishGesture) {
294 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: gesture finished: " << state << event;
295 finishedGestures << state;
296 } else if (recognizerState == QGestureRecognizer::MayBeGesture) {
297 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: maybe gesture: " << state << event;
298 newMaybeGestures << state;
299 } else if (recognizerState == QGestureRecognizer::CancelGesture) {
300 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: not gesture: " << state << event;
301 notGestures << state;
302 } else if (recognizerState == QGestureRecognizer::Ignore) {
303 if (logIgnoredEvent(event->type()))
304 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: ignored the event: " << state << event;
305 } else {
306 if (logIgnoredEvent(event->type())) {
307 qCDebug(lcGestureManager) << "QGestureManager:Recognizer: hm, lets assume the recognizer"
308 << "ignored the event: " << state << event;
309 }
310 }
311 if (resultHint & QGestureRecognizer::ConsumeEventHint) {
312 qCDebug(lcGestureManager) << "QGestureManager: we were asked to consume the event: "
313 << state << event;
314 consumeEventHint = true;
315 }
316 }
317 }
318 if (!triggeredGestures.isEmpty() || !finishedGestures.isEmpty()
319 || !newMaybeGestures.isEmpty() || !notGestures.isEmpty()) {
320 QSet<QGesture *> startedGestures = triggeredGestures - m_activeGestures;
321 triggeredGestures &= m_activeGestures;
322
323 // check if a running gesture switched back to maybe state
324 QSet<QGesture *> activeToMaybeGestures = m_activeGestures & newMaybeGestures;
325
326 // check if a maybe gesture switched to canceled - reset it but don't send an event
327 QSet<QGesture *> maybeToCanceledGestures = m_maybeGestures & notGestures;
328
329 // check if a running gesture switched back to not gesture state,
330 // i.e. were canceled
331 QSet<QGesture *> canceledGestures = m_activeGestures & notGestures;
332
333 // new gestures in maybe state
334 m_maybeGestures += newMaybeGestures;
335
336 // gestures that were in maybe state
337 QSet<QGesture *> notMaybeGestures = (startedGestures | triggeredGestures
338 | finishedGestures | canceledGestures
339 | notGestures);
340 m_maybeGestures -= notMaybeGestures;
341
342 Q_ASSERT((startedGestures & finishedGestures).isEmpty());
343 Q_ASSERT((startedGestures & newMaybeGestures).isEmpty());
344 Q_ASSERT((startedGestures & canceledGestures).isEmpty());
345 Q_ASSERT((finishedGestures & newMaybeGestures).isEmpty());
346 Q_ASSERT((finishedGestures & canceledGestures).isEmpty());
347 Q_ASSERT((canceledGestures & newMaybeGestures).isEmpty());
348
349 QSet<QGesture *> notStarted = finishedGestures - m_activeGestures;
350 if (!notStarted.isEmpty()) {
351 // there are some gestures that claim to be finished, but never started.
352 // probably those are "singleshot" gestures so we'll fake the started state.
353 foreach (QGesture *gesture, notStarted)
354 gesture->d_func()->state = Qt::GestureStarted;
355 QSet<QGesture *> undeliveredGestures;
356 deliverEvents(notStarted, &undeliveredGestures);
357 finishedGestures -= undeliveredGestures;
358 }
359
360 m_activeGestures += startedGestures;
361 // sanity check: all triggered gestures should already be in active gestures list
362 Q_ASSERT((m_activeGestures & triggeredGestures).size() == triggeredGestures.size());
363 m_activeGestures -= finishedGestures;
364 m_activeGestures -= activeToMaybeGestures;
365 m_activeGestures -= canceledGestures;
366
367 // set the proper gesture state on each gesture
368 foreach (QGesture *gesture, startedGestures)
369 gesture->d_func()->state = Qt::GestureStarted;
370 foreach (QGesture *gesture, triggeredGestures)
371 gesture->d_func()->state = Qt::GestureUpdated;
372 foreach (QGesture *gesture, finishedGestures)
373 gesture->d_func()->state = Qt::GestureFinished;
374 foreach (QGesture *gesture, canceledGestures)
375 gesture->d_func()->state = Qt::GestureCanceled;
376 foreach (QGesture *gesture, activeToMaybeGestures)
377 gesture->d_func()->state = Qt::GestureFinished;
378
379 if (!m_activeGestures.isEmpty() || !m_maybeGestures.isEmpty() ||
380 !startedGestures.isEmpty() || !triggeredGestures.isEmpty() ||
381 !finishedGestures.isEmpty() || !canceledGestures.isEmpty()) {
382 qCDebug(lcGestureManager) << "QGestureManager::filterEventThroughContexts:"
383 << "\n\tactiveGestures:" << m_activeGestures
384 << "\n\tmaybeGestures:" << m_maybeGestures
385 << "\n\tstarted:" << startedGestures
386 << "\n\ttriggered:" << triggeredGestures
387 << "\n\tfinished:" << finishedGestures
388 << "\n\tcanceled:" << canceledGestures
389 << "\n\tmaybe-canceled:" << maybeToCanceledGestures;
390 }
391
392 QSet<QGesture *> undeliveredGestures;
393 deliverEvents(startedGestures+triggeredGestures+finishedGestures+canceledGestures,
394 &undeliveredGestures);
395
396 foreach (QGesture *g, startedGestures) {
397 if (undeliveredGestures.contains(g))
398 continue;
399 if (g->gestureCancelPolicy() == QGesture::CancelAllInContext) {
400 qCDebug(lcGestureManager) << "lets try to cancel some";
401 // find gestures in context in Qt::GestureStarted or Qt::GestureUpdated state and cancel them
402 cancelGesturesForChildren(g);
403 }
404 }
405
406 m_activeGestures -= undeliveredGestures;
407
408 // reset gestures that ended
409 QSet<QGesture *> endedGestures =
410 finishedGestures + canceledGestures + undeliveredGestures + maybeToCanceledGestures;
411 foreach (QGesture *gesture, endedGestures) {
412 recycle(gesture);
413 m_gestureTargets.remove(gesture);
414 }
415 }
416 //Clean up the Gestures
417 qDeleteAll(m_gesturesToDelete);
418 m_gesturesToDelete.clear();
419
420 return consumeEventHint;
421}
422
423// Cancel all gestures of children of the widget that original is associated with
424void QGestureManager::cancelGesturesForChildren(QGesture *original)
425{
426 Q_ASSERT(original);
427 QWidget *originatingWidget = m_gestureTargets.value(original);
428 Q_ASSERT(originatingWidget);
429 if (!originatingWidget)
430 return;
431
432 // iterate over all active gestures and all maybe gestures
433 // for each find the owner
434 // if the owner is part of our sub-hierarchy, cancel it.
435
436 QSet<QGesture*> cancelledGestures;
437 QSet<QGesture*>::Iterator iter = m_activeGestures.begin();
438 while (iter != m_activeGestures.end()) {
439 QWidget *widget = m_gestureTargets.value(*iter);
440 // note that we don't touch the gestures for our originatingWidget
441 if (widget != originatingWidget && originatingWidget->isAncestorOf(widget)) {
442 qCDebug(lcGestureManager) << " found a gesture to cancel" << (*iter);
443 (*iter)->d_func()->state = Qt::GestureCanceled;
444 cancelledGestures << *iter;
445 iter = m_activeGestures.erase(iter);
446 } else {
447 ++iter;
448 }
449 }
450
451 // TODO handle 'maybe' gestures too
452
453 // sort them per target widget by cherry picking from almostCanceledGestures and delivering
454 QSet<QGesture *> almostCanceledGestures = cancelledGestures;
455 while (!almostCanceledGestures.isEmpty()) {
456 QWidget *target = nullptr;
457 QSet<QGesture*> gestures;
458 iter = almostCanceledGestures.begin();
459 // sort per target widget
460 while (iter != almostCanceledGestures.end()) {
461 QWidget *widget = m_gestureTargets.value(*iter);
462 if (target == nullptr)
463 target = widget;
464 if (target == widget) {
465 gestures << *iter;
466 iter = almostCanceledGestures.erase(iter);
467 } else {
468 ++iter;
469 }
470 }
471 Q_ASSERT(target);
472
473 QSet<QGesture*> undeliveredGestures;
474 deliverEvents(gestures, &undeliveredGestures);
475 }
476
477 for (iter = cancelledGestures.begin(); iter != cancelledGestures.end(); ++iter)
478 recycle(*iter);
479}
480
481void QGestureManager::cleanupGesturesForRemovedRecognizer(QGesture *gesture)
482{
483 QGestureRecognizer *recognizer = m_deletedRecognizers.value(gesture);
484 if(!recognizer) //The Gesture is removed while in the even loop, so the recognizers for this gestures was removed
485 return;
486 m_deletedRecognizers.remove(gesture);
487 if (m_deletedRecognizers.keys(recognizer).isEmpty()) {
488 // no more active gestures, cleanup!
489 qDeleteAll(m_obsoleteGestures.value(recognizer));
490 m_obsoleteGestures.remove(recognizer);
491 delete recognizer;
492 }
493}
494
495// return true if accepted (consumed)
496bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event)
497{
498 QVarLengthArray<Qt::GestureType, 16> types;
499 QMultiMap<QObject *, Qt::GestureType> contexts;
500 QWidget *w = receiver;
501 typedef QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator ContextIterator;
502 if (!w->d_func()->gestureContext.isEmpty()) {
503 for(ContextIterator it = w->d_func()->gestureContext.constBegin(),
504 e = w->d_func()->gestureContext.constEnd(); it != e; ++it) {
505 types.push_back(it.key());
506 contexts.insert(w, it.key());
507 }
508 }
509 // find all gesture contexts for the widget tree
510 w = w->isWindow() ? nullptr : w->parentWidget();
511 while (w)
512 {
513 for (ContextIterator it = w->d_func()->gestureContext.constBegin(),
514 e = w->d_func()->gestureContext.constEnd(); it != e; ++it) {
515 if (!(it.value() & Qt::DontStartGestureOnChildren)) {
516 if (!types.contains(it.key())) {
517 types.push_back(it.key());
518 contexts.insert(w, it.key());
519 }
520 }
521 }
522 if (w->isWindow())
523 break;
524 w = w->parentWidget();
525 }
526 return contexts.isEmpty() ? false : filterEventThroughContexts(contexts, event);
527}
528
529#if QT_CONFIG(graphicsview)
530bool QGestureManager::filterEvent(QGraphicsObject *receiver, QEvent *event)
531{
532 QVarLengthArray<Qt::GestureType, 16> types;
533 QMultiMap<QObject *, Qt::GestureType> contexts;
534 QGraphicsObject *item = receiver;
535 if (!item->QGraphicsItem::d_func()->gestureContext.isEmpty()) {
536 typedef QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator ContextIterator;
537 for(ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.constBegin(),
538 e = item->QGraphicsItem::d_func()->gestureContext.constEnd(); it != e; ++it) {
539 types.push_back(it.key());
540 contexts.insert(item, it.key());
541 }
542 }
543 // find all gesture contexts for the graphics object tree
544 item = item->parentObject();
545 while (item)
546 {
547 typedef QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator ContextIterator;
548 for (ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.constBegin(),
549 e = item->QGraphicsItem::d_func()->gestureContext.constEnd(); it != e; ++it) {
550 if (!(it.value() & Qt::DontStartGestureOnChildren)) {
551 if (!types.contains(it.key())) {
552 types.push_back(it.key());
553 contexts.insert(item, it.key());
554 }
555 }
556 }
557 item = item->parentObject();
558 }
559 return contexts.isEmpty() ? false : filterEventThroughContexts(contexts, event);
560}
561#endif
562
563bool QGestureManager::filterEvent(QObject *receiver, QEvent *event)
564{
565 // if the receiver is actually a widget, we need to call the correct event
566 // filter method.
567 QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(receiver);
568
569 if (widgetWindow && widgetWindow->widget())
570 return filterEvent(widgetWindow->widget(), event);
571
572 QGesture *state = qobject_cast<QGesture *>(receiver);
573 if (!state || !m_gestureToRecognizer.contains(state))
574 return false;
575 QMultiMap<QObject *, Qt::GestureType> contexts;
576 contexts.insert(state, state->gestureType());
577 return filterEventThroughContexts(contexts, event);
578}
579
580void QGestureManager::getGestureTargets(const QSet<QGesture*> &gestures,
581 QHash<QWidget *, QList<QGesture *> > *conflicts,
582 QHash<QWidget *, QList<QGesture *> > *normal)
583{
584 typedef QHash<Qt::GestureType, QHash<QWidget *, QGesture *> > GestureByTypes;
585 GestureByTypes gestureByTypes;
586
587 // sort gestures by types
588 foreach (QGesture *gesture, gestures) {
589 QWidget *receiver = m_gestureTargets.value(gesture, nullptr);
590 Q_ASSERT(receiver);
591 if (receiver)
592 gestureByTypes[gesture->gestureType()].insert(receiver, gesture);
593 }
594
595 // for each gesture type
596 for (GestureByTypes::const_iterator git = gestureByTypes.cbegin(), gend = gestureByTypes.cend(); git != gend; ++git) {
597 const QHash<QWidget *, QGesture *> &gestures = git.value();
598 for (QHash<QWidget *, QGesture *>::const_iterator wit = gestures.cbegin(), wend = gestures.cend(); wit != wend; ++wit) {
599 QWidget *widget = wit.key();
600 QWidget *w = widget->parentWidget();
601 while (w) {
602 QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator it
603 = w->d_func()->gestureContext.constFind(git.key());
604 if (it != w->d_func()->gestureContext.constEnd()) {
605 // i.e. 'w' listens to gesture 'type'
606 if (!(it.value() & Qt::DontStartGestureOnChildren) && w != widget) {
607 // conflicting gesture!
608 (*conflicts)[widget].append(wit.value());
609 break;
610 }
611 }
612 if (w->isWindow()) {
613 w = nullptr;
614 break;
615 }
616 w = w->parentWidget();
617 }
618 if (!w)
619 (*normal)[widget].append(wit.value());
620 }
621 }
622}
623
624void QGestureManager::deliverEvents(const QSet<QGesture *> &gestures,
625 QSet<QGesture *> *undeliveredGestures)
626{
627 if (gestures.isEmpty())
628 return;
629
630 typedef QHash<QWidget *, QList<QGesture *> > GesturesPerWidget;
631 GesturesPerWidget conflictedGestures;
632 GesturesPerWidget normalStartedGestures;
633
634 QSet<QGesture *> startedGestures;
635 // first figure out the initial receivers of gestures
636 for (QSet<QGesture *>::const_iterator it = gestures.begin(),
637 e = gestures.end(); it != e; ++it) {
638 QGesture *gesture = *it;
639 QWidget *target = m_gestureTargets.value(gesture, nullptr);
640 if (!target) {
641 // the gesture has just started and doesn't have a target yet.
642 Q_ASSERT(gesture->state() == Qt::GestureStarted);
643 if (gesture->hasHotSpot()) {
644 // guess the target widget using the hotspot of the gesture
645 QPoint pt = gesture->hotSpot().toPoint();
646 if (QWidget *topLevel = QApplication::topLevelAt(pt)) {
647 QWidget *child = topLevel->childAt(topLevel->mapFromGlobal(pt));
648 target = child ? child : topLevel;
649 }
650 } else {
651 // or use the context of the gesture
652 QObject *context = m_gestureOwners.value(gesture, 0);
653 if (context->isWidgetType())
654 target = static_cast<QWidget *>(context);
655 }
656 if (target)
657 m_gestureTargets.insert(gesture, target);
658 }
659
660 Qt::GestureType gestureType = gesture->gestureType();
661 Q_ASSERT(gestureType != Qt::CustomGesture);
662 Q_UNUSED(gestureType);
663
664 if (Q_UNLIKELY(!target)) {
665 qCDebug(lcGestureManager) << "QGestureManager::deliverEvent: could not find the target for gesture"
666 << gesture->gestureType();
667 qWarning("QGestureManager::deliverEvent: could not find the target for gesture");
668 undeliveredGestures->insert(gesture);
669 } else {
670 if (gesture->state() == Qt::GestureStarted) {
671 startedGestures.insert(gesture);
672 } else {
673 normalStartedGestures[target].append(gesture);
674 }
675 }
676 }
677
678 getGestureTargets(startedGestures, &conflictedGestures, &normalStartedGestures);
679 qCDebug(lcGestureManager) << "QGestureManager::deliverEvents:"
680 << "\nstarted: " << startedGestures
681 << "\nconflicted: " << conflictedGestures
682 << "\nnormal: " << normalStartedGestures
683 << "\n";
684
685 // if there are conflicting gestures, send the GestureOverride event
686 for (GesturesPerWidget::const_iterator it = conflictedGestures.constBegin(),
687 e = conflictedGestures.constEnd(); it != e; ++it) {
688 QWidget *receiver = it.key();
689 QList<QGesture *> gestures = it.value();
690 qCDebug(lcGestureManager) << "QGestureManager::deliverEvents: sending GestureOverride to"
691 << receiver
692 << "gestures:" << gestures;
693 QGestureEvent event(gestures);
694 event.t = QEvent::GestureOverride;
695 // mark event and individual gestures as ignored
696 event.ignore();
697 foreach(QGesture *g, gestures)
698 event.setAccepted(g, false);
699
700 QCoreApplication::sendEvent(receiver, &event);
701 bool eventAccepted = event.isAccepted();
702 const auto eventGestures = event.gestures();
703 for (QGesture *gesture : eventGestures) {
704 if (eventAccepted || event.isAccepted(gesture)) {
705 QWidget *w = event.m_targetWidgets.value(gesture->gestureType(), 0);
706 Q_ASSERT(w);
707 qCDebug(lcGestureManager) << "override event: gesture was accepted:" << gesture << w;
708 QList<QGesture *> &gestures = normalStartedGestures[w];
709 gestures.append(gesture);
710 // override the target
711 m_gestureTargets[gesture] = w;
712 } else {
713 qCDebug(lcGestureManager) << "override event: gesture wasn't accepted. putting back:" << gesture;
714 QList<QGesture *> &gestures = normalStartedGestures[receiver];
715 gestures.append(gesture);
716 }
717 }
718 }
719
720 // delivering gestures that are not in conflicted state
721 for (GesturesPerWidget::const_iterator it = normalStartedGestures.constBegin(),
722 e = normalStartedGestures.constEnd(); it != e; ++it) {
723 if (!it.value().isEmpty()) {
724 qCDebug(lcGestureManager) << "QGestureManager::deliverEvents: sending to" << it.key()
725 << "gestures:" << it.value();
726 QGestureEvent event(it.value());
727 QCoreApplication::sendEvent(it.key(), &event);
728 bool eventAccepted = event.isAccepted();
729 const auto eventGestures = event.gestures();
730 for (QGesture *gesture : eventGestures) {
731 if (gesture->state() == Qt::GestureStarted &&
732 (eventAccepted || event.isAccepted(gesture))) {
733 QWidget *w = event.m_targetWidgets.value(gesture->gestureType(), 0);
734 Q_ASSERT(w);
735 qCDebug(lcGestureManager) << "started gesture was delivered and accepted by" << w;
736 m_gestureTargets[gesture] = w;
737 }
738 }
739 }
740 }
741}
742
743void QGestureManager::recycle(QGesture *gesture)
744{
745 QGestureRecognizer *recognizer = m_gestureToRecognizer.value(gesture, 0);
746 if (recognizer) {
747 gesture->setGestureCancelPolicy(QGesture::CancelNone);
748 recognizer->reset(gesture);
749 m_activeGestures.remove(gesture);
750 } else {
751 cleanupGesturesForRemovedRecognizer(gesture);
752 }
753}
754
755bool QGestureManager::gesturePending(QObject *o)
756{
757 const QGestureManager *gm = QGestureManager::instance(DontForceCreation);
758 return gm && gm->m_gestureOwners.key(o);
759}
760
761QT_END_NAMESPACE
762
763#endif // QT_NO_GESTURES
764
765#include "moc_qgesturemanager_p.cpp"
766