20ca0f689a39b68cd1f28c897db4499cb7fef271
[profile/ivi/qtdeclarative.git] / src / declarative / graphicsitems / qdeclarativemousearea.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
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
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
60 QDeclarativeDrag::~QDeclarativeDrag()
61 {
62 }
63
64 QGraphicsObject *QDeclarativeDrag::target() const
65 {
66     return _target;
67 }
68
69 void QDeclarativeDrag::setTarget(QGraphicsObject *t)
70 {
71     if (_target == t)
72         return;
73     _target = t;
74     emit targetChanged();
75 }
76
77 void QDeclarativeDrag::resetTarget()
78 {
79     if (!_target)
80         return;
81     _target = 0;
82     emit targetChanged();
83 }
84
85 QDeclarativeDrag::Axis QDeclarativeDrag::axis() const
86 {
87     return _axis;
88 }
89
90 void QDeclarativeDrag::setAxis(QDeclarativeDrag::Axis a)
91 {
92     if (_axis == a)
93         return;
94     _axis = a;
95     emit axisChanged();
96 }
97
98 qreal QDeclarativeDrag::xmin() const
99 {
100     return _xmin;
101 }
102
103 void QDeclarativeDrag::setXmin(qreal m)
104 {
105     if (_xmin == m)
106         return;
107     _xmin = m;
108     emit minimumXChanged();
109 }
110
111 qreal QDeclarativeDrag::xmax() const
112 {
113     return _xmax;
114 }
115
116 void QDeclarativeDrag::setXmax(qreal m)
117 {
118     if (_xmax == m)
119         return;
120     _xmax = m;
121     emit maximumXChanged();
122 }
123
124 qreal QDeclarativeDrag::ymin() const
125 {
126     return _ymin;
127 }
128
129 void QDeclarativeDrag::setYmin(qreal m)
130 {
131     if (_ymin == m)
132         return;
133     _ymin = m;
134     emit minimumYChanged();
135 }
136
137 qreal QDeclarativeDrag::ymax() const
138 {
139     return _ymax;
140 }
141
142 void QDeclarativeDrag::setYmax(qreal m)
143 {
144     if (_ymax == m)
145         return;
146     _ymax = m;
147     emit maximumYChanged();
148 }
149
150 bool QDeclarativeDrag::active() const
151 {
152     return _active;
153 }
154
155 void QDeclarativeDrag::setActive(bool drag)
156 {
157     if (_active == drag)
158         return;
159     _active = drag;
160     emit activeChanged();
161 }
162
163 bool QDeclarativeDrag::filterChildren() const
164 {
165     return _filterChildren;
166 }
167
168 void QDeclarativeDrag::setFilterChildren(bool filter)
169 {
170     if (_filterChildren == filter)
171         return;
172     _filterChildren = filter;
173     emit filterChildrenChanged();
174 }
175
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
357 QDeclarativeMouseArea::QDeclarativeMouseArea(QDeclarativeItem *parent)
358   : QDeclarativeItem(*(new QDeclarativeMouseAreaPrivate), parent)
359 {
360     Q_D(QDeclarativeMouseArea);
361     d->init();
362 }
363
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 */
387 qreal QDeclarativeMouseArea::mouseX() const
388 {
389     Q_D(const QDeclarativeMouseArea);
390     return d->lastPos.x();
391 }
392
393 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 */
405 bool QDeclarativeMouseArea::isEnabled() const
406 {
407     Q_D(const QDeclarativeMouseArea);
408     return d->absorb;
409 }
410
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 Quick 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 */
437 bool QDeclarativeMouseArea::preventStealing() const
438 {
439     Q_D(const QDeclarativeMouseArea);
440     return d->preventStealing;
441 }
442
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 */
470 Qt::MouseButtons QDeclarativeMouseArea::pressedButtons() const
471 {
472     Q_D(const QDeclarativeMouseArea);
473     return d->lastButtons;
474 }
475
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->dragX = drag()->axis() & QDeclarativeDrag::XAxis;
488             d->dragY = drag()->axis() & QDeclarativeDrag::YAxis;
489         }
490         if (d->drag)
491             d->drag->setActive(false);
492         setHovered(true);
493         d->startScene = event->scenePos();
494         // we should only start timer if pressAndHold is connected to.
495         if (d->isPressAndHoldConnected())
496             d->pressAndHoldTimer.start(PressAndHoldDelay, this);
497         setKeepMouseGrab(d->stealMouse);
498         event->setAccepted(setPressed(true));
499
500         if(!event->isAccepted() && d->forwardToList.count())
501            d->forwardEvent(event);
502     }
503 }
504
505 void QDeclarativeMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
506 {
507     Q_D(QDeclarativeMouseArea);
508     if (!d->absorb) {
509         QDeclarativeItem::mouseMoveEvent(event);
510         return;
511     }
512
513     d->saveEvent(event);
514
515     // ### we should skip this if these signals aren't used
516     // ### can GV handle this for us?
517     bool contains = boundingRect().contains(d->lastPos);
518     if (d->hovered && !contains)
519         setHovered(false);
520     else if (!d->hovered && contains)
521         setHovered(true);
522
523     if (d->drag && d->drag->target()) {
524         if (!d->moved) {
525             d->startX = drag()->target()->x();
526             d->startY = drag()->target()->y();
527         }
528
529         QPointF startLocalPos;
530         QPointF curLocalPos;
531         if (drag()->target()->parentItem()) {
532             startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene);
533             curLocalPos = drag()->target()->parentItem()->mapFromScene(event->scenePos());
534         } else {
535             startLocalPos = d->startScene;
536             curLocalPos = event->scenePos();
537         }
538
539         const int dragThreshold = QApplication::startDragDistance();
540         qreal dx = qAbs(curLocalPos.x() - startLocalPos.x());
541         qreal dy = qAbs(curLocalPos.y() - startLocalPos.y());
542
543         if (keepMouseGrab() && d->stealMouse)
544             d->drag->setActive(true);
545
546         if (d->dragX && d->drag->active()) {
547             qreal x = (curLocalPos.x() - startLocalPos.x()) + d->startX;
548             if (x < drag()->xmin())
549                 x = drag()->xmin();
550             else if (x > drag()->xmax())
551                 x = drag()->xmax();
552             drag()->target()->setX(x);
553         }
554         if (d->dragY && d->drag->active()) {
555             qreal y = (curLocalPos.y() - startLocalPos.y()) + d->startY;
556             if (y < drag()->ymin())
557                 y = drag()->ymin();
558             else if (y > drag()->ymax())
559                 y = drag()->ymax();
560             drag()->target()->setY(y);
561         }
562
563         if (!keepMouseGrab()) {
564             if ((!d->dragY && dy < dragThreshold && d->dragX && dx > dragThreshold)
565                 || (!d->dragX && dx < dragThreshold && d->dragY && dy > dragThreshold)
566                 || (d->dragX && d->dragY && (dx > dragThreshold || dy > dragThreshold))) {
567                 setKeepMouseGrab(true);
568                 d->stealMouse = true;
569             }
570         }
571
572         d->moved = true;
573     }
574     QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
575     emit mousePositionChanged(&me);
576     me.setX(d->lastPos.x());
577     me.setY(d->lastPos.y());
578     emit positionChanged(&me);
579
580     if(!event->isAccepted() && d->forwardToList.count())
581         d->forwardEvent(event);
582 }
583
584
585 void QDeclarativeMouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
586 {
587     Q_D(QDeclarativeMouseArea);
588     d->stealMouse = false;
589     if (!d->absorb) {
590         QDeclarativeItem::mouseReleaseEvent(event);
591     } else {
592         d->saveEvent(event);
593         setPressed(false);
594         if (d->drag)
595             d->drag->setActive(false);
596         // If we don't accept hover, we need to reset containsMouse.
597         if (!acceptHoverEvents())
598             setHovered(false);
599         QGraphicsScene *s = scene();
600         if (s && s->mouseGrabberItem() == this)
601             ungrabMouse();
602         setKeepMouseGrab(false);
603
604         if(!event->isAccepted() && d->forwardToList.count())
605             d->forwardEvent(event);
606     }
607     d->doubleClick = false;
608 }
609
610 void QDeclarativeMouseArea::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
611 {
612     Q_D(QDeclarativeMouseArea);
613     if (!d->absorb) {
614         QDeclarativeItem::mouseDoubleClickEvent(event);
615     } else {
616         if (d->isDoubleClickConnected())
617             d->doubleClick = true;
618         d->saveEvent(event);
619         QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
620         me.setAccepted(d->isDoubleClickConnected());
621         emit this->doubleClicked(&me);
622         QDeclarativeItem::mouseDoubleClickEvent(event);
623     }
624 }
625
626 void QDeclarativeMouseArea::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
627 {
628     Q_D(QDeclarativeMouseArea);
629     if (!d->absorb)
630         QDeclarativeItem::hoverEnterEvent(event);
631     else {
632         d->lastPos = event->pos();
633         setHovered(true);
634         QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false);
635         emit mousePositionChanged(&me);
636     }
637 }
638
639 void QDeclarativeMouseArea::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
640 {
641     Q_D(QDeclarativeMouseArea);
642     if (!d->absorb) {
643         QDeclarativeItem::hoverMoveEvent(event);
644     } else {
645         d->lastPos = event->pos();
646         QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false);
647         emit mousePositionChanged(&me);
648         me.setX(d->lastPos.x());
649         me.setY(d->lastPos.y());
650         emit positionChanged(&me);
651     }
652 }
653
654 void QDeclarativeMouseArea::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
655 {
656     Q_D(QDeclarativeMouseArea);
657     if (!d->absorb)
658         QDeclarativeItem::hoverLeaveEvent(event);
659     else
660         setHovered(false);
661 }
662
663 #ifndef QT_NO_CONTEXTMENU
664 void QDeclarativeMouseArea::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
665 {
666     bool acceptsContextMenuButton;
667 #if defined(Q_OS_SYMBIAN)
668     // In Symbian a Long Tap on the screen will trigger. See QSymbianControl::HandleLongTapEventL().
669     acceptsContextMenuButton = acceptedButtons() & Qt::LeftButton;
670 #elif defined(Q_WS_WINCE)
671     // ### WinCE can trigger context menu event with a gesture in the left button or a
672     // click with the right button. Since we have no way here to differentiate them when
673     // event happens, accepting either of the them will block the event.
674     acceptsContextMenuButton = acceptedButtons() & (Qt::LeftButton | Qt::RightButton);
675 #else
676     acceptsContextMenuButton = acceptedButtons() & Qt::RightButton;
677 #endif
678
679     if (isEnabled() && event->reason() == QGraphicsSceneContextMenuEvent::Mouse
680         && acceptsContextMenuButton) {
681         // Do not let the context menu event propagate to items behind.
682         return;
683     }
684
685     QDeclarativeItem::contextMenuEvent(event);
686 }
687 #endif // QT_NO_CONTEXTMENU
688
689 bool QDeclarativeMouseArea::sceneEvent(QEvent *event)
690 {
691     bool rv = QDeclarativeItem::sceneEvent(event);
692     if (event->type() == QEvent::UngrabMouse) {
693         Q_D(QDeclarativeMouseArea);
694         if (d->pressed) {
695             // if our mouse grab has been removed (probably by Flickable), fix our
696             // state
697             d->pressed = false;
698             d->stealMouse = false;
699             setKeepMouseGrab(false);
700             emit canceled();
701             emit pressedChanged();
702             if (d->hovered) {
703                 d->hovered = false;
704                 emit hoveredChanged();
705             }
706         }
707     }
708     return rv;
709 }
710
711 bool QDeclarativeMouseArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
712 {
713     Q_D(QDeclarativeMouseArea);
714     QGraphicsSceneMouseEvent mouseEvent(event->type());
715     QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
716
717     QGraphicsScene *s = scene();
718     QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
719     bool stealThisEvent = d->stealMouse;
720     if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
721         mouseEvent.setAccepted(false);
722         for (int i = 0x1; i <= 0x10; i <<= 1) {
723             if (event->buttons() & i) {
724                 Qt::MouseButton button = Qt::MouseButton(i);
725                 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
726             }
727         }
728         mouseEvent.setScenePos(event->scenePos());
729         mouseEvent.setLastScenePos(event->lastScenePos());
730         mouseEvent.setPos(mapFromScene(event->scenePos()));
731         mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
732
733         switch(mouseEvent.type()) {
734         case QEvent::GraphicsSceneMouseMove:
735             mouseMoveEvent(&mouseEvent);
736             break;
737         case QEvent::GraphicsSceneMousePress:
738             mousePressEvent(&mouseEvent);
739             break;
740         case QEvent::GraphicsSceneMouseRelease:
741             mouseReleaseEvent(&mouseEvent);
742             break;
743         default:
744             break;
745         }
746         grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
747         if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
748             grabMouse();
749
750         return stealThisEvent;
751     }
752     if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
753         if (d->pressed) {
754             d->pressed = false;
755             d->stealMouse = false;
756             if (s && s->mouseGrabberItem() == this)
757                 ungrabMouse();
758             emit canceled();
759             emit pressedChanged();
760             if (d->hovered) {
761                 d->hovered = false;
762                 emit hoveredChanged();
763             }
764         }
765     }
766     return false;
767 }
768
769 bool QDeclarativeMouseArea::sceneEventFilter(QGraphicsItem *i, QEvent *e)
770 {
771     Q_D(QDeclarativeMouseArea);
772     if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren())
773         return QDeclarativeItem::sceneEventFilter(i, e);
774     switch (e->type()) {
775     case QEvent::GraphicsSceneMousePress:
776     case QEvent::GraphicsSceneMouseMove:
777     case QEvent::GraphicsSceneMouseRelease:
778         return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
779     default:
780         break;
781     }
782
783     return QDeclarativeItem::sceneEventFilter(i, e);
784 }
785
786 void QDeclarativeMouseArea::timerEvent(QTimerEvent *event)
787 {
788     Q_D(QDeclarativeMouseArea);
789     if (event->timerId() == d->pressAndHoldTimer.timerId()) {
790         d->pressAndHoldTimer.stop();
791         bool dragged = d->drag && d->drag->active();
792         if (d->pressed && dragged == false && d->hovered == true) {
793             d->longPress = true;
794             QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
795             emit pressAndHold(&me);
796         }
797     }
798 }
799
800 void QDeclarativeMouseArea::geometryChanged(const QRectF &newGeometry,
801                                             const QRectF &oldGeometry)
802 {
803     Q_D(QDeclarativeMouseArea);
804     QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
805
806     if (d->lastScenePos.isNull)
807         d->lastScenePos = mapToScene(d->lastPos);
808     else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y())
809         d->lastPos = mapFromScene(d->lastScenePos);
810 }
811
812 QVariant QDeclarativeMouseArea::itemChange(GraphicsItemChange change,
813                                        const QVariant &value)
814 {
815     Q_D(QDeclarativeMouseArea);
816     switch (change) {
817     case ItemVisibleHasChanged:
818         if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse()))
819             setHovered(!d->hovered);
820         break;
821     default:
822         break;
823     }
824
825     return QDeclarativeItem::itemChange(change, value);
826 }
827
828 /*!
829     \qmlproperty bool MouseArea::hoverEnabled
830     This property holds whether hover events are handled.
831
832     By default, mouse events are only handled in response to a button event, or when a button is
833     pressed.  Hover enables handling of all mouse events even when no mouse button is
834     pressed.
835
836     This property affects the containsMouse property and the onEntered, onExited and
837     onPositionChanged signals.
838 */
839 bool QDeclarativeMouseArea::hoverEnabled() const
840 {
841     return acceptHoverEvents();
842 }
843
844 void QDeclarativeMouseArea::setHoverEnabled(bool h)
845 {
846     Q_D(QDeclarativeMouseArea);
847     if (h == acceptHoverEvents())
848         return;
849
850     setAcceptHoverEvents(h);
851     emit hoverEnabledChanged();
852     if (d->hovered != isUnderMouse())
853         setHovered(!d->hovered);
854 }
855
856 /*!
857     \qmlproperty bool MouseArea::containsMouse
858     This property holds whether the mouse is currently inside the mouse area.
859
860     \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change.
861     In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed.
862 */
863 bool QDeclarativeMouseArea::hovered() const
864 {
865     Q_D(const QDeclarativeMouseArea);
866     return d->hovered;
867 }
868
869 /*!
870     \qmlproperty bool MouseArea::pressed
871     This property holds whether the mouse area is currently pressed.
872 */
873 bool QDeclarativeMouseArea::pressed() const
874 {
875     Q_D(const QDeclarativeMouseArea);
876     return d->pressed;
877 }
878
879 void QDeclarativeMouseArea::setHovered(bool h)
880 {
881     Q_D(QDeclarativeMouseArea);
882     if (d->hovered != h) {
883         d->hovered = h;
884         emit hoveredChanged();
885         d->hovered ? emit entered() : emit exited();
886     }
887 }
888
889 /*!
890     \qmlproperty Qt::MouseButtons MouseArea::acceptedButtons
891     This property holds the mouse buttons that the mouse area reacts to.
892
893     The available buttons are:
894     \list
895     \o Qt.LeftButton
896     \o Qt.RightButton
897     \o Qt.MiddleButton
898     \endlist
899
900     To accept more than one button the flags can be combined with the
901     "|" (or) operator:
902
903     \code
904     MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton }
905     \endcode
906
907     The default value is \c Qt.LeftButton.
908 */
909 Qt::MouseButtons QDeclarativeMouseArea::acceptedButtons() const
910 {
911     return acceptedMouseButtons();
912 }
913
914 void QDeclarativeMouseArea::setAcceptedButtons(Qt::MouseButtons buttons)
915 {
916     if (buttons != acceptedMouseButtons()) {
917         setAcceptedMouseButtons(buttons);
918         emit acceptedButtonsChanged();
919     }
920 }
921
922 bool QDeclarativeMouseArea::setPressed(bool p)
923 {
924     Q_D(QDeclarativeMouseArea);
925     bool dragged = d->drag && d->drag->active();
926     bool isclick = d->pressed == true && p == false && dragged == false && d->hovered == true;
927
928     if (d->pressed != p) {
929         d->pressed = p;
930         QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
931         if (d->pressed) {
932             if (!d->doubleClick)
933                 emit pressed(&me);
934             me.setX(d->lastPos.x());
935             me.setY(d->lastPos.y());
936             emit mousePositionChanged(&me);
937             emit pressedChanged();
938         } else {
939             emit released(&me);
940             me.setX(d->lastPos.x());
941             me.setY(d->lastPos.y());
942             emit pressedChanged();
943             if (isclick && !d->longPress && !d->doubleClick)
944                 emit clicked(&me);
945         }
946
947         return me.isAccepted();
948     }
949     return false;
950 }
951
952 QDeclarativeDrag *QDeclarativeMouseArea::drag()
953 {
954     Q_D(QDeclarativeMouseArea);
955     if (!d->drag)
956         d->drag = new QDeclarativeDrag;
957     return d->drag;
958 }
959
960 /*!
961     \qmlproperty Item MouseArea::drag.target
962     \qmlproperty bool MouseArea::drag.active
963     \qmlproperty enumeration MouseArea::drag.axis
964     \qmlproperty real MouseArea::drag.minimumX
965     \qmlproperty real MouseArea::drag.maximumX
966     \qmlproperty real MouseArea::drag.minimumY
967     \qmlproperty real MouseArea::drag.maximumY
968     \qmlproperty bool MouseArea::drag.filterChildren
969
970     \c drag provides a convenient way to make an item draggable.
971
972     \list
973     \i \c drag.target specifies the id of the item to drag.
974     \i \c drag.active specifies if the target item is currently being dragged.
975     \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis)
976     \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes.
977     \endlist
978
979     The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity
980     of the rectangle is reduced when it is dragged to the right.
981
982     \snippet doc/src/snippets/declarative/mousearea/mousearea.qml drag
983
984     \note Items cannot be dragged if they are anchored for the requested 
985     \c drag.axis. For example, if \c anchors.left or \c anchors.right was set
986     for \c rect in the above example, it cannot be dragged along the X-axis.
987     This can be avoided by settng the anchor value to \c undefined in 
988     an \l onPressed handler.
989
990     If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas.  This
991     enables a parent MouseArea to handle drags, for example, while descendants handle clicks:
992
993     \snippet doc/src/snippets/declarative/mousearea/mouseareadragfilter.qml dragfilter
994
995 */
996
997 QDeclarativeListProperty<QGraphicsObject> QDeclarativeMouseArea::forwardTo()
998 {
999     Q_D(QDeclarativeMouseArea);
1000     return d->forwardTo;
1001 }
1002
1003
1004 QT_END_NAMESPACE