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