Handle MouseArea.enabled = false after mouse is pressed.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickmousearea.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickmousearea_p.h"
43 #include "qquickmousearea_p_p.h"
44 #include "qquickcanvas.h"
45 #include "qquickevents_p_p.h"
46 #include "qquickdrag_p.h"
47
48 #include <QtGui/qevent.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qstylehints.h>
51
52 #include <float.h>
53
54 QT_BEGIN_NAMESPACE
55
56 DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
57
58 static const int PressAndHoldDelay = 800;
59
60 QQuickDrag::QQuickDrag(QObject *parent)
61 : QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX),
62 _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false)
63 {
64 }
65
66 QQuickDrag::~QQuickDrag()
67 {
68 }
69
70 QQuickItem *QQuickDrag::target() const
71 {
72     return _target;
73 }
74
75 void QQuickDrag::setTarget(QQuickItem *t)
76 {
77     if (_target == t)
78         return;
79     _target = t;
80     emit targetChanged();
81 }
82
83 void QQuickDrag::resetTarget()
84 {
85     if (_target == 0)
86         return;
87     _target = 0;
88     emit targetChanged();
89 }
90
91 QQuickDrag::Axis QQuickDrag::axis() const
92 {
93     return _axis;
94 }
95
96 void QQuickDrag::setAxis(QQuickDrag::Axis a)
97 {
98     if (_axis == a)
99         return;
100     _axis = a;
101     emit axisChanged();
102 }
103
104 qreal QQuickDrag::xmin() const
105 {
106     return _xmin;
107 }
108
109 void QQuickDrag::setXmin(qreal m)
110 {
111     if (_xmin == m)
112         return;
113     _xmin = m;
114     emit minimumXChanged();
115 }
116
117 qreal QQuickDrag::xmax() const
118 {
119     return _xmax;
120 }
121
122 void QQuickDrag::setXmax(qreal m)
123 {
124     if (_xmax == m)
125         return;
126     _xmax = m;
127     emit maximumXChanged();
128 }
129
130 qreal QQuickDrag::ymin() const
131 {
132     return _ymin;
133 }
134
135 void QQuickDrag::setYmin(qreal m)
136 {
137     if (_ymin == m)
138         return;
139     _ymin = m;
140     emit minimumYChanged();
141 }
142
143 qreal QQuickDrag::ymax() const
144 {
145     return _ymax;
146 }
147
148 void QQuickDrag::setYmax(qreal m)
149 {
150     if (_ymax == m)
151         return;
152     _ymax = m;
153     emit maximumYChanged();
154 }
155
156 bool QQuickDrag::active() const
157 {
158     return _active;
159 }
160
161 void QQuickDrag::setActive(bool drag)
162 {
163     if (_active == drag)
164         return;
165     _active = drag;
166     emit activeChanged();
167 }
168
169 bool QQuickDrag::filterChildren() const
170 {
171     return _filterChildren;
172 }
173
174 void QQuickDrag::setFilterChildren(bool filter)
175 {
176     if (_filterChildren == filter)
177         return;
178     _filterChildren = filter;
179     emit filterChildrenChanged();
180 }
181
182 QQuickDragAttached *QQuickDrag::qmlAttachedProperties(QObject *obj)
183 {
184     return new QQuickDragAttached(obj);
185 }
186
187 QQuickMouseAreaPrivate::QQuickMouseAreaPrivate()
188 : absorb(true), hovered(false), pressed(false), longPress(false),
189   moved(false), stealMouse(false), doubleClick(false), preventStealing(false),
190   propagateComposedEvents(false), drag(0)
191 {
192 }
193
194 QQuickMouseAreaPrivate::~QQuickMouseAreaPrivate()
195 {
196     delete drag;
197 }
198
199 void QQuickMouseAreaPrivate::init()
200 {
201     Q_Q(QQuickMouseArea);
202     q->setAcceptedMouseButtons(Qt::LeftButton);
203     q->setFiltersChildMouseEvents(true);
204     if (qmlVisualTouchDebugging()) {
205         q->setFlag(QQuickItem::ItemHasContents);
206     }
207 }
208
209 void QQuickMouseAreaPrivate::saveEvent(QMouseEvent *event)
210 {
211     lastPos = event->localPos();
212     lastScenePos = event->windowPos();
213     lastButton = event->button();
214     lastButtons = event->buttons();
215     lastModifiers = event->modifiers();
216 }
217
218 bool QQuickMouseAreaPrivate::isPressAndHoldConnected()
219 {
220     Q_Q(QQuickMouseArea);
221     static int idx = QObjectPrivate::get(q)->signalIndex("pressAndHold(QQuickMouseEvent*)");
222     return QObjectPrivate::get(q)->isSignalConnected(idx);
223 }
224
225 bool QQuickMouseAreaPrivate::isDoubleClickConnected()
226 {
227     Q_Q(QQuickMouseArea);
228     static int idx = QObjectPrivate::get(q)->signalIndex("doubleClicked(QQuickMouseEvent*)");
229     return QObjectPrivate::get(q)->isSignalConnected(idx);
230 }
231
232 bool QQuickMouseAreaPrivate::isClickConnected()
233 {
234     Q_Q(QQuickMouseArea);
235     static int idx = QObjectPrivate::get(q)->signalIndex("clicked(QQuickMouseEvent*)");
236     return QObjectPrivate::get(q)->isSignalConnected(idx);
237 }
238
239 void QQuickMouseAreaPrivate::propagate(QQuickMouseEvent* event, PropagateType t)
240 {
241     Q_Q(QQuickMouseArea);
242     if (!propagateComposedEvents)
243         return;
244     QPointF scenePos = q->mapToScene(QPointF(event->x(), event->y()));
245     propagateHelper(event, canvas->rootItem(), scenePos, t);
246 }
247
248 bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *item,const QPointF &sp, PropagateType sig)
249 {
250     //Based off of QQuickCanvas::deliverInitialMousePressEvent
251     //But specific to MouseArea, so doesn't belong in canvas
252     Q_Q(const QQuickMouseArea);
253     QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
254     if (itemPrivate->opacity() == 0.0)
255         return false;
256
257     if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
258         QPointF p = item->mapFromScene(sp);
259         if (!QRectF(0, 0, item->width(), item->height()).contains(p))
260             return false;
261     }
262
263     QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
264     for (int ii = children.count() - 1; ii >= 0; --ii) {
265         QQuickItem *child = children.at(ii);
266         if (!child->isVisible() || !child->isEnabled())
267             continue;
268         if (propagateHelper(ev, child, sp, sig))
269             return true;
270     }
271
272     QQuickMouseArea* ma = qobject_cast<QQuickMouseArea*>(item);
273     if (ma && ma != q && itemPrivate->acceptedMouseButtons() & ev->button()) {
274         switch (sig) {
275         case Click:
276             if (!ma->d_func()->isClickConnected())
277                 return false;
278             break;
279         case DoubleClick:
280             if (!ma->d_func()->isDoubleClickConnected())
281                 return false;
282             break;
283         case PressAndHold:
284             if (!ma->d_func()->isPressAndHoldConnected())
285                 return false;
286             break;
287         }
288         QPointF p = item->mapFromScene(sp);
289         if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
290             ev->setX(p.x());
291             ev->setY(p.y());
292             ev->setAccepted(true);//It is connected, they have to explicitly ignore to let it slide
293             switch (sig) {
294             case Click: emit ma->clicked(ev); break;
295             case DoubleClick: emit ma->doubleClicked(ev); break;
296             case PressAndHold: emit ma->pressAndHold(ev); break;
297             }
298             if (ev->isAccepted())
299                 return true;
300         }
301     }
302     return false;
303
304 }
305
306 /*!
307     \qmlclass MouseArea QQuickMouseArea
308     \inqmlmodule QtQuick 2
309     \ingroup qml-basic-interaction-elements
310     \brief The MouseArea item enables simple mouse handling.
311     \inherits Item
312
313     A MouseArea is an invisible item that is typically used in conjunction with
314     a visible item in order to provide mouse handling for that item.
315     By effectively acting as a proxy, the logic for mouse handling can be
316     contained within a MouseArea item.
317
318     For basic key handling, see the \l{Keys}{Keys attached property}.
319
320     The \l enabled property is used to enable and disable mouse handling for
321     the proxied item. When disabled, the mouse area becomes transparent to
322     mouse events.
323
324     The \l pressed read-only property indicates whether or not the user is
325     holding down a mouse button over the mouse area. This property is often
326     used in bindings between properties in a user interface. The containsMouse
327     read-only property indicates the presence of the mouse cursor over the
328     mouse area but, by default, only when a mouse button is held down; see below
329     for further details.
330
331     Information about the mouse position and button clicks are provided via
332     signals for which event handler properties are defined. The most commonly
333     used involved handling mouse presses and clicks: onClicked, onDoubleClicked,
334     onPressed, onReleased and onPressAndHold.
335
336     By default, MouseArea items only report mouse clicks and not changes to the
337     position of the mouse cursor. Setting the hoverEnabled property ensures that
338     handlers defined for onPositionChanged, onEntered and onExited are used and
339     that the containsMouse property is updated even when no mouse buttons are
340     pressed.
341
342     \section1 Example Usage
343
344     \div {class="float-right"}
345     \inlineimage qml-mousearea-snippet.png
346     \enddiv
347
348     The following example uses a MouseArea in a \l Rectangle that changes
349     the \l Rectangle color to red when clicked:
350
351     \snippet doc/src/snippets/qml/mousearea/mousearea.qml import
352     \codeline
353     \snippet doc/src/snippets/qml/mousearea/mousearea.qml intro
354
355     \clearfloat
356     Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains
357     additional information about the mouse event, such as the position, button,
358     and any key modifiers.
359
360     Here is an extension of the previous example that produces a different
361     color when the area is right clicked:
362
363     \snippet doc/src/snippets/qml/mousearea/mousearea.qml intro-extended
364
365   Behavioral Change in QtQuick 2.0
366
367   From QtQuick 2.0, the signals clicked, doubleClicked and pressAndHold have a different interaction
368   model with regards to the delivery of events to multiple overlapping MouseAreas. These signals can now propagate
369   to all MouseAreas in the area, in painting order, until accepted by one of them. A signal is accepted by
370   default if there is a signal handler for it, use mouse.accepted = false; to ignore. This propagation
371   can send the signal to MouseAreas other than the one which accepted the press event, although that MouseArea
372   will receive the signal first. This behavior can be enabled by setting propagateComposedEvents to true.
373
374     \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example}
375 */
376
377 /*!
378     \qmlsignal QtQuick2::MouseArea::onEntered()
379
380     This handler is called when the mouse enters the mouse area.
381
382     By default the onEntered handler is only called while a button is
383     pressed. Setting hoverEnabled to true enables handling of
384     onEntered when no mouse button is pressed.
385
386     \sa hoverEnabled
387 */
388
389 /*!
390     \qmlsignal QtQuick2::MouseArea::onExited()
391
392     This handler is called when the mouse exits the mouse area.
393
394     By default the onExited handler is only called while a button is
395     pressed. Setting hoverEnabled to true enables handling of
396     onExited when no mouse button is pressed.
397
398     The example below shows a fairly typical relationship between
399     two MouseAreas, with \c mouseArea2 on top of \c mouseArea1. Moving the
400     mouse into \c mouseArea2 from \c mouseArea1 will cause \c onExited
401     to be called for \c mouseArea1.
402     \qml
403     Rectangle {
404         width: 400; height: 400
405         MouseArea {
406             id: mouseArea1
407             anchors.fill: parent
408             hoverEnabled: true
409         }
410         MouseArea {
411             id: mouseArea2
412             width: 100; height: 100
413             anchors.centerIn: parent
414             hoverEnabled: true
415         }
416     }
417     \endqml
418
419     If instead you give the two mouseAreas a parent-child relationship,
420     moving the mouse into \c mouseArea2 from \c mouseArea1 will \b not
421     cause \c onExited to be called for \c mouseArea1. Instead, they will
422     both be considered to be simultaneously hovered.
423
424     \sa hoverEnabled
425 */
426
427 /*!
428     \qmlsignal QtQuick2::MouseArea::onPositionChanged(MouseEvent mouse)
429
430     This handler is called when the mouse position changes.
431
432     The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y
433     position, and any buttons currently pressed.
434
435     The \e accepted property of the MouseEvent parameter is ignored in this handler.
436
437     By default the onPositionChanged handler is only called while a button is
438     pressed.  Setting hoverEnabled to true enables handling of
439     onPositionChanged when no mouse button is pressed.
440 */
441
442 /*!
443     \qmlsignal QtQuick2::MouseArea::onClicked(MouseEvent mouse)
444
445     This handler is called when there is a click. A click is defined as a press followed by a release,
446     both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and
447     releasing is also considered a click).
448
449     The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
450     position of the release of the click, and whether the click was held.
451
452     The \e accepted property of the MouseEvent parameter is ignored in this handler.
453 */
454
455 /*!
456     \qmlsignal QtQuick2::MouseArea::onPressed(MouseEvent mouse)
457
458     This handler is called when there is a press.
459     The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
460     position and which button was pressed.
461
462     The \e accepted property of the MouseEvent parameter determines whether this MouseArea
463     will handle the press and all future mouse events until release.  The default is to accept
464     the event and not allow other MouseArea beneath this one to handle the event.  If \e accepted
465     is set to false, no further events will be sent to this MouseArea until the button is next
466     pressed.
467 */
468
469 /*!
470     \qmlsignal QtQuick2::MouseArea::onReleased(MouseEvent mouse)
471
472     This handler is called when there is a release.
473     The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
474     position of the release of the click, and whether the click was held.
475
476     The \e accepted property of the MouseEvent parameter is ignored in this handler.
477
478     \sa onCanceled
479 */
480
481 /*!
482     \qmlsignal QtQuick2::MouseArea::onPressAndHold(MouseEvent mouse)
483
484     This handler is called when there is a long press (currently 800ms).
485     The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
486     position of the press, and which button is pressed.
487
488     The \e accepted property of the MouseEvent parameter is ignored in this handler.
489 */
490
491 /*!
492     \qmlsignal QtQuick2::MouseArea::onDoubleClicked(MouseEvent mouse)
493
494     This handler is called when there is a double-click (a press followed by a release followed by a press).
495     The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
496     position of the release of the click, and whether the click was held.
497
498     If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false
499     in the handler, the onPressed/onReleased/onClicked handlers will be called for the second
500     click; otherwise they are suppressed.  The accepted property defaults to true.
501 */
502
503 /*!
504     \qmlsignal QtQuick2::MouseArea::onCanceled()
505
506     This handler is called when mouse events have been canceled, either because an event was not accepted, or
507     because another element stole the mouse event handling.
508
509     This signal is for advanced use: it is useful when there is more than one MouseArea
510     that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter
511     case, if you execute some logic on the pressed signal and then start dragging, the
512     \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset
513     the logic when the MouseArea has lost the mouse handling to the \l Flickable,
514     \c onCanceled should be used in addition to onReleased.
515 */
516 QQuickMouseArea::QQuickMouseArea(QQuickItem *parent)
517   : QQuickItem(*(new QQuickMouseAreaPrivate), parent)
518 {
519     Q_D(QQuickMouseArea);
520     d->init();
521 }
522
523 QQuickMouseArea::~QQuickMouseArea()
524 {
525 }
526
527 /*!
528     \qmlproperty real QtQuick2::MouseArea::mouseX
529     \qmlproperty real QtQuick2::MouseArea::mouseY
530     These properties hold the coordinates of the mouse cursor.
531
532     If the hoverEnabled property is false then these properties will only be valid
533     while a button is pressed, and will remain valid as long as the button is held
534     down even if the mouse is moved outside the area.
535
536     By default, this property is false.
537
538     If hoverEnabled is true then these properties will be valid when:
539     \list
540         \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true).
541         \i a button is pressed and held, even if it has since moved out of the area.
542     \endlist
543
544     The coordinates are relative to the MouseArea.
545 */
546 qreal QQuickMouseArea::mouseX() const
547 {
548     Q_D(const QQuickMouseArea);
549     return d->lastPos.x();
550 }
551
552 qreal QQuickMouseArea::mouseY() const
553 {
554     Q_D(const QQuickMouseArea);
555     return d->lastPos.y();
556 }
557
558 /*!
559     \qmlproperty bool QtQuick2::MouseArea::enabled
560     This property holds whether the item accepts mouse events.
561
562     By default, this property is true.
563 */
564 bool QQuickMouseArea::isEnabled() const
565 {
566     Q_D(const QQuickMouseArea);
567     return d->absorb;
568 }
569
570 void QQuickMouseArea::setEnabled(bool a)
571 {
572     Q_D(QQuickMouseArea);
573     if (a != d->absorb) {
574         d->absorb = a;
575         emit enabledChanged();
576     }
577 }
578
579 /*!
580     \qmlproperty bool QtQuick2::MouseArea::preventStealing
581     This property holds whether the mouse events may be stolen from this
582     MouseArea.
583
584     If a MouseArea is placed within an item that filters child mouse
585     events, such as Flickable, the mouse
586     events may be stolen from the MouseArea if a gesture is recognized
587     by the parent element, e.g. a flick gesture.  If preventStealing is
588     set to true, no element will steal the mouse events.
589
590     Note that setting preventStealing to true once an element has started
591     stealing events will have no effect until the next press event.
592
593     By default this property is false.
594 */
595 bool QQuickMouseArea::preventStealing() const
596 {
597     Q_D(const QQuickMouseArea);
598     return d->preventStealing;
599 }
600
601 void QQuickMouseArea::setPreventStealing(bool prevent)
602 {
603     Q_D(QQuickMouseArea);
604     if (prevent != d->preventStealing) {
605         d->preventStealing = prevent;
606         setKeepMouseGrab(d->preventStealing && d->absorb);
607         emit preventStealingChanged();
608     }
609 }
610
611
612 /*!
613     \qmlproperty bool QtQuick2::MouseArea::propagateComposedEvents
614     This property holds whether composed mouse events will automatically propagate to
615     other MouseAreas.
616
617     MouseArea contains several composed events, clicked, doubleClicked,
618     and pressAndHold. These can propagate via a separate mechanism to basic
619     mouse events, like pressed, which they are composed of.
620
621     If propagateComposedEvents is set to true, then composed events will be automatically
622     propagated to other MouseAreas in the same location in the scene. They are propagated
623     in painting order until an item accepts them. Unlike pressed handling, events will
624     not be automatically accepted if no handler is present.
625
626     This property greatly simplifies the usecase of when you want to have overlapping MouseAreas
627     handling the composed events together. For example: if you want one MouseArea to handle click
628     signals and the other to handle pressAndHold, or if you want one MouseArea to handle click most
629     of the time, but pass it through when certain conditions are met.
630
631     By default this property is false.
632 */
633 bool QQuickMouseArea::propagateComposedEvents() const
634 {
635     Q_D(const QQuickMouseArea);
636     return d->propagateComposedEvents;
637 }
638
639 void QQuickMouseArea::setPropagateComposedEvents(bool prevent)
640 {
641     Q_D(QQuickMouseArea);
642     if (prevent != d->propagateComposedEvents) {
643         d->propagateComposedEvents = prevent;
644         setKeepMouseGrab(d->propagateComposedEvents && d->absorb);
645         emit propagateComposedEventsChanged();
646     }
647 }
648
649 /*!
650     \qmlproperty MouseButtons QtQuick2::MouseArea::pressedButtons
651     This property holds the mouse buttons currently pressed.
652
653     It contains a bitwise combination of:
654     \list
655     \o Qt.LeftButton
656     \o Qt.RightButton
657     \o Qt.MiddleButton
658     \endlist
659
660     The code below displays "right" when the right mouse buttons is pressed:
661
662     \snippet doc/src/snippets/qml/mousearea/mousearea.qml mousebuttons
663
664     \sa acceptedButtons
665 */
666 Qt::MouseButtons QQuickMouseArea::pressedButtons() const
667 {
668     Q_D(const QQuickMouseArea);
669     return d->lastButtons;
670 }
671
672 void QQuickMouseArea::mousePressEvent(QMouseEvent *event)
673 {
674     Q_D(QQuickMouseArea);
675     d->moved = false;
676     d->stealMouse = d->preventStealing;
677     if (!d->absorb)
678         QQuickItem::mousePressEvent(event);
679     else {
680         d->longPress = false;
681         d->saveEvent(event);
682         if (d->drag) {
683             d->dragX = drag()->axis() & QQuickDrag::XAxis;
684             d->dragY = drag()->axis() & QQuickDrag::YAxis;
685         }
686         if (d->drag)
687             d->drag->setActive(false);
688         setHovered(true);
689         d->startScene = event->windowPos();
690         d->pressAndHoldTimer.start(PressAndHoldDelay, this);
691         setKeepMouseGrab(d->stealMouse);
692         event->setAccepted(setPressed(true));
693
694     }
695 }
696
697 void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
698 {
699     Q_D(QQuickMouseArea);
700     if (!d->absorb && !d->pressed) {
701         QQuickItem::mouseMoveEvent(event);
702         return;
703     }
704
705     d->saveEvent(event);
706
707     // ### we should skip this if these signals aren't used
708     // ### can GV handle this for us?
709     bool contains = boundingRect().contains(d->lastPos);
710     if (d->hovered && !contains)
711         setHovered(false);
712     else if (!d->hovered && contains)
713         setHovered(true);
714
715     if (d->drag && d->drag->target()) {
716         if (!d->moved) {
717             d->targetStartPos = d->drag->target()->parentItem()
718                     ? d->drag->target()->parentItem()->mapToScene(d->drag->target()->pos())
719                     : d->drag->target()->pos();
720         }
721
722         QPointF startLocalPos;
723         QPointF curLocalPos;
724         if (drag()->target()->parentItem()) {
725             startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene);
726             curLocalPos = drag()->target()->parentItem()->mapFromScene(event->windowPos());
727         } else {
728             startLocalPos = d->startScene;
729             curLocalPos = event->windowPos();
730         }
731
732         const int dragThreshold = qApp->styleHints()->startDragDistance();
733         qreal dx = qAbs(curLocalPos.x() - startLocalPos.x());
734         qreal dy = qAbs(curLocalPos.y() - startLocalPos.y());
735
736         if (keepMouseGrab() && d->stealMouse && !d->drag->active())
737             d->drag->setActive(true);
738
739         QPointF startPos = d->drag->target()->parentItem()
740                 ? d->drag->target()->parentItem()->mapFromScene(d->targetStartPos)
741                 : d->targetStartPos;
742
743         QPointF dragPos = d->drag->target()->pos();
744
745         if (d->dragX && d->drag->active()) {
746             qreal x = (curLocalPos.x() - startLocalPos.x()) + startPos.x();
747             if (x < drag()->xmin())
748                 x = drag()->xmin();
749             else if (x > drag()->xmax())
750                 x = drag()->xmax();
751             dragPos.setX(x);
752         }
753         if (d->dragY && d->drag->active()) {
754             qreal y = (curLocalPos.y() - startLocalPos.y()) + startPos.y();
755             if (y < drag()->ymin())
756                 y = drag()->ymin();
757             else if (y > drag()->ymax())
758                 y = drag()->ymax();
759             dragPos.setY(y);
760         }
761         d->drag->target()->setPos(dragPos);
762
763         if (!keepMouseGrab()) {
764             if ((!d->dragY && dy < dragThreshold && d->dragX && dx > dragThreshold)
765                 || (!d->dragX && dx < dragThreshold && d->dragY && dy > dragThreshold)
766                 || (d->dragX && d->dragY && (dx > dragThreshold || dy > dragThreshold))) {
767                 setKeepMouseGrab(true);
768                 d->stealMouse = true;
769             }
770         }
771
772         d->moved = true;
773     }
774     QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
775     emit mouseXChanged(&me);
776     me.setPosition(d->lastPos);
777     emit mouseYChanged(&me);
778     me.setPosition(d->lastPos);
779     emit positionChanged(&me);
780 }
781
782 void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event)
783 {
784     Q_D(QQuickMouseArea);
785     d->stealMouse = false;
786     if (!d->absorb && !d->pressed) {
787         QQuickItem::mouseReleaseEvent(event);
788     } else {
789         d->saveEvent(event);
790         setPressed(false);
791         if (d->drag)
792             d->drag->setActive(false);
793         // If we don't accept hover, we need to reset containsMouse.
794         if (!acceptHoverEvents())
795             setHovered(false);
796         QQuickCanvas *c = canvas();
797         if (c && c->mouseGrabberItem() == this)
798             ungrabMouse();
799         setKeepMouseGrab(false);
800
801     }
802     d->doubleClick = false;
803 }
804
805 void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event)
806 {
807     Q_D(QQuickMouseArea);
808     if (d->absorb) {
809         d->saveEvent(event);
810         QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
811         me.setAccepted(d->isDoubleClickConnected());
812         emit this->doubleClicked(&me);
813         if (!me.isAccepted())
814             d->propagate(&me, QQuickMouseAreaPrivate::DoubleClick);
815         d->doubleClick = d->isDoubleClickConnected() || me.isAccepted();
816     }
817     QQuickItem::mouseDoubleClickEvent(event);
818 }
819
820 void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event)
821 {
822     Q_D(QQuickMouseArea);
823     if (!d->absorb && !d->pressed) {
824         QQuickItem::hoverEnterEvent(event);
825     } else {
826         d->lastPos = event->posF();
827         d->lastModifiers = event->modifiers();
828         setHovered(true);
829         QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
830         emit mouseXChanged(&me);
831         me.setPosition(d->lastPos);
832         emit mouseYChanged(&me);
833         me.setPosition(d->lastPos);
834     }
835 }
836
837 void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event)
838 {
839     Q_D(QQuickMouseArea);
840     if (!d->absorb && !d->pressed) {
841         QQuickItem::hoverMoveEvent(event);
842     } else {
843         d->lastPos = event->posF();
844         d->lastModifiers = event->modifiers();
845         QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
846         emit mouseXChanged(&me);
847         me.setPosition(d->lastPos);
848         emit mouseYChanged(&me);
849         me.setPosition(d->lastPos);
850         emit positionChanged(&me);
851     }
852 }
853
854 void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event)
855 {
856     Q_D(QQuickMouseArea);
857     if (!d->absorb && !d->pressed)
858         QQuickItem::hoverLeaveEvent(event);
859     else
860         setHovered(false);
861 }
862
863 void QQuickMouseArea::ungrabMouse()
864 {
865     Q_D(QQuickMouseArea);
866     if (d->pressed) {
867         // if our mouse grab has been removed (probably by Flickable), fix our
868         // state
869         d->pressed = false;
870         d->stealMouse = false;
871         setKeepMouseGrab(false);
872         emit canceled();
873         emit pressedChanged();
874         if (d->hovered) {
875             d->hovered = false;
876             emit hoveredChanged();
877         }
878     }
879 }
880
881 void QQuickMouseArea::mouseUngrabEvent()
882 {
883     ungrabMouse();
884 }
885
886 bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event)
887 {
888     Q_D(QQuickMouseArea);
889     QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
890
891     QQuickCanvas *c = canvas();
892     QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
893     bool stealThisEvent = d->stealMouse;
894     if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) {
895         QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
896                                event->button(), event->buttons(), event->modifiers());
897         mouseEvent.setAccepted(false);
898
899         switch (event->type()) {
900         case QEvent::MouseMove:
901             mouseMoveEvent(&mouseEvent);
902             break;
903         case QEvent::MouseButtonPress:
904             mousePressEvent(&mouseEvent);
905             break;
906         case QEvent::MouseButtonRelease:
907             mouseReleaseEvent(&mouseEvent);
908             break;
909         default:
910             break;
911         }
912         grabber = c->mouseGrabberItem();
913         if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
914             grabMouse();
915
916         return stealThisEvent;
917     }
918     if (event->type() == QEvent::MouseButtonRelease) {
919         if (d->pressed) {
920             d->pressed = false;
921             d->stealMouse = false;
922             if (c && c->mouseGrabberItem() == this)
923                 ungrabMouse();
924             emit canceled();
925             emit pressedChanged();
926             if (d->hovered) {
927                 d->hovered = false;
928                 emit hoveredChanged();
929             }
930         }
931     }
932     return false;
933 }
934
935 bool QQuickMouseArea::childMouseEventFilter(QQuickItem *i, QEvent *e)
936 {
937     Q_D(QQuickMouseArea);
938     if (!d->pressed && (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren()))
939         return QQuickItem::childMouseEventFilter(i, e);
940     switch (e->type()) {
941     case QEvent::MouseButtonPress:
942     case QEvent::MouseMove:
943     case QEvent::MouseButtonRelease:
944         return sendMouseEvent(static_cast<QMouseEvent *>(e));
945     default:
946         break;
947     }
948
949     return QQuickItem::childMouseEventFilter(i, e);
950 }
951
952 void QQuickMouseArea::timerEvent(QTimerEvent *event)
953 {
954     Q_D(QQuickMouseArea);
955     if (event->timerId() == d->pressAndHoldTimer.timerId()) {
956         d->pressAndHoldTimer.stop();
957         bool dragged = d->drag && d->drag->active();
958         if (d->pressed && dragged == false && d->hovered == true) {
959             d->longPress = true;
960             QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
961             me.setAccepted(d->isPressAndHoldConnected());
962             emit pressAndHold(&me);
963             if (!me.isAccepted())
964                 d->propagate(&me, QQuickMouseAreaPrivate::PressAndHold);
965             if (!me.isAccepted()) // no one handled the long press - allow click
966                 d->longPress = false;
967         }
968     }
969 }
970
971 void QQuickMouseArea::windowDeactivateEvent()
972 {
973     ungrabMouse();
974     QQuickItem::windowDeactivateEvent();
975 }
976
977 void QQuickMouseArea::geometryChanged(const QRectF &newGeometry,
978                                             const QRectF &oldGeometry)
979 {
980     Q_D(QQuickMouseArea);
981     QQuickItem::geometryChanged(newGeometry, oldGeometry);
982
983     if (d->lastScenePos.isNull)
984         d->lastScenePos = mapToScene(d->lastPos);
985     else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y())
986         d->lastPos = mapFromScene(d->lastScenePos);
987 }
988
989 void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value)
990 {
991     Q_D(QQuickMouseArea);
992     switch (change) {
993     case ItemVisibleHasChanged:
994         if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse()))
995             setHovered(!d->hovered);
996         break;
997     default:
998         break;
999     }
1000
1001     QQuickItem::itemChange(change, value);
1002 }
1003
1004 /*!
1005     \qmlproperty bool QtQuick2::MouseArea::hoverEnabled
1006     This property holds whether hover events are handled.
1007
1008     By default, mouse events are only handled in response to a button event, or when a button is
1009     pressed.  Hover enables handling of all mouse events even when no mouse button is
1010     pressed.
1011
1012     This property affects the containsMouse property and the onEntered, onExited and
1013     onPositionChanged signals.
1014 */
1015 bool QQuickMouseArea::hoverEnabled() const
1016 {
1017     return acceptHoverEvents();
1018 }
1019
1020 void QQuickMouseArea::setHoverEnabled(bool h)
1021 {
1022     if (h == acceptHoverEvents())
1023         return;
1024
1025     setAcceptHoverEvents(h);
1026     emit hoverEnabledChanged();
1027 }
1028
1029
1030 /*!
1031     \qmlproperty bool QtQuick2::MouseArea::containsMouse
1032     This property holds whether the mouse is currently inside the mouse area.
1033
1034     \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change.
1035     In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed.
1036 */
1037 bool QQuickMouseArea::hovered() const
1038 {
1039     Q_D(const QQuickMouseArea);
1040     return d->hovered;
1041 }
1042
1043 /*!
1044     \qmlproperty bool QtQuick2::MouseArea::pressed
1045     This property holds whether the mouse area is currently pressed.
1046 */
1047 bool QQuickMouseArea::pressed() const
1048 {
1049     Q_D(const QQuickMouseArea);
1050     return d->pressed;
1051 }
1052
1053 void QQuickMouseArea::setHovered(bool h)
1054 {
1055     Q_D(QQuickMouseArea);
1056     if (d->hovered != h) {
1057         d->hovered = h;
1058         emit hoveredChanged();
1059         d->hovered ? emit entered() : emit exited();
1060     }
1061 }
1062 /*!
1063     \qmlproperty QtQuick2::Qt::MouseButtons MouseArea::acceptedButtons
1064     This property holds the mouse buttons that the mouse area reacts to.
1065
1066     The available buttons are:
1067     \list
1068     \o Qt.LeftButton
1069     \o Qt.RightButton
1070     \o Qt.MiddleButton
1071     \endlist
1072
1073     To accept more than one button the flags can be combined with the
1074     "|" (or) operator:
1075
1076     \code
1077     MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton }
1078     \endcode
1079
1080     The default value is \c Qt.LeftButton.
1081 */
1082 Qt::MouseButtons QQuickMouseArea::acceptedButtons() const
1083 {
1084     return acceptedMouseButtons();
1085 }
1086
1087 void QQuickMouseArea::setAcceptedButtons(Qt::MouseButtons buttons)
1088 {
1089     if (buttons != acceptedMouseButtons()) {
1090         setAcceptedMouseButtons(buttons);
1091         emit acceptedButtonsChanged();
1092     }
1093 }
1094
1095 bool QQuickMouseArea::setPressed(bool p)
1096 {
1097     Q_D(QQuickMouseArea);
1098     bool dragged = d->drag && d->drag->active();
1099     bool isclick = d->pressed == true && p == false && dragged == false && d->hovered == true;
1100
1101     if (d->pressed != p) {
1102         d->pressed = p;
1103         QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
1104         if (d->pressed) {
1105             if (!d->doubleClick)
1106                 emit pressed(&me);
1107             me.setPosition(d->lastPos);
1108             emit mouseXChanged(&me);
1109             me.setPosition(d->lastPos);
1110             emit mouseYChanged(&me);
1111             emit pressedChanged();
1112         } else {
1113             emit released(&me);
1114             me.setPosition(d->lastPos);
1115             emit pressedChanged();
1116             if (isclick && !d->longPress && !d->doubleClick){
1117                 me.setAccepted(d->isClickConnected());
1118                 emit clicked(&me);
1119                 if (!me.isAccepted())
1120                     d->propagate(&me, QQuickMouseAreaPrivate::Click);
1121             }
1122         }
1123
1124         return me.isAccepted();
1125     }
1126     return false;
1127 }
1128
1129 /*!
1130     \qmlproperty Item QtQuick2::MouseArea::drag.target
1131     \qmlproperty bool QtQuick2::MouseArea::drag.active
1132     \qmlproperty enumeration QtQuick2::MouseArea::drag.axis
1133     \qmlproperty real QtQuick2::MouseArea::drag.minimumX
1134     \qmlproperty real QtQuick2::MouseArea::drag.maximumX
1135     \qmlproperty real QtQuick2::MouseArea::drag.minimumY
1136     \qmlproperty real QtQuick2::MouseArea::drag.maximumY
1137     \qmlproperty bool QtQuick2::MouseArea::drag.filterChildren
1138
1139     \c drag provides a convenient way to make an item draggable.
1140
1141     \list
1142     \i \c drag.target specifies the id of the item to drag.
1143     \i \c drag.active specifies if the target item is currently being dragged.
1144     \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis)
1145     \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes.
1146     \endlist
1147
1148     The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity
1149     of the rectangle is reduced when it is dragged to the right.
1150
1151     \snippet doc/src/snippets/qml/mousearea/mousearea.qml drag
1152
1153     \note Items cannot be dragged if they are anchored for the requested
1154     \c drag.axis. For example, if \c anchors.left or \c anchors.right was set
1155     for \c rect in the above example, it cannot be dragged along the X-axis.
1156     This can be avoided by settng the anchor value to \c undefined in
1157     an \l onPressed handler.
1158
1159     If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas.  This
1160     enables a parent MouseArea to handle drags, for example, while descendants handle clicks:
1161
1162     \snippet doc/src/snippets/qml/mousearea/mouseareadragfilter.qml dragfilter
1163
1164 */
1165
1166 QQuickDrag *QQuickMouseArea::drag()
1167 {
1168     Q_D(QQuickMouseArea);
1169     if (!d->drag)
1170         d->drag = new QQuickDrag;
1171     return d->drag;
1172 }
1173
1174 QSGNode *QQuickMouseArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1175 {
1176     Q_UNUSED(data);
1177     Q_D(QQuickMouseArea);
1178
1179     if (!qmlVisualTouchDebugging())
1180         return 0;
1181
1182     QSGRectangleNode *rectangle = static_cast<QSGRectangleNode *>(oldNode);
1183     if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode();
1184
1185     rectangle->setRect(QRectF(0, 0, width(), height()));
1186     rectangle->setColor(QColor(255, 0, 0, 50));
1187     rectangle->update();
1188     return rectangle;
1189 }
1190
1191 QT_END_NAMESPACE