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