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