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