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