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 "qgesture.h"
41 #include "qapplication.h"
42 #include "qevent.h"
43 #include "qwidget.h"
44 #if QT_CONFIG(graphicsview)
45 #include "qgraphicsitem.h"
46 #include "qgraphicsscene.h"
47 #include "qgraphicssceneevent.h"
48 #include "qgraphicsview.h"
49 #endif
50 #include "qscroller.h"
51 #include <QtGui/qtouchdevice.h>
52 #include "private/qapplication_p.h"
53 #include "private/qevent_p.h"
54 #include "private/qflickgesture_p.h"
55 #include "qdebug.h"
56 
57 #ifndef QT_NO_GESTURES
58 
59 QT_BEGIN_NAMESPACE
60 
61 //#define QFLICKGESTURE_DEBUG
62 
63 #ifdef QFLICKGESTURE_DEBUG
64 #  define qFGDebug  qDebug
65 #else
66 #  define qFGDebug  while (false) qDebug
67 #endif
68 
69 extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
70 
copyMouseEvent(QEvent * e)71 static QMouseEvent *copyMouseEvent(QEvent *e)
72 {
73     switch (e->type()) {
74     case QEvent::MouseButtonPress:
75     case QEvent::MouseButtonRelease:
76     case QEvent::MouseMove: {
77         QMouseEvent *me = static_cast<QMouseEvent *>(e);
78         QMouseEvent *cme = new QMouseEvent(me->type(), QPoint(0, 0), me->windowPos(), me->screenPos(),
79                                            me->button(), me->buttons(), me->modifiers(), me->source());
80         return cme;
81     }
82 #if QT_CONFIG(graphicsview)
83     case QEvent::GraphicsSceneMousePress:
84     case QEvent::GraphicsSceneMouseRelease:
85     case QEvent::GraphicsSceneMouseMove: {
86         QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(e);
87 #if 1
88         QEvent::Type met = me->type() == QEvent::GraphicsSceneMousePress ? QEvent::MouseButtonPress :
89                            (me->type() == QEvent::GraphicsSceneMouseRelease ? QEvent::MouseButtonRelease : QEvent::MouseMove);
90         QMouseEvent *cme = new QMouseEvent(met, QPoint(0, 0), QPoint(0, 0), me->screenPos(),
91                                            me->button(), me->buttons(), me->modifiers(), me->source());
92         return cme;
93 #else
94         QGraphicsSceneMouseEvent *copy = new QGraphicsSceneMouseEvent(me->type());
95         copy->setPos(me->pos());
96         copy->setScenePos(me->scenePos());
97         copy->setScreenPos(me->screenPos());
98         for (int i = 0x1; i <= 0x10; i <<= 1) {
99             Qt::MouseButton button = Qt::MouseButton(i);
100             copy->setButtonDownPos(button, me->buttonDownPos(button));
101             copy->setButtonDownScenePos(button, me->buttonDownScenePos(button));
102             copy->setButtonDownScreenPos(button, me->buttonDownScreenPos(button));
103         }
104         copy->setLastPos(me->lastPos());
105         copy->setLastScenePos(me->lastScenePos());
106         copy->setLastScreenPos(me->lastScreenPos());
107         copy->setButtons(me->buttons());
108         copy->setButton(me->button());
109         copy->setModifiers(me->modifiers());
110         copy->setSource(me->source());
111         copy->setFlags(me->flags());
112         return copy;
113 #endif
114     }
115 #endif // QT_CONFIG(graphicsview)
116     default:
117         return nullptr;
118     }
119 }
120 
121 class PressDelayHandler : public QObject
122 {
123 private:
PressDelayHandler(QObject * parent=nullptr)124     PressDelayHandler(QObject *parent = nullptr)
125         : QObject(parent)
126         , pressDelayTimer(0)
127         , sendingEvent(false)
128         , mouseButton(Qt::NoButton)
129         , mouseTarget(nullptr)
130         , mouseEventSource(Qt::MouseEventNotSynthesized)
131     { }
132 
133 public:
134     enum {
135         UngrabMouseBefore = 1,
136         RegrabMouseAfterwards = 2
137     };
138 
instance()139     static PressDelayHandler *instance()
140     {
141         static PressDelayHandler *inst = nullptr;
142         if (!inst)
143             inst = new PressDelayHandler(QCoreApplication::instance());
144         return inst;
145     }
146 
shouldEventBeIgnored(QEvent *) const147     bool shouldEventBeIgnored(QEvent *) const
148     {
149         return sendingEvent;
150     }
151 
isDelaying() const152     bool isDelaying() const
153     {
154         return !pressDelayEvent.isNull();
155     }
156 
pressed(QEvent * e,int delay)157     void pressed(QEvent *e, int delay)
158     {
159         if (!pressDelayEvent) {
160             pressDelayEvent.reset(copyMouseEvent(e));
161             pressDelayTimer = startTimer(delay);
162             mouseTarget = QApplication::widgetAt(pressDelayEvent->globalPos());
163             mouseButton = pressDelayEvent->button();
164             mouseEventSource = pressDelayEvent->source();
165             qFGDebug("QFG: consuming/delaying mouse press");
166         } else {
167             qFGDebug("QFG: NOT consuming/delaying mouse press");
168         }
169         e->setAccepted(true);
170     }
171 
released(QEvent * e,bool scrollerWasActive,bool scrollerIsActive)172     bool released(QEvent *e, bool scrollerWasActive, bool scrollerIsActive)
173     {
174         // consume this event if the scroller was or is active
175         bool result = scrollerWasActive || scrollerIsActive;
176 
177         // stop the timer
178         if (pressDelayTimer) {
179             killTimer(pressDelayTimer);
180             pressDelayTimer = 0;
181         }
182         // we still haven't even sent the press, so do it now
183         if (pressDelayEvent && mouseTarget && !scrollerIsActive) {
184             QScopedPointer<QMouseEvent> releaseEvent(copyMouseEvent(e));
185 
186             qFGDebug() << "QFG: re-sending mouse press (due to release) for " << mouseTarget;
187             sendMouseEvent(pressDelayEvent.data(), UngrabMouseBefore);
188 
189             qFGDebug() << "QFG: faking mouse release (due to release) for " << mouseTarget;
190             sendMouseEvent(releaseEvent.data());
191 
192             result = true; // consume this event
193         } else if (mouseTarget && scrollerIsActive) {
194             // we grabbed the mouse expicitly when the scroller became active, so undo that now
195             sendMouseEvent(nullptr, UngrabMouseBefore);
196         }
197         pressDelayEvent.reset(nullptr);
198         mouseTarget = nullptr;
199         return result;
200     }
201 
scrollerWasIntercepted()202     void scrollerWasIntercepted()
203     {
204         qFGDebug("QFG: deleting delayed mouse press, since scroller was only intercepted");
205         if (pressDelayEvent) {
206             // we still haven't even sent the press, so just throw it away now
207             if (pressDelayTimer) {
208                 killTimer(pressDelayTimer);
209                 pressDelayTimer = 0;
210             }
211             pressDelayEvent.reset(nullptr);
212         }
213         mouseTarget = nullptr;
214     }
215 
scrollerBecameActive()216     void scrollerBecameActive()
217     {
218         if (pressDelayEvent) {
219             // we still haven't even sent the press, so just throw it away now
220             qFGDebug("QFG: deleting delayed mouse press, since scroller is active now");
221             if (pressDelayTimer) {
222                 killTimer(pressDelayTimer);
223                 pressDelayTimer = 0;
224             }
225             pressDelayEvent.reset(nullptr);
226             mouseTarget = nullptr;
227         } else if (mouseTarget) {
228             // we did send a press, so we need to fake a release now
229 
230             // release all pressed mouse buttons
231             /* Qt::MouseButtons mouseButtons = QGuiApplication::mouseButtons();
232             for (int i = 0; i < 32; ++i) {
233                 if (mouseButtons & (1 << i)) {
234                     Qt::MouseButton b = static_cast<Qt::MouseButton>(1 << i);
235                     mouseButtons &= ~b;
236                     QPoint farFarAway(-QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
237 
238                     qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << mouseTarget;
239                     QMouseEvent re(QEvent::MouseButtonRelease, QPoint(), farFarAway,
240                                    b, mouseButtons, QGuiApplication::keyboardModifiers());
241                     sendMouseEvent(&re);
242                 }
243             }*/
244 
245             QPoint farFarAway(-QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
246 
247             qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << mouseTarget;
248             QMouseEvent re(QEvent::MouseButtonRelease, QPoint(), farFarAway, farFarAway,
249                            mouseButton, QGuiApplication::mouseButtons() & ~mouseButton,
250                            QGuiApplication::keyboardModifiers(), mouseEventSource);
251             sendMouseEvent(&re, RegrabMouseAfterwards);
252             // don't clear the mouseTarget just yet, since we need to explicitly ungrab the mouse on release!
253         }
254     }
255 
256 protected:
timerEvent(QTimerEvent * e)257     void timerEvent(QTimerEvent *e) override
258     {
259         if (e->timerId() == pressDelayTimer) {
260             if (pressDelayEvent && mouseTarget) {
261                 qFGDebug() << "QFG: timer event: re-sending mouse press to " << mouseTarget;
262                 sendMouseEvent(pressDelayEvent.data(), UngrabMouseBefore);
263             }
264             pressDelayEvent.reset(nullptr);
265 
266             if (pressDelayTimer) {
267                 killTimer(pressDelayTimer);
268                 pressDelayTimer = 0;
269             }
270         }
271     }
272 
sendMouseEvent(QMouseEvent * me,int flags=0)273     void sendMouseEvent(QMouseEvent *me, int flags = 0)
274     {
275         if (mouseTarget) {
276             sendingEvent = true;
277 
278 #if QT_CONFIG(graphicsview)
279             QGraphicsItem *grabber = nullptr;
280             if (mouseTarget->parentWidget()) {
281                 if (QGraphicsView *gv = qobject_cast<QGraphicsView *>(mouseTarget->parentWidget())) {
282                     if (gv->scene())
283                         grabber = gv->scene()->mouseGrabberItem();
284                 }
285             }
286 
287             if (grabber && (flags & UngrabMouseBefore)) {
288                 // GraphicsView Mouse Handling Workaround #1:
289                 // we need to ungrab the mouse before re-sending the press,
290                 // since the scene had already set the mouse grabber to the
291                 // original (and consumed) event's receiver
292                 qFGDebug() << "QFG: ungrabbing" << grabber;
293                 grabber->ungrabMouse();
294             }
295 #else
296             Q_UNUSED(flags);
297 #endif // QT_CONFIG(graphicsview)
298 
299             if (me) {
300                 QMouseEvent copy(me->type(), mouseTarget->mapFromGlobal(me->globalPos()),
301                                  mouseTarget->topLevelWidget()->mapFromGlobal(me->globalPos()), me->screenPos(),
302                                  me->button(), me->buttons(), me->modifiers(), me->source());
303                 qt_sendSpontaneousEvent(mouseTarget, &copy);
304             }
305 
306 #if QT_CONFIG(graphicsview)
307             if (grabber && (flags & RegrabMouseAfterwards)) {
308                 // GraphicsView Mouse Handling Workaround #2:
309                 // we need to re-grab the mouse after sending a faked mouse
310                 // release, since we still need the mouse moves for the gesture
311                 // (the scene will clear the item's mouse grabber status on
312                 // release).
313                 qFGDebug() << "QFG: re-grabbing" << grabber;
314                 grabber->grabMouse();
315             }
316 #endif
317             sendingEvent = false;
318         }
319     }
320 
321 
322 private:
323     int pressDelayTimer;
324     QScopedPointer<QMouseEvent> pressDelayEvent;
325     bool sendingEvent;
326     Qt::MouseButton mouseButton;
327     QPointer<QWidget> mouseTarget;
328     Qt::MouseEventSource mouseEventSource;
329 };
330 
331 
332 /*!
333     \internal
334     \class QFlickGesture
335     \since 4.8
336     \brief The QFlickGesture class describes a flicking gesture made by the user.
337     \ingroup gestures
338     The QFlickGesture is more complex than the QPanGesture that uses QScroller and QScrollerProperties
339     to decide if it is triggered.
340     This gesture is reacting on touch event as compared to the QMouseFlickGesture.
341 
342     \sa {Gestures in Widgets and Graphics View}, QScroller, QScrollerProperties, QMouseFlickGesture
343 */
344 
345 /*!
346     \internal
347 */
QFlickGesture(QObject * receiver,Qt::MouseButton button,QObject * parent)348 QFlickGesture::QFlickGesture(QObject *receiver, Qt::MouseButton button, QObject *parent)
349     : QGesture(*new QFlickGesturePrivate, parent)
350 {
351     d_func()->q_ptr = this;
352     d_func()->receiver = receiver;
353     d_func()->receiverScroller = (receiver && QScroller::hasScroller(receiver)) ? QScroller::scroller(receiver) : nullptr;
354     d_func()->button = button;
355 }
356 
~QFlickGesture()357 QFlickGesture::~QFlickGesture()
358 { }
359 
QFlickGesturePrivate()360 QFlickGesturePrivate::QFlickGesturePrivate()
361     : receiverScroller(nullptr), button(Qt::NoButton), macIgnoreWheel(false)
362 { }
363 
364 
365 //
366 // QFlickGestureRecognizer
367 //
368 
369 
QFlickGestureRecognizer(Qt::MouseButton button)370 QFlickGestureRecognizer::QFlickGestureRecognizer(Qt::MouseButton button)
371 {
372     this->button = button;
373 }
374 
375 /*! \reimp
376  */
create(QObject * target)377 QGesture *QFlickGestureRecognizer::create(QObject *target)
378 {
379 #if QT_CONFIG(graphicsview)
380     QGraphicsObject *go = qobject_cast<QGraphicsObject*>(target);
381     if (go && button == Qt::NoButton) {
382         go->setAcceptTouchEvents(true);
383     }
384 #endif
385     return new QFlickGesture(target, button);
386 }
387 
388 /*! \internal
389     The recognize function detects a touch event suitable to start the attached QScroller.
390     The QFlickGesture will be triggered as soon as the scroller is no longer in the state
391     QScroller::Inactive or QScroller::Pressed. It will be finished or canceled
392     at the next QEvent::TouchEnd.
393     Note that the QScroller might continue scrolling (kinetically) at this point.
394  */
recognize(QGesture * state,QObject * watched,QEvent * event)395 QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state,
396                                                               QObject *watched,
397                                                               QEvent *event)
398 {
399     Q_UNUSED(watched);
400 
401     static QElapsedTimer monotonicTimer;
402     if (!monotonicTimer.isValid())
403         monotonicTimer.start();
404 
405     QFlickGesture *q = static_cast<QFlickGesture *>(state);
406     QFlickGesturePrivate *d = q->d_func();
407 
408     QScroller *scroller = d->receiverScroller;
409     if (!scroller)
410         return Ignore; // nothing to do without a scroller?
411 
412     QWidget *receiverWidget = qobject_cast<QWidget *>(d->receiver);
413 #if QT_CONFIG(graphicsview)
414     QGraphicsObject *receiverGraphicsObject = qobject_cast<QGraphicsObject *>(d->receiver);
415 #endif
416 
417     // this is only set for events that we inject into the event loop via sendEvent()
418     if (PressDelayHandler::instance()->shouldEventBeIgnored(event)) {
419         //qFGDebug() << state << "QFG: ignored event: " << event->type();
420         return Ignore;
421     }
422 
423     const QMouseEvent *me = nullptr;
424 #if QT_CONFIG(graphicsview)
425     const QGraphicsSceneMouseEvent *gsme = nullptr;
426 #endif
427     const QTouchEvent *te = nullptr;
428     QPoint globalPos;
429 
430     // qFGDebug() << "FlickGesture "<<state<<"watched:"<<watched<<"receiver"<<d->receiver<<"event"<<event->type()<<"button"<<button;
431 
432     switch (event->type()) {
433     case QEvent::MouseButtonPress:
434     case QEvent::MouseButtonRelease:
435     case QEvent::MouseMove:
436         if (!receiverWidget)
437             return Ignore;
438         if (button != Qt::NoButton) {
439             me = static_cast<const QMouseEvent *>(event);
440             globalPos = me->globalPos();
441         }
442         break;
443 #if QT_CONFIG(graphicsview)
444     case QEvent::GraphicsSceneMousePress:
445     case QEvent::GraphicsSceneMouseRelease:
446     case QEvent::GraphicsSceneMouseMove:
447         if (!receiverGraphicsObject)
448             return Ignore;
449         if (button != Qt::NoButton) {
450             gsme = static_cast<const QGraphicsSceneMouseEvent *>(event);
451             globalPos = gsme->screenPos();
452         }
453         break;
454 #endif
455     case QEvent::TouchBegin:
456     case QEvent::TouchEnd:
457     case QEvent::TouchUpdate:
458         if (button == Qt::NoButton) {
459             te = static_cast<const QTouchEvent *>(event);
460             if (!te->touchPoints().isEmpty())
461                 globalPos = te->touchPoints().at(0).screenPos().toPoint();
462         }
463         break;
464 
465     // consume all wheel events if the scroller is active
466     case QEvent::Wheel:
467         if (d->macIgnoreWheel || (scroller->state() != QScroller::Inactive))
468             return Ignore | ConsumeEventHint;
469         break;
470 
471     // consume all dbl click events if the scroller is active
472     case QEvent::MouseButtonDblClick:
473         if (scroller->state() != QScroller::Inactive)
474             return Ignore | ConsumeEventHint;
475         break;
476 
477     default:
478         break;
479     }
480 
481     if (!me
482 #if QT_CONFIG(graphicsview)
483         && !gsme
484 #endif
485         && !te) // Neither mouse nor touch
486         return Ignore;
487 
488     // get the current pointer position in local coordinates.
489     QPointF point;
490     QScroller::Input inputType = (QScroller::Input) 0;
491 
492     switch (event->type()) {
493     case QEvent::MouseButtonPress:
494         if (me && me->button() == button && me->buttons() == button) {
495             point = me->globalPos();
496             inputType = QScroller::InputPress;
497         } else if (me) {
498             scroller->stop();
499             return CancelGesture;
500         }
501         break;
502     case QEvent::MouseButtonRelease:
503         if (me && me->button() == button) {
504             point = me->globalPos();
505             inputType = QScroller::InputRelease;
506         }
507         break;
508     case QEvent::MouseMove:
509         if (me && me->buttons() == button) {
510             point = me->globalPos();
511             inputType = QScroller::InputMove;
512         }
513         break;
514 
515 #if QT_CONFIG(graphicsview)
516     case QEvent::GraphicsSceneMousePress:
517         if (gsme && gsme->button() == button && gsme->buttons() == button) {
518             point = gsme->scenePos();
519             inputType = QScroller::InputPress;
520         } else if (gsme) {
521             scroller->stop();
522             return CancelGesture;
523         }
524         break;
525     case QEvent::GraphicsSceneMouseRelease:
526         if (gsme && gsme->button() == button) {
527             point = gsme->scenePos();
528             inputType = QScroller::InputRelease;
529         }
530         break;
531     case QEvent::GraphicsSceneMouseMove:
532         if (gsme && gsme->buttons() == button) {
533             point = gsme->scenePos();
534             inputType = QScroller::InputMove;
535         }
536         break;
537 #endif
538 
539     case QEvent::TouchBegin:
540         inputType = QScroller::InputPress;
541         Q_FALLTHROUGH();
542     case QEvent::TouchEnd:
543         if (!inputType)
544             inputType = QScroller::InputRelease;
545         Q_FALLTHROUGH();
546     case QEvent::TouchUpdate:
547         if (!inputType)
548             inputType = QScroller::InputMove;
549 
550         if (te->device()->type() == QTouchDevice::TouchPad) {
551             if (te->touchPoints().count() != 2)  // 2 fingers on pad
552                 return Ignore;
553 
554             point = te->touchPoints().at(0).startScenePos() +
555                     ((te->touchPoints().at(0).scenePos() - te->touchPoints().at(0).startScenePos()) +
556                      (te->touchPoints().at(1).scenePos() - te->touchPoints().at(1).startScenePos())) / 2;
557         } else { // TouchScreen
558             if (te->touchPoints().count() != 1) // 1 finger on screen
559                 return Ignore;
560 
561             point = te->touchPoints().at(0).scenePos();
562         }
563         break;
564 
565     default:
566         break;
567     }
568 
569     // Check for an active scroller at globalPos
570     if (inputType == QScroller::InputPress) {
571         const auto activeScrollers = QScroller::activeScrollers();
572         for (QScroller *as : activeScrollers) {
573             if (as != scroller) {
574                 QRegion scrollerRegion;
575 
576                 if (QWidget *w = qobject_cast<QWidget *>(as->target())) {
577                     scrollerRegion = QRect(w->mapToGlobal(QPoint(0, 0)), w->size());
578 #if QT_CONFIG(graphicsview)
579                 } else if (QGraphicsObject *go = qobject_cast<QGraphicsObject *>(as->target())) {
580                     if (const auto *scene = go->scene()) {
581                         const auto goBoundingRectMappedToScene = go->mapToScene(go->boundingRect());
582                         const auto views = scene->views();
583                         for (QGraphicsView *gv : views) {
584                             scrollerRegion |= gv->mapFromScene(goBoundingRectMappedToScene)
585                                               .translated(gv->mapToGlobal(QPoint(0, 0)));
586                         }
587                     }
588 #endif
589                 }
590                 // active scrollers always have priority
591                 if (scrollerRegion.contains(globalPos))
592                     return Ignore;
593             }
594         }
595     }
596 
597     bool scrollerWasDragging = (scroller->state() == QScroller::Dragging);
598     bool scrollerWasScrolling = (scroller->state() == QScroller::Scrolling);
599 
600     if (inputType) {
601         if (QWidget *w = qobject_cast<QWidget *>(d->receiver))
602             point = w->mapFromGlobal(point.toPoint());
603 #if QT_CONFIG(graphicsview)
604         else if (QGraphicsObject *go = qobject_cast<QGraphicsObject *>(d->receiver))
605             point = go->mapFromScene(point);
606 #endif
607 
608         // inform the scroller about the new event
609         scroller->handleInput(inputType, point, monotonicTimer.elapsed());
610     }
611 
612     // depending on the scroller state return the gesture state
613     Result result;
614     bool scrollerIsActive = (scroller->state() == QScroller::Dragging ||
615                              scroller->state() == QScroller::Scrolling);
616 
617     // Consume all mouse events while dragging or scrolling to avoid nasty
618     // side effects with Qt's standard widgets.
619     if ((me
620 #if QT_CONFIG(graphicsview)
621          || gsme
622 #endif
623          ) && scrollerIsActive)
624         result |= ConsumeEventHint;
625 
626     // The only problem with this approach is that we consume the
627     // MouseRelease when we start the scrolling with a flick gesture, so we
628     // have to fake a MouseRelease "somewhere" to not mess with the internal
629     // states of Qt's widgets (a QPushButton would stay in 'pressed' state
630     // forever, if it doesn't receive a MouseRelease).
631     if (me
632 #if QT_CONFIG(graphicsview)
633         || gsme
634 #endif
635         ) {
636         if (!scrollerWasDragging && !scrollerWasScrolling && scrollerIsActive)
637             PressDelayHandler::instance()->scrollerBecameActive();
638         else if (scrollerWasScrolling && (scroller->state() == QScroller::Dragging || scroller->state() == QScroller::Inactive))
639             PressDelayHandler::instance()->scrollerWasIntercepted();
640     }
641 
642     if (!inputType) {
643         result |= Ignore;
644     } else {
645         switch (event->type()) {
646         case QEvent::MouseButtonPress:
647 #if QT_CONFIG(graphicsview)
648         case QEvent::GraphicsSceneMousePress:
649 #endif
650             if (scroller->state() == QScroller::Pressed) {
651                 int pressDelay = int(1000 * scroller->scrollerProperties().scrollMetric(QScrollerProperties::MousePressEventDelay).toReal());
652                 if (pressDelay > 0) {
653                     result |= ConsumeEventHint;
654 
655                     PressDelayHandler::instance()->pressed(event, pressDelay);
656                     event->accept();
657                 }
658             }
659             Q_FALLTHROUGH();
660         case QEvent::TouchBegin:
661             q->setHotSpot(globalPos);
662             result |= scrollerIsActive ? TriggerGesture : MayBeGesture;
663             break;
664 
665         case QEvent::MouseMove:
666 #if QT_CONFIG(graphicsview)
667         case QEvent::GraphicsSceneMouseMove:
668 #endif
669             if (PressDelayHandler::instance()->isDelaying())
670                 result |= ConsumeEventHint;
671             Q_FALLTHROUGH();
672         case QEvent::TouchUpdate:
673             result |= scrollerIsActive ? TriggerGesture : Ignore;
674             break;
675 
676 #if QT_CONFIG(graphicsview)
677         case QEvent::GraphicsSceneMouseRelease:
678 #endif
679         case QEvent::MouseButtonRelease:
680             if (PressDelayHandler::instance()->released(event, scrollerWasDragging || scrollerWasScrolling, scrollerIsActive))
681                 result |= ConsumeEventHint;
682             Q_FALLTHROUGH();
683         case QEvent::TouchEnd:
684             result |= scrollerIsActive ? FinishGesture : CancelGesture;
685             break;
686 
687         default:
688             result |= Ignore;
689             break;
690         }
691     }
692     return result;
693 }
694 
695 
696 /*! \reimp
697  */
reset(QGesture * state)698 void QFlickGestureRecognizer::reset(QGesture *state)
699 {
700     QGestureRecognizer::reset(state);
701 }
702 
703 QT_END_NAMESPACE
704 
705 #include "moc_qflickgesture_p.cpp"
706 
707 #endif // QT_NO_GESTURES
708