Doc: Grouped Qt Quick types into several groups
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickdrag.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 "qquickdrag_p.h"
43
44 #include <private/qquickitem_p.h>
45 #include <QtQuick/private/qquickevents_p_p.h>
46 #include <private/qquickitemchangelistener_p.h>
47 #include <private/qv8engine_p.h>
48 #include <QtCore/qcoreapplication.h>
49 #include <QtQml/qqmlinfo.h>
50 #include <QtGui/qevent.h>
51
52 QT_BEGIN_NAMESPACE
53
54 class QQuickDragAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener
55 {
56     Q_DECLARE_PUBLIC(QQuickDragAttached)
57 public:
58     static QQuickDragAttachedPrivate *get(QQuickDragAttached *attached) {
59         return static_cast<QQuickDragAttachedPrivate *>(QObjectPrivate::get(attached)); }
60
61     QQuickDragAttachedPrivate()
62         : attachedItem(0)
63         , mimeData(0)
64         , proposedAction(Qt::MoveAction)
65         , supportedActions(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction)
66         , active(false)
67         , listening(false)
68         , inEvent(false)
69         , itemMoved(false)
70         , eventQueued(false)
71     {
72     }
73
74     void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &);
75     void deliverMoveEvent();
76     void deliverEvent(QQuickCanvas *canvas, QEvent *event);
77     void start() { start(supportedActions); }
78     void start(Qt::DropActions supportedActions);
79     void setTarget(QQuickItem *item);
80
81     QQuickDragGrabber dragGrabber;
82
83     QQmlGuard<QObject> source;
84     QQmlGuard<QObject> target;
85     QQuickItem *attachedItem;
86     QQuickDragMimeData *mimeData;
87     Qt::DropAction proposedAction;
88     Qt::DropActions supportedActions;
89     bool active : 1;
90     bool listening : 1;
91     bool inEvent : 1;
92     bool itemMoved : 1;
93     bool eventQueued : 1;
94     QPointF hotSpot;
95     QStringList keys;
96 };
97
98 /*!
99     \qmlclass Drag QQuickDrag
100     \inqmlmodule QtQuick 2
101     \ingroup qtquick-interaction
102     \brief For specifying drag and drop events for moved Items
103
104     Using the Drag attached property any Item can made a source of drag and drop
105     events within a scene.
106
107     When a drag is \l active on an item any change in that items position will
108     generate a drag events that will be sent to any DropArea that intersects
109     the with new  position of the item.  Other items which implement drag and
110      drop event handlers can also receive these events.
111
112     The following snippet shows how an item can be dragged with a MouseArea.
113     However, dragging is not limited to mouse drags, anything that can move an item
114     can generate drag events, this can include touch events, animations and bindings.
115
116     \snippet qml/drag.qml 0
117
118     A drag can be terminated either by canceling it with Drag.cancel() or setting
119     Drag.active to false, or it can be terminated with a drop event by calling
120     Drag.drop().  If the drop event is accepted Drag.drop() will return the
121     \l {supportedActions}{drop action} chosen by the recipient of the event,
122     otherwise it will return Qt.IgnoreAction.
123
124 */
125
126 void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry)
127 {
128     Q_Q(QQuickDragAttached);
129     if (newGeometry.topLeft() == oldGeometry.topLeft() || !active || itemMoved)
130         return;
131
132     itemMoved = true;
133
134     if (!eventQueued) {
135         eventQueued = true;
136         QCoreApplication::postEvent(q, new QEvent(QEvent::User));
137     }
138 }
139
140 void QQuickDragAttachedPrivate::deliverMoveEvent()
141 {
142     Q_Q(QQuickDragAttached);
143
144     itemMoved = false;
145     if (QQuickCanvas *canvas = attachedItem->canvas()) {
146         QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
147         QDragMoveEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
148         QQuickDropEventEx::setProposedAction(&event, proposedAction);
149         deliverEvent(canvas, &event);
150         if (target != dragGrabber.target()) {
151             target = dragGrabber.target();
152             emit q->targetChanged();
153         }
154     }
155 }
156
157 void QQuickDragAttachedPrivate::deliverEvent(QQuickCanvas *canvas, QEvent *event)
158 {
159     Q_ASSERT(!inEvent);
160     inEvent = true;
161     QQuickCanvasPrivate::get(canvas)->deliverDragEvent(&dragGrabber, event);
162     inEvent = false;
163 }
164
165 bool QQuickDragAttached::event(QEvent *event)
166 {
167     Q_D(QQuickDragAttached);
168
169     if (event->type() == QEvent::User) {
170         d->eventQueued = false;
171         if (d->itemMoved)
172             d->deliverMoveEvent();
173         return true;
174     } else {
175         return QObject::event(event);
176     }
177 }
178
179 QQuickDragAttached::QQuickDragAttached(QObject *parent)
180     : QObject(*new QQuickDragAttachedPrivate, parent)
181 {
182     Q_D(QQuickDragAttached);
183     d->attachedItem = qobject_cast<QQuickItem *>(parent);
184     d->source = d->attachedItem;
185 }
186
187 QQuickDragAttached::~QQuickDragAttached()
188 {
189     Q_D(QQuickDragAttached);
190     delete d->mimeData;
191 }
192
193 /*!
194     \qmlattachedproperty bool QtQuick2::Drag::active
195
196     This property holds whether a drag event sequence is currently active.
197
198     Setting this property to true will send a QDragEnter event to the scene
199     with the item's current position.  Setting it to false will send a
200     QDragLeave event.
201
202     While a drag is active any change in an item's position will send a QDragMove
203     event with item's new position to the scene.
204 */
205
206 bool QQuickDragAttached::isActive() const
207 {
208     Q_D(const QQuickDragAttached);
209     return d->active;
210 }
211
212 void QQuickDragAttached::setActive(bool active)
213 {
214     Q_D(QQuickDragAttached);
215     if (d->active != active) {
216         if (d->inEvent)
217             qmlInfo(this) << "active cannot be changed from within a drag event handler";
218         else if (active)
219             d->start(d->supportedActions);
220         else
221             cancel();
222     }
223 }
224
225 /*!
226     \qmlattachedproperty Object QtQuick2::Drag::source
227
228     This property holds an object that is identified to recipients of drag events as
229     the source of the events.  By default this is the item Drag property is attached to.
230
231     Changes to source while a Drag is active don't take effect until a new drag is started.
232 */
233
234 QObject *QQuickDragAttached::source() const
235 {
236     Q_D(const QQuickDragAttached);
237     return d->source;
238 }
239
240 void QQuickDragAttached::setSource(QObject *item)
241 {
242     Q_D(QQuickDragAttached);
243     if (d->source != item) {
244         d->source = item;
245         emit sourceChanged();
246     }
247 }
248
249 void QQuickDragAttached::resetSource()
250 {
251     Q_D(QQuickDragAttached);
252     if (d->source != d->attachedItem) {
253         d->source = d->attachedItem;
254         emit sourceChanged();
255     }
256 }
257
258 /*!
259     \qmlattachedproperty Object QtQuick2::Drag::target
260
261     While a drag is active this property holds the last object to accept an
262     enter event from the dragged item, if the current drag position doesn't
263     intersect any accepting targets it is null.
264
265     When a drag is not active this property holds the object that accepted
266     the drop event that ended the drag, if no object accepted the drop or
267     the drag was canceled the target will then be null.
268 */
269
270 QObject *QQuickDragAttached::target() const
271 {
272     Q_D(const QQuickDragAttached);
273     return d->target;
274 }
275
276 /*!
277     \qmlattachedproperty QPointF QtQuick2::Drag::hotSpot
278
279     This property holds the drag position relative to the top left of the item.
280
281     By default this is (0, 0).
282
283     Changes to hotSpot will take effect when the next event is sent.
284 */
285
286 QPointF QQuickDragAttached::hotSpot() const
287 {
288     Q_D(const QQuickDragAttached);
289     return d->hotSpot;
290 }
291
292 void QQuickDragAttached::setHotSpot(const QPointF &hotSpot)
293 {
294     Q_D(QQuickDragAttached);
295     if (d->hotSpot != hotSpot) {
296         d->hotSpot = hotSpot;
297
298         if (d->active) {
299             d->itemMoved = true;
300
301             if (!d->eventQueued) {
302                 d->eventQueued = true;
303                 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
304             }
305         }
306
307         emit hotSpotChanged();
308     }
309 }
310
311 /*!
312     \qmlattachedproperty stringlist QtQuick2::Drag::keys
313
314     This property holds a list of keys that can be used by a DropArea to filter drag events.
315
316     Changes to keys while a Drag is active don't take effect until a new drag is started.
317 */
318
319 QStringList QQuickDragAttached::keys() const
320 {
321     Q_D(const QQuickDragAttached);
322     return d->keys;
323 }
324
325 void QQuickDragAttached::setKeys(const QStringList &keys)
326 {
327     Q_D(QQuickDragAttached);
328     if (d->keys != keys) {
329         d->keys = keys;
330         emit keysChanged();
331     }
332 }
333
334 /*!
335     \qmlattachedproperty flags QtQuick2::Drag::supportedActions
336
337     This property holds return values of Drag.drop() supported by the drag source.
338
339     Changes to supportedActions while a Drag is active don't take effect
340     until a new drag is started.
341 */
342
343 Qt::DropActions QQuickDragAttached::supportedActions() const
344 {
345     Q_D(const QQuickDragAttached);
346     return d->supportedActions;
347 }
348
349 void QQuickDragAttached::setSupportedActions(Qt::DropActions actions)
350 {
351     Q_D(QQuickDragAttached);
352     if (d->supportedActions != actions) {
353         d->supportedActions = actions;
354         emit supportedActionsChanged();
355     }
356 }
357
358 /*!
359     \qmlattachedproperty enumeration QtQuick2::Drag::proposedAction
360
361     This property holds an action that is recommended by the drag source as a
362     return value from Drag.drop().
363
364     Changes to proposedAction will take effect when the next event is sent.
365 */
366
367 Qt::DropAction QQuickDragAttached::proposedAction() const
368 {
369     Q_D(const QQuickDragAttached);
370     return d->proposedAction;
371 }
372
373 void QQuickDragAttached::setProposedAction(Qt::DropAction action)
374 {
375     Q_D(QQuickDragAttached);
376     if (d->proposedAction != action) {
377         d->proposedAction = action;
378         emit proposedActionChanged();
379         // send a move event with the new default action if active?
380     }
381 }
382
383 void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
384 {
385     Q_Q(QQuickDragAttached);
386     Q_ASSERT(!active);
387
388     if (QQuickCanvas *canvas = attachedItem ? attachedItem->canvas() : 0) {
389         if (!mimeData)
390             mimeData = new QQuickDragMimeData;
391         if (!listening) {
392             QQuickItemPrivate::get(attachedItem)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
393             listening = true;
394         }
395
396         mimeData->m_source = source;
397         mimeData->m_supportedActions = supportedActions;
398         mimeData->m_keys = keys;
399         active = true;
400
401         QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint();
402         QDragEnterEvent event(scenePos, supportedActions, mimeData, Qt::NoButton, Qt::NoModifier);
403         QQuickDropEventEx::setProposedAction(&event, proposedAction);
404         deliverEvent(canvas, &event);
405
406         emit q->activeChanged();
407         if (target != dragGrabber.target()) {
408             target = dragGrabber.target();
409             emit q->targetChanged();
410         }
411     }
412 }
413
414 /*!
415     \qmlattachedmethod void QtQuick2::Drag::start(flags supportedActions)
416
417     Starts sending drag events.
418
419     The optional \a supportedActions argument can be used to override the \l supportedActions
420     property for the started sequence.
421 */
422
423 void QQuickDragAttached::start(QQmlV8Function *args)
424 {
425     Q_D(QQuickDragAttached);
426     if (d->inEvent) {
427         qmlInfo(this) << "start() cannot be called from within a drag event handler";
428         return;
429     }
430
431     if (d->active)
432         cancel();
433
434     Qt::DropActions supportedActions = d->supportedActions;
435     // check arguments for supportedActions, maybe data?
436     if (args->Length() >= 1) {
437         v8::Local<v8::Value> v = (*args)[0];
438         if (v->IsInt32())
439             supportedActions = Qt::DropActions(v->Int32Value());
440     }
441
442     d->start(supportedActions);
443 }
444
445 /*!
446     \qmlattachedmethod enum QtQuick2::Drag::drop()
447
448     Ends a drag sequence by sending a drop event to the target item.
449
450     Returns the action accepted by the target item.  If the target item or a parent doesn't accept
451     the drop event then Qt.IgnoreAction will be returned.
452
453     The returned drop action may be one of:
454
455     \list
456     \li Qt.CopyAction Copy the data to the target
457     \li Qt.MoveAction Move the data from the source to the target
458     \li Qt.LinkAction Create a link from the source to the target.
459     \li Qt.IgnoreAction Ignore the action (do nothing with the data).
460     \endlist
461
462 */
463
464 int QQuickDragAttached::drop()
465 {
466     Q_D(QQuickDragAttached);
467     Qt::DropAction acceptedAction = Qt::IgnoreAction;
468
469     if (d->inEvent) {
470         qmlInfo(this) << "drop() cannot be called from within a drag event handler";
471         return acceptedAction;
472     }
473
474     if (d->itemMoved)
475         d->deliverMoveEvent();
476
477     if (!d->active)
478         return acceptedAction;
479     d->active = false;
480
481     QObject *target = 0;
482
483     if (QQuickCanvas *canvas = d->attachedItem->canvas()) {
484         QPoint scenePos = d->attachedItem->mapToScene(d->hotSpot).toPoint();
485
486         QDropEvent event(
487                 scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier);
488         QQuickDropEventEx::setProposedAction(&event, d->proposedAction);
489         d->deliverEvent(canvas, &event);
490
491         if (event.isAccepted()) {
492             acceptedAction = event.dropAction();
493             target = d->dragGrabber.target();
494         }
495     }
496
497     if (d->target != target) {
498         d->target = target;
499         emit targetChanged();
500     }
501
502     emit activeChanged();
503     return acceptedAction;
504 }
505
506 /*!
507     \qmlattachedmethod void QtQuick2::Drag::cancel()
508
509     Ends a drag sequence.
510 */
511
512 void QQuickDragAttached::cancel()
513 {
514     Q_D(QQuickDragAttached);
515
516     if (d->inEvent) {
517         qmlInfo(this) << "cancel() cannot be called from within a drag event handler";
518         return;
519     }
520
521     if (!d->active)
522         return;
523     d->active = false;
524     d->itemMoved = false;
525
526     if (QQuickCanvas *canvas = d->attachedItem->canvas()) {
527         QDragLeaveEvent event;
528         d->deliverEvent(canvas, &event);
529     }
530
531     if (d->target) {
532         d->target = 0;
533         emit targetChanged();
534     }
535     emit activeChanged();
536 }
537
538 QT_END_NAMESPACE