1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "private/qdeclarativemousearea_p.h"
43 #include "private/qdeclarativemousearea_p_p.h"
44 
45 #include "private/qdeclarativeevents_p_p.h"
46 
47 #include <QGraphicsSceneMouseEvent>
48 
49 #include <float.h>
50 
51 QT_BEGIN_NAMESPACE
52 static const int PressAndHoldDelay = 800;
53 
QDeclarativeDrag(QObject * parent)54 QDeclarativeDrag::QDeclarativeDrag(QObject *parent)
55 : QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX),
56 _active(false), _filterChildren(false)
57 {
58 }
59 
~QDeclarativeDrag()60 QDeclarativeDrag::~QDeclarativeDrag()
61 {
62 }
63 
target() const64 QGraphicsObject *QDeclarativeDrag::target() const
65 {
66     return _target;
67 }
68 
setTarget(QGraphicsObject * t)69 void QDeclarativeDrag::setTarget(QGraphicsObject *t)
70 {
71     if (_target == t)
72         return;
73     _target = t;
74     emit targetChanged();
75 }
76 
resetTarget()77 void QDeclarativeDrag::resetTarget()
78 {
79     if (!_target)
80         return;
81     _target = 0;
82     emit targetChanged();
83 }
84 
axis() const85 QDeclarativeDrag::Axis QDeclarativeDrag::axis() const
86 {
87     return _axis;
88 }
89 
setAxis(QDeclarativeDrag::Axis a)90 void QDeclarativeDrag::setAxis(QDeclarativeDrag::Axis a)
91 {
92     if (_axis == a)
93         return;
94     _axis = a;
95     emit axisChanged();
96 }
97 
xmin() const98 qreal QDeclarativeDrag::xmin() const
99 {
100     return _xmin;
101 }
102 
setXmin(qreal m)103 void QDeclarativeDrag::setXmin(qreal m)
104 {
105     if (_xmin == m)
106         return;
107     _xmin = m;
108     emit minimumXChanged();
109 }
110 
xmax() const111 qreal QDeclarativeDrag::xmax() const
112 {
113     return _xmax;
114 }
115 
setXmax(qreal m)116 void QDeclarativeDrag::setXmax(qreal m)
117 {
118     if (_xmax == m)
119         return;
120     _xmax = m;
121     emit maximumXChanged();
122 }
123 
ymin() const124 qreal QDeclarativeDrag::ymin() const
125 {
126     return _ymin;
127 }
128 
setYmin(qreal m)129 void QDeclarativeDrag::setYmin(qreal m)
130 {
131     if (_ymin == m)
132         return;
133     _ymin = m;
134     emit minimumYChanged();
135 }
136 
ymax() const137 qreal QDeclarativeDrag::ymax() const
138 {
139     return _ymax;
140 }
141 
setYmax(qreal m)142 void QDeclarativeDrag::setYmax(qreal m)
143 {
144     if (_ymax == m)
145         return;
146     _ymax = m;
147     emit maximumYChanged();
148 }
149 
active() const150 bool QDeclarativeDrag::active() const
151 {
152     return _active;
153 }
154 
setActive(bool drag)155 void QDeclarativeDrag::setActive(bool drag)
156 {
157     if (_active == drag)
158         return;
159     _active = drag;
160     emit activeChanged();
161 }
162 
filterChildren() const163 bool QDeclarativeDrag::filterChildren() const
164 {
165     return _filterChildren;
166 }
167 
setFilterChildren(bool filter)168 void QDeclarativeDrag::setFilterChildren(bool filter)
169 {
170     if (_filterChildren == filter)
171         return;
172     _filterChildren = filter;
173     emit filterChildrenChanged();
174 }
175 
~QDeclarativeMouseAreaPrivate()176 QDeclarativeMouseAreaPrivate::~QDeclarativeMouseAreaPrivate()
177 {
178     delete drag;
179 }
180 
181 /*!
182     \qmlclass MouseArea QDeclarativeMouseArea
183     \ingroup qml-basic-interaction-elements
184     \since 4.7
185     \brief The MouseArea item enables simple mouse handling.
186     \inherits Item
187 
188     A MouseArea is an invisible item that is typically used in conjunction with
189     a visible item in order to provide mouse handling for that item.
190     By effectively acting as a proxy, the logic for mouse handling can be
191     contained within a MouseArea item.
192 
193     For basic key handling, see the \l{Keys}{Keys attached property}.
194 
195     The \l enabled property is used to enable and disable mouse handling for
196     the proxied item. When disabled, the mouse area becomes transparent to
197     mouse events.
198 
199     The \l pressed read-only property indicates whether or not the user is
200     holding down a mouse button over the mouse area. This property is often
201     used in bindings between properties in a user interface. The containsMouse
202     read-only property indicates the presence of the mouse cursor over the
203     mouse area but, by default, only when a mouse button is held down; see below
204     for further details.
205 
206     Information about the mouse position and button clicks are provided via
207     signals for which event handler properties are defined. The most commonly
208     used involved handling mouse presses and clicks: onClicked, onDoubleClicked,
209     onPressed, onReleased and onPressAndHold.
210 
211     By default, MouseArea items only report mouse clicks and not changes to the
212     position of the mouse cursor. Setting the hoverEnabled property ensures that
213     handlers defined for onPositionChanged, onEntered and onExited are used and
214     that the containsMouse property is updated even when no mouse buttons are
215     pressed.
216 
217     \section1 Example Usage
218 
219     \div {class="float-right"}
220     \inlineimage qml-mousearea-snippet.png
221     \enddiv
222 
223     The following example uses a MouseArea in a \l Rectangle that changes
224     the \l Rectangle color to red when clicked:
225 
226     \snippet doc/src/snippets/declarative/mousearea/mousearea.qml import
227     \codeline
228     \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro
229 
230     \clearfloat
231     Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains
232     additional information about the mouse event, such as the position, button,
233     and any key modifiers.
234 
235     Here is an extension of the previous example that produces a different
236     color when the area is right clicked:
237 
238     \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro-extended
239 
240     \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example}
241 */
242 
243 /*!
244     \qmlsignal MouseArea::onEntered()
245 
246     This handler is called when the mouse enters the mouse area.
247 
248     By default the onEntered handler is only called while a button is
249     pressed. Setting hoverEnabled to true enables handling of
250     onEntered when no mouse button is pressed.
251 
252     \sa hoverEnabled
253 */
254 
255 /*!
256     \qmlsignal MouseArea::onExited()
257 
258     This handler is called when the mouse exists the mouse area.
259 
260     By default the onExited handler is only called while a button is
261     pressed. Setting hoverEnabled to true enables handling of
262     onExited when no mouse button is pressed.
263 
264     \sa hoverEnabled
265 */
266 
267 /*!
268     \qmlsignal MouseArea::onPositionChanged(MouseEvent mouse)
269 
270     This handler is called when the mouse position changes.
271 
272     The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y
273     position, and any buttons currently pressed.
274 
275     The \e accepted property of the MouseEvent parameter is ignored in this handler.
276 
277     By default the onPositionChanged handler is only called while a button is
278     pressed.  Setting hoverEnabled to true enables handling of
279     onPositionChanged when no mouse button is pressed.
280 */
281 
282 /*!
283     \qmlsignal MouseArea::onClicked(MouseEvent mouse)
284 
285     This handler is called when there is a click. A click is defined as a press followed by a release,
286     both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and
287     releasing is also considered a click).
288 
289     The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
290     position of the release of the click, and whether the click was held.
291 
292     The \e accepted property of the MouseEvent parameter is ignored in this handler.
293 */
294 
295 /*!
296     \qmlsignal MouseArea::onPressed(MouseEvent mouse)
297 
298     This handler is called when there is a press.
299     The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
300     position and which button was pressed.
301 
302     The \e accepted property of the MouseEvent parameter determines whether this MouseArea
303     will handle the press and all future mouse events until release.  The default is to accept
304     the event and not allow other MouseArea beneath this one to handle the event.  If \e accepted
305     is set to false, no further events will be sent to this MouseArea until the button is next
306     pressed.
307 */
308 
309 /*!
310     \qmlsignal MouseArea::onReleased(MouseEvent mouse)
311 
312     This handler is called when there is a release.
313     The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
314     position of the release of the click, and whether the click was held.
315 
316     The \e accepted property of the MouseEvent parameter is ignored in this handler.
317 
318     \sa onCanceled
319 */
320 
321 /*!
322     \qmlsignal MouseArea::onPressAndHold(MouseEvent mouse)
323 
324     This handler is called when there is a long press (currently 800ms).
325     The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
326     position of the press, and which button is pressed.
327 
328     The \e accepted property of the MouseEvent parameter is ignored in this handler.
329 */
330 
331 /*!
332     \qmlsignal MouseArea::onDoubleClicked(MouseEvent mouse)
333 
334     This handler is called when there is a double-click (a press followed by a release followed by a press).
335     The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
336     position of the release of the click, and whether the click was held.
337 
338     If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false
339     in the handler, the onPressed/onReleased/onClicked handlers will be called for the second
340     click; otherwise they are suppressed.  The accepted property defaults to true.
341 */
342 
343 /*!
344     \qmlsignal MouseArea::onCanceled()
345 
346     This handler is called when mouse events have been canceled, either because an event was not accepted, or
347     because another element stole the mouse event handling.
348 
349     This signal is for advanced use: it is useful when there is more than one MouseArea
350     that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter
351     case, if you execute some logic on the pressed signal and then start dragging, the
352     \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset
353     the logic when the MouseArea has lost the mouse handling to the \l Flickable,
354     \c onCanceled should be used in addition to onReleased.
355 */
356 
QDeclarativeMouseArea(QDeclarativeItem * parent)357 QDeclarativeMouseArea::QDeclarativeMouseArea(QDeclarativeItem *parent)
358   : QDeclarativeItem(*(new QDeclarativeMouseAreaPrivate), parent)
359 {
360     Q_D(QDeclarativeMouseArea);
361     d->init();
362 }
363 
~QDeclarativeMouseArea()364 QDeclarativeMouseArea::~QDeclarativeMouseArea()
365 {
366 }
367 
368 /*!
369     \qmlproperty real MouseArea::mouseX
370     \qmlproperty real MouseArea::mouseY
371     These properties hold the coordinates of the mouse cursor.
372 
373     If the hoverEnabled property is false then these properties will only be valid
374     while a button is pressed, and will remain valid as long as the button is held
375     down even if the mouse is moved outside the area.
376 
377     By default, this property is false.
378 
379     If hoverEnabled is true then these properties will be valid when:
380     \list
381         \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true).
382         \i a button is pressed and held, even if it has since moved out of the area.
383     \endlist
384 
385     The coordinates are relative to the MouseArea.
386 */
mouseX() const387 qreal QDeclarativeMouseArea::mouseX() const
388 {
389     Q_D(const QDeclarativeMouseArea);
390     return d->lastPos.x();
391 }
392 
mouseY() const393 qreal QDeclarativeMouseArea::mouseY() const
394 {
395     Q_D(const QDeclarativeMouseArea);
396     return d->lastPos.y();
397 }
398 
399 /*!
400     \qmlproperty bool MouseArea::enabled
401     This property holds whether the item accepts mouse events.
402 
403     By default, this property is true.
404 */
isEnabled() const405 bool QDeclarativeMouseArea::isEnabled() const
406 {
407     Q_D(const QDeclarativeMouseArea);
408     return d->absorb;
409 }
410 
setEnabled(bool a)411 void QDeclarativeMouseArea::setEnabled(bool a)
412 {
413     Q_D(QDeclarativeMouseArea);
414     if (a != d->absorb) {
415         d->absorb = a;
416         emit enabledChanged();
417     }
418 }
419 
420 /*!
421     \qmlproperty bool MouseArea::preventStealing
422     \since QtQuick 1.1
423     This property holds whether the mouse events may be stolen from this
424     MouseArea.
425 
426     If a MouseArea is placed within an item that filters child mouse
427     events, such as Flickable, the mouse
428     events may be stolen from the MouseArea if a gesture is recognized
429     by the parent element, e.g. a flick gesture.  If preventStealing is
430     set to true, no element will steal the mouse events.
431 
432     Note that setting preventStealing to true once an element has started
433     stealing events will have no effect until the next press event.
434 
435     By default this property is false.
436 */
preventStealing() const437 bool QDeclarativeMouseArea::preventStealing() const
438 {
439     Q_D(const QDeclarativeMouseArea);
440     return d->preventStealing;
441 }
442 
setPreventStealing(bool prevent)443 void QDeclarativeMouseArea::setPreventStealing(bool prevent)
444 {
445     Q_D(QDeclarativeMouseArea);
446     if (prevent != d->preventStealing) {
447         d->preventStealing = prevent;
448         setKeepMouseGrab(d->preventStealing && d->absorb);
449         emit preventStealingChanged();
450     }
451 }
452 
453 /*!
454     \qmlproperty MouseButtons MouseArea::pressedButtons
455     This property holds the mouse buttons currently pressed.
456 
457     It contains a bitwise combination of:
458     \list
459     \o Qt.LeftButton
460     \o Qt.RightButton
461     \o Qt.MiddleButton
462     \endlist
463 
464     The code below displays "right" when the right mouse buttons is pressed:
465 
466     \snippet doc/src/snippets/declarative/mousearea/mousearea.qml mousebuttons
467 
468     \sa acceptedButtons
469 */
pressedButtons() const470 Qt::MouseButtons QDeclarativeMouseArea::pressedButtons() const
471 {
472     Q_D(const QDeclarativeMouseArea);
473     return d->lastButtons;
474 }
475 
mousePressEvent(QGraphicsSceneMouseEvent * event)476 void QDeclarativeMouseArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
477 {
478     Q_D(QDeclarativeMouseArea);
479     d->moved = false;
480     d->stealMouse = d->preventStealing;
481     if (!d->absorb)
482         QDeclarativeItem::mousePressEvent(event);
483     else {
484         d->longPress = false;
485         d->saveEvent(event);
486         if (d->drag)
487             d->drag->setActive(false);
488         setHovered(true);
489         d->startScene = event->scenePos();
490         // we should only start timer if pressAndHold is connected to.
491         if (d->isPressAndHoldConnected())
492             d->pressAndHoldTimer.start(PressAndHoldDelay, this);
493         setKeepMouseGrab(d->stealMouse);
494         event->setAccepted(setPressed(true));
495     }
496 }
497 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)498 void QDeclarativeMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
499 {
500     Q_D(QDeclarativeMouseArea);
501     if (!d->absorb) {
502         QDeclarativeItem::mouseMoveEvent(event);
503         return;
504     }
505 
506     d->saveEvent(event);
507 
508     // ### we should skip this if these signals aren't used
509     // ### can GV handle this for us?
510     bool contains = boundingRect().contains(d->lastPos);
511     if (d->hovered && !contains)
512         setHovered(false);
513     else if (!d->hovered && contains)
514         setHovered(true);
515 
516     if (d->drag && d->drag->target()) {
517         if (!d->moved) {
518             d->startX = drag()->target()->x();
519             d->startY = drag()->target()->y();
520         }
521 
522         QPointF startLocalPos;
523         QPointF curLocalPos;
524         if (drag()->target()->parentItem()) {
525             startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene);
526             curLocalPos = drag()->target()->parentItem()->mapFromScene(event->scenePos());
527         } else {
528             startLocalPos = d->startScene;
529             curLocalPos = event->scenePos();
530         }
531 
532         if (keepMouseGrab() && d->stealMouse)
533             d->drag->setActive(true);
534 
535         bool dragX = drag()->axis() & QDeclarativeDrag::XAxis;
536         bool dragY = drag()->axis() & QDeclarativeDrag::YAxis;
537 
538         const qreal x = dragX
539                 ? qBound(d->drag->xmin(), d->startX + curLocalPos.x() - startLocalPos.x(), d->drag->xmax())
540                 : d->startX;
541         const qreal y = dragY
542                 ? qBound(d->drag->ymin(), d->startY + curLocalPos.y() - startLocalPos.y(), d->drag->ymax())
543                 : d->startY;
544 
545         if (d->drag->active()) {
546             if (dragX && dragY)
547                 d->drag->target()->setPos(x, y);
548             else if (dragX)
549                 d->drag->target()->setX(x);
550             else if (dragY)
551                 d->drag->target()->setY(y);
552         }
553 
554         if (!keepMouseGrab()) {
555             const int dragThreshold = QApplication::startDragDistance();
556 
557             if (qAbs(x - d->startX) > dragThreshold || qAbs(y - d->startY) > dragThreshold) {
558                 setKeepMouseGrab(true);
559                 d->stealMouse = true;
560             }
561         }
562 
563         d->moved = true;
564     }
565     QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
566     emit mousePositionChanged(&me);
567     me.setX(d->lastPos.x());
568     me.setY(d->lastPos.y());
569     emit positionChanged(&me);
570 }
571 
572 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)573 void QDeclarativeMouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
574 {
575     Q_D(QDeclarativeMouseArea);
576     d->stealMouse = false;
577     if (!d->absorb) {
578         QDeclarativeItem::mouseReleaseEvent(event);
579     } else {
580         d->saveEvent(event);
581         setPressed(false);
582         if (d->drag)
583             d->drag->setActive(false);
584         // If we don't accept hover, we need to reset containsMouse.
585         if (!acceptHoverEvents())
586             setHovered(false);
587         QGraphicsScene *s = scene();
588         if (s && s->mouseGrabberItem() == this)
589             ungrabMouse();
590         setKeepMouseGrab(false);
591     }
592     d->doubleClick = false;
593 }
594 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)595 void QDeclarativeMouseArea::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
596 {
597     Q_D(QDeclarativeMouseArea);
598     if (!d->absorb) {
599         QDeclarativeItem::mouseDoubleClickEvent(event);
600     } else {
601         if (d->isDoubleClickConnected())
602             d->doubleClick = true;
603         d->saveEvent(event);
604         QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
605         me.setAccepted(d->isDoubleClickConnected());
606         emit this->doubleClicked(&me);
607         QDeclarativeItem::mouseDoubleClickEvent(event);
608     }
609 }
610 
hoverEnterEvent(QGraphicsSceneHoverEvent * event)611 void QDeclarativeMouseArea::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
612 {
613     Q_D(QDeclarativeMouseArea);
614     if (!d->absorb)
615         QDeclarativeItem::hoverEnterEvent(event);
616     else {
617         d->lastPos = event->pos();
618         setHovered(true);
619         QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false);
620         emit mousePositionChanged(&me);
621     }
622 }
623 
hoverMoveEvent(QGraphicsSceneHoverEvent * event)624 void QDeclarativeMouseArea::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
625 {
626     Q_D(QDeclarativeMouseArea);
627     if (!d->absorb) {
628         QDeclarativeItem::hoverMoveEvent(event);
629     } else {
630         d->lastPos = event->pos();
631         QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false);
632         emit mousePositionChanged(&me);
633         me.setX(d->lastPos.x());
634         me.setY(d->lastPos.y());
635         emit positionChanged(&me);
636     }
637 }
638 
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)639 void QDeclarativeMouseArea::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
640 {
641     Q_D(QDeclarativeMouseArea);
642     if (!d->absorb)
643         QDeclarativeItem::hoverLeaveEvent(event);
644     else
645         setHovered(false);
646 }
647 
648 #ifndef QT_NO_CONTEXTMENU
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)649 void QDeclarativeMouseArea::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
650 {
651     bool acceptsContextMenuButton;
652 #if defined(Q_OS_SYMBIAN)
653     // In Symbian a Long Tap on the screen will trigger. See QSymbianControl::HandleLongTapEventL().
654     acceptsContextMenuButton = acceptedButtons() & Qt::LeftButton;
655 #elif defined(Q_WS_WINCE)
656     // ### WinCE can trigger context menu event with a gesture in the left button or a
657     // click with the right button. Since we have no way here to differentiate them when
658     // event happens, accepting either of the them will block the event.
659     acceptsContextMenuButton = acceptedButtons() & (Qt::LeftButton | Qt::RightButton);
660 #else
661     acceptsContextMenuButton = acceptedButtons() & Qt::RightButton;
662 #endif
663 
664     if (isEnabled() && event->reason() == QGraphicsSceneContextMenuEvent::Mouse
665         && acceptsContextMenuButton) {
666         // Do not let the context menu event propagate to items behind.
667         return;
668     }
669 
670     QDeclarativeItem::contextMenuEvent(event);
671 }
672 #endif // QT_NO_CONTEXTMENU
673 
sceneEvent(QEvent * event)674 bool QDeclarativeMouseArea::sceneEvent(QEvent *event)
675 {
676     bool rv = QDeclarativeItem::sceneEvent(event);
677     if (event->type() == QEvent::UngrabMouse) {
678         Q_D(QDeclarativeMouseArea);
679         if (d->pressed) {
680             // if our mouse grab has been removed (probably by Flickable), fix our
681             // state
682             d->pressed = false;
683             d->stealMouse = false;
684             setKeepMouseGrab(false);
685             emit canceled();
686             emit pressedChanged();
687             if (d->hovered) {
688                 d->hovered = false;
689                 emit hoveredChanged();
690             }
691         }
692     }
693     return rv;
694 }
695 
sendMouseEvent(QGraphicsSceneMouseEvent * event)696 bool QDeclarativeMouseArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
697 {
698     Q_D(QDeclarativeMouseArea);
699     QGraphicsSceneMouseEvent mouseEvent(event->type());
700     QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
701 
702     QGraphicsScene *s = scene();
703     QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
704     bool stealThisEvent = d->stealMouse;
705     if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
706         mouseEvent.setAccepted(false);
707         for (int i = 0x1; i <= 0x10; i <<= 1) {
708             if (event->buttons() & i) {
709                 Qt::MouseButton button = Qt::MouseButton(i);
710                 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
711             }
712         }
713         mouseEvent.setScenePos(event->scenePos());
714         mouseEvent.setLastScenePos(event->lastScenePos());
715         mouseEvent.setPos(mapFromScene(event->scenePos()));
716         mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
717 
718         switch(mouseEvent.type()) {
719         case QEvent::GraphicsSceneMouseMove:
720             mouseMoveEvent(&mouseEvent);
721             break;
722         case QEvent::GraphicsSceneMousePress:
723             mousePressEvent(&mouseEvent);
724             break;
725         case QEvent::GraphicsSceneMouseRelease:
726             mouseReleaseEvent(&mouseEvent);
727             break;
728         default:
729             break;
730         }
731         grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
732         if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
733             grabMouse();
734 
735         return stealThisEvent;
736     }
737     if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
738         if (d->pressed) {
739             d->pressed = false;
740             d->stealMouse = false;
741             if (s && s->mouseGrabberItem() == this)
742                 ungrabMouse();
743             emit canceled();
744             emit pressedChanged();
745             if (d->hovered) {
746                 d->hovered = false;
747                 emit hoveredChanged();
748             }
749         }
750     }
751     return false;
752 }
753 
sceneEventFilter(QGraphicsItem * i,QEvent * e)754 bool QDeclarativeMouseArea::sceneEventFilter(QGraphicsItem *i, QEvent *e)
755 {
756     Q_D(QDeclarativeMouseArea);
757     if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren())
758         return QDeclarativeItem::sceneEventFilter(i, e);
759     switch (e->type()) {
760     case QEvent::GraphicsSceneMousePress:
761     case QEvent::GraphicsSceneMouseMove:
762     case QEvent::GraphicsSceneMouseRelease:
763         return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
764     default:
765         break;
766     }
767 
768     return QDeclarativeItem::sceneEventFilter(i, e);
769 }
770 
timerEvent(QTimerEvent * event)771 void QDeclarativeMouseArea::timerEvent(QTimerEvent *event)
772 {
773     Q_D(QDeclarativeMouseArea);
774     if (event->timerId() == d->pressAndHoldTimer.timerId()) {
775         d->pressAndHoldTimer.stop();
776         bool dragged = d->drag && d->drag->active();
777         if (d->pressed && dragged == false && d->hovered == true) {
778             d->longPress = true;
779             QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
780             emit pressAndHold(&me);
781         }
782     }
783 }
784 
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)785 void QDeclarativeMouseArea::geometryChanged(const QRectF &newGeometry,
786                                             const QRectF &oldGeometry)
787 {
788     Q_D(QDeclarativeMouseArea);
789     QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
790 
791     if (d->lastScenePos.isNull)
792         d->lastScenePos = mapToScene(d->lastPos);
793     else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y())
794         d->lastPos = mapFromScene(d->lastScenePos);
795 }
796 
itemChange(GraphicsItemChange change,const QVariant & value)797 QVariant QDeclarativeMouseArea::itemChange(GraphicsItemChange change,
798                                        const QVariant &value)
799 {
800     Q_D(QDeclarativeMouseArea);
801     switch (change) {
802     case ItemVisibleHasChanged:
803         if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse()))
804             setHovered(!d->hovered);
805         break;
806     default:
807         break;
808     }
809 
810     return QDeclarativeItem::itemChange(change, value);
811 }
812 
813 /*!
814     \qmlproperty bool MouseArea::hoverEnabled
815     This property holds whether hover events are handled.
816 
817     By default, mouse events are only handled in response to a button event, or when a button is
818     pressed.  Hover enables handling of all mouse events even when no mouse button is
819     pressed.
820 
821     This property affects the containsMouse property and the onEntered, onExited and
822     onPositionChanged signals.
823 */
hoverEnabled() const824 bool QDeclarativeMouseArea::hoverEnabled() const
825 {
826     return acceptHoverEvents();
827 }
828 
setHoverEnabled(bool h)829 void QDeclarativeMouseArea::setHoverEnabled(bool h)
830 {
831     Q_D(QDeclarativeMouseArea);
832     if (h == acceptHoverEvents())
833         return;
834 
835     setAcceptHoverEvents(h);
836     emit hoverEnabledChanged();
837     if (d->hovered != isUnderMouse())
838         setHovered(!d->hovered);
839 }
840 
841 /*!
842     \qmlproperty bool MouseArea::containsMouse
843     This property holds whether the mouse is currently inside the mouse area.
844 
845     \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change.
846     In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed.
847 */
hovered() const848 bool QDeclarativeMouseArea::hovered() const
849 {
850     Q_D(const QDeclarativeMouseArea);
851     return d->hovered;
852 }
853 
854 /*!
855     \qmlproperty bool MouseArea::pressed
856     This property holds whether the mouse area is currently pressed.
857 */
pressed() const858 bool QDeclarativeMouseArea::pressed() const
859 {
860     Q_D(const QDeclarativeMouseArea);
861     return d->pressed;
862 }
863 
setHovered(bool h)864 void QDeclarativeMouseArea::setHovered(bool h)
865 {
866     Q_D(QDeclarativeMouseArea);
867     if (d->hovered != h) {
868         d->hovered = h;
869         emit hoveredChanged();
870         d->hovered ? emit entered() : emit exited();
871     }
872 }
873 
874 /*!
875     \qmlproperty Qt::MouseButtons MouseArea::acceptedButtons
876     This property holds the mouse buttons that the mouse area reacts to.
877 
878     The available buttons are:
879     \list
880     \o Qt.LeftButton
881     \o Qt.RightButton
882     \o Qt.MiddleButton
883     \o Qt.XButton1
884     \o Qt.XButton2
885     \endlist
886 
887     To accept more than one button the flags can be combined with the
888     "|" (or) operator:
889 
890     \code
891     MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton }
892     \endcode
893 
894     The default value is \c Qt.LeftButton.
895 */
acceptedButtons() const896 Qt::MouseButtons QDeclarativeMouseArea::acceptedButtons() const
897 {
898     return acceptedMouseButtons();
899 }
900 
setAcceptedButtons(Qt::MouseButtons buttons)901 void QDeclarativeMouseArea::setAcceptedButtons(Qt::MouseButtons buttons)
902 {
903     if (buttons != acceptedMouseButtons()) {
904         setAcceptedMouseButtons(buttons);
905         emit acceptedButtonsChanged();
906     }
907 }
908 
setPressed(bool p)909 bool QDeclarativeMouseArea::setPressed(bool p)
910 {
911     Q_D(QDeclarativeMouseArea);
912     bool dragged = d->drag && d->drag->active();
913     bool isclick = d->pressed == true && p == false && dragged == false && d->hovered == true;
914 
915     if (d->pressed != p) {
916         d->pressed = p;
917         QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
918         if (d->pressed) {
919             if (!d->doubleClick)
920                 emit pressed(&me);
921             me.setX(d->lastPos.x());
922             me.setY(d->lastPos.y());
923             emit mousePositionChanged(&me);
924             emit pressedChanged();
925         } else {
926             emit released(&me);
927             me.setX(d->lastPos.x());
928             me.setY(d->lastPos.y());
929             emit pressedChanged();
930             if (isclick && !d->longPress && !d->doubleClick)
931                 emit clicked(&me);
932         }
933 
934         return me.isAccepted();
935     }
936     return false;
937 }
938 
drag()939 QDeclarativeDrag *QDeclarativeMouseArea::drag()
940 {
941     Q_D(QDeclarativeMouseArea);
942     if (!d->drag)
943         d->drag = new QDeclarativeDrag;
944     return d->drag;
945 }
946 
947 /*!
948     \qmlproperty Item MouseArea::drag.target
949     \qmlproperty bool MouseArea::drag.active
950     \qmlproperty enumeration MouseArea::drag.axis
951     \qmlproperty real MouseArea::drag.minimumX
952     \qmlproperty real MouseArea::drag.maximumX
953     \qmlproperty real MouseArea::drag.minimumY
954     \qmlproperty real MouseArea::drag.maximumY
955     \qmlproperty bool MouseArea::drag.filterChildren
956 
957     \c drag provides a convenient way to make an item draggable.
958 
959     \list
960     \i \c drag.target specifies the id of the item to drag.
961     \i \c drag.active specifies if the target item is currently being dragged.
962     \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis)
963     \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes.
964     \endlist
965 
966     The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity
967     of the rectangle is reduced when it is dragged to the right.
968 
969     \snippet doc/src/snippets/declarative/mousearea/mousearea.qml drag
970 
971     \note Items cannot be dragged if they are anchored for the requested
972     \c drag.axis. For example, if \c anchors.left or \c anchors.right was set
973     for \c rect in the above example, it cannot be dragged along the X-axis.
974     This can be avoided by settng the anchor value to \c undefined in
975     an \l onPressed handler.
976 
977     If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas.  This
978     enables a parent MouseArea to handle drags, for example, while descendants handle clicks:
979 
980     \snippet doc/src/snippets/declarative/mousearea/mouseareadragfilter.qml dragfilter
981 
982 */
983 
984 QT_END_NAMESPACE
985