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