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