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