1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui 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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <qdrag.h>
41 #include "private/qguiapplication_p.h"
42 #include "qpa/qplatformintegration.h"
43 #include "qpa/qplatformdrag.h"
44 #include <qpixmap.h>
45 #include <qpoint.h>
46 #include "qdnd_p.h"
47 
48 QT_BEGIN_NAMESPACE
49 
50 /*!
51     \class QDrag
52     \inmodule QtGui
53     \ingroup draganddrop
54     \brief The QDrag class provides support for MIME-based drag and drop data
55     transfer.
56 
57     Drag and drop is an intuitive way for users to copy or move data around in an
58     application, and is used in many desktop environments as a mechanism for copying
59     data between applications. Drag and drop support in Qt is centered around the
60     QDrag class that handles most of the details of a drag and drop operation.
61 
62     The data to be transferred by the drag and drop operation is contained in a
63     QMimeData object. This is specified with the setMimeData() function in the
64     following way:
65 
66     \snippet dragging/mainwindow.cpp 1
67 
68     Note that setMimeData() assigns ownership of the QMimeData object to the
69     QDrag object. The QDrag must be constructed on the heap with a parent QObject
70     to ensure that Qt can clean up after the drag and drop operation has been
71     completed.
72 
73     A pixmap can be used to represent the data while the drag is in
74     progress, and will move with the cursor to the drop target. This
75     pixmap typically shows an icon that represents the MIME type of
76     the data being transferred, but any pixmap can be set with
77     setPixmap(). The cursor's hot spot can be given a position
78     relative to the top-left corner of the pixmap with the
79     setHotSpot() function. The following code positions the pixmap so
80     that the cursor's hot spot points to the center of its bottom
81     edge:
82 
83     \snippet separations/finalwidget.cpp 2
84 
85     \note On X11, the pixmap may not be able to keep up with the mouse
86     movements if the hot spot causes the pixmap to be displayed
87     directly under the cursor.
88 
89     The source and target widgets can be found with source() and target().
90     These functions are often used to determine whether drag and drop operations
91     started and finished at the same widget, so that special behavior can be
92     implemented.
93 
94     QDrag only deals with the drag and drop operation itself. It is up to the
95     developer to decide when a drag operation begins, and how a QDrag object should
96     be constructed and used. For a given widget, it is often necessary to
97     reimplement \l{QWidget::mousePressEvent()}{mousePressEvent()} to determine
98     whether the user has pressed a mouse button, and reimplement
99     \l{QWidget::mouseMoveEvent()}{mouseMoveEvent()} to check whether a QDrag is
100     required.
101 
102     \sa {Drag and Drop}, QClipboard, QMimeData, QMacPasteboardMime,
103         {Draggable Icons Example}, {Draggable Text Example}, {Drop Site Example},
104         {Fridge Magnets Example}
105 */
106 
107 /*!
108     Constructs a new drag object for the widget specified by \a dragSource.
109 */
QDrag(QObject * dragSource)110 QDrag::QDrag(QObject *dragSource)
111     : QObject(*new QDragPrivate, dragSource)
112 {
113     Q_D(QDrag);
114     d->source = dragSource;
115     d->target = nullptr;
116     d->data = nullptr;
117     d->hotspot = QPoint(-10, -10);
118     d->executed_action = Qt::IgnoreAction;
119     d->supported_actions = Qt::IgnoreAction;
120     d->default_action = Qt::IgnoreAction;
121 }
122 
123 /*!
124     Destroys the drag object.
125 */
~QDrag()126 QDrag::~QDrag()
127 {
128     Q_D(QDrag);
129     delete d->data;
130 }
131 
132 /*!
133     Sets the data to be sent to the given MIME \a data. Ownership of the data is
134     transferred to the QDrag object.
135 */
setMimeData(QMimeData * data)136 void QDrag::setMimeData(QMimeData *data)
137 {
138     Q_D(QDrag);
139     if (d->data == data)
140         return;
141     if (d->data != nullptr)
142         delete d->data;
143     d->data = data;
144 }
145 
146 /*!
147     Returns the MIME data that is encapsulated by the drag object.
148 */
mimeData() const149 QMimeData *QDrag::mimeData() const
150 {
151     Q_D(const QDrag);
152     return d->data;
153 }
154 
155 /*!
156     Sets \a pixmap as the pixmap used to represent the data in a drag
157     and drop operation. You can only set a pixmap before the drag is
158     started.
159 */
setPixmap(const QPixmap & pixmap)160 void QDrag::setPixmap(const QPixmap &pixmap)
161 {
162     Q_D(QDrag);
163     d->pixmap = pixmap;
164 }
165 
166 /*!
167     Returns the pixmap used to represent the data in a drag and drop operation.
168 */
pixmap() const169 QPixmap QDrag::pixmap() const
170 {
171     Q_D(const QDrag);
172     return d->pixmap;
173 }
174 
175 /*!
176     Sets the position of the hot spot relative to the top-left corner of the
177     pixmap used to the point specified by \a hotspot.
178 
179     \b{Note:} on X11, the pixmap may not be able to keep up with the mouse
180     movements if the hot spot causes the pixmap to be displayed
181     directly under the cursor.
182 */
setHotSpot(const QPoint & hotspot)183 void QDrag::setHotSpot(const QPoint& hotspot)
184 {
185     Q_D(QDrag);
186     d->hotspot = hotspot;
187 }
188 
189 /*!
190     Returns the position of the hot spot relative to the top-left corner of the
191     cursor.
192 */
hotSpot() const193 QPoint QDrag::hotSpot() const
194 {
195     Q_D(const QDrag);
196     return d->hotspot;
197 }
198 
199 /*!
200     Returns the source of the drag object. This is the widget where the drag
201     and drop operation originated.
202 */
source() const203 QObject *QDrag::source() const
204 {
205     Q_D(const QDrag);
206     return d->source;
207 }
208 
209 /*!
210     Returns the target of the drag and drop operation. This is the widget where
211     the drag object was dropped.
212 */
target() const213 QObject *QDrag::target() const
214 {
215     Q_D(const QDrag);
216     return d->target;
217 }
218 
219 /*!
220     \since 4.3
221 
222     Starts the drag and drop operation and returns a value indicating the requested
223     drop action when it is completed. The drop actions that the user can choose
224     from are specified in \a supportedActions. The default proposed action will be selected
225     among the allowed actions in the following order: Move, Copy and Link.
226 
227     \b{Note:} On Linux and \macos, the drag and drop operation
228     can take some time, but this function does not block the event
229     loop. Other events are still delivered to the application while
230     the operation is performed. On Windows, the Qt event loop is
231     blocked during the operation.
232 
233     \sa cancel()
234 */
235 
exec(Qt::DropActions supportedActions)236 Qt::DropAction QDrag::exec(Qt::DropActions supportedActions)
237 {
238     return exec(supportedActions, Qt::IgnoreAction);
239 }
240 
241 /*!
242     \since 4.3
243 
244     Starts the drag and drop operation and returns a value indicating the requested
245     drop action when it is completed. The drop actions that the user can choose
246     from are specified in \a supportedActions.
247 
248     The \a defaultDropAction determines which action will be proposed when the user performs a
249     drag without using modifier keys.
250 
251     \b{Note:} On Linux and \macos, the drag and drop operation
252     can take some time, but this function does not block the event
253     loop. Other events are still delivered to the application while
254     the operation is performed. On Windows, the Qt event loop is
255     blocked during the operation. However, QDrag::exec() on
256     Windows causes processEvents() to be called frequently to keep the GUI responsive.
257     If any loops or operations are called while a drag operation is active, it will block the drag operation.
258 */
259 
exec(Qt::DropActions supportedActions,Qt::DropAction defaultDropAction)260 Qt::DropAction QDrag::exec(Qt::DropActions supportedActions, Qt::DropAction defaultDropAction)
261 {
262     Q_D(QDrag);
263     if (!d->data) {
264         qWarning("QDrag: No mimedata set before starting the drag");
265         return d->executed_action;
266     }
267     Qt::DropAction transformedDefaultDropAction = Qt::IgnoreAction;
268 
269     if (defaultDropAction == Qt::IgnoreAction) {
270         if (supportedActions & Qt::MoveAction) {
271             transformedDefaultDropAction = Qt::MoveAction;
272         } else if (supportedActions & Qt::CopyAction) {
273             transformedDefaultDropAction = Qt::CopyAction;
274         } else if (supportedActions & Qt::LinkAction) {
275             transformedDefaultDropAction = Qt::LinkAction;
276         }
277     } else {
278         transformedDefaultDropAction = defaultDropAction;
279     }
280     d->supported_actions = supportedActions;
281     d->default_action = transformedDefaultDropAction;
282     QPointer<QDrag> self = this;
283     auto executed_action = QDragManager::self()->drag(self.data());
284     if (self.isNull())
285         return Qt::IgnoreAction;
286     d->executed_action = executed_action;
287     return d->executed_action;
288 }
289 
290 #if QT_DEPRECATED_SINCE(5, 13)
291 /*!
292     \obsolete
293 
294     \b{Note:} It is recommended to use exec() instead of this function.
295 
296     Starts the drag and drop operation and returns a value indicating the requested
297     drop action when it is completed. The drop actions that the user can choose
298     from are specified in \a request. Qt::CopyAction is always allowed.
299 
300     \b{Note:} Although the drag and drop operation can take some time, this function
301     does not block the event loop. Other events are still delivered to the application
302     while the operation is performed.
303 
304     \sa exec()
305 */
start(Qt::DropActions request)306 Qt::DropAction QDrag::start(Qt::DropActions request)
307 {
308     Q_D(QDrag);
309     if (!d->data) {
310         qWarning("QDrag: No mimedata set before starting the drag");
311         return d->executed_action;
312     }
313     d->supported_actions = request | Qt::CopyAction;
314     d->default_action = Qt::IgnoreAction;
315     d->executed_action = QDragManager::self()->drag(this);
316     return d->executed_action;
317 }
318 #endif
319 
320 /*!
321     Sets the drag \a cursor for the \a action. This allows you
322     to override the default native cursors. To revert to using the
323     native cursor for \a action pass in a null QPixmap as \a cursor.
324 
325     Note: setting the drag cursor for IgnoreAction may not work on
326     all platforms. X11 and macOS has been tested to work. Windows
327     does not support it.
328 */
setDragCursor(const QPixmap & cursor,Qt::DropAction action)329 void QDrag::setDragCursor(const QPixmap &cursor, Qt::DropAction action)
330 {
331     Q_D(QDrag);
332     if (cursor.isNull())
333         d->customCursors.remove(action);
334     else
335         d->customCursors[action] = cursor;
336 }
337 
338 /*!
339     Returns the drag cursor for the \a action.
340 
341     \since 5.0
342 */
343 
dragCursor(Qt::DropAction action) const344 QPixmap QDrag::dragCursor(Qt::DropAction action) const
345 {
346     typedef QMap<Qt::DropAction, QPixmap>::const_iterator Iterator;
347 
348     Q_D(const QDrag);
349     const Iterator it = d->customCursors.constFind(action);
350     if (it != d->customCursors.constEnd())
351         return it.value();
352 
353     Qt::CursorShape shape = Qt::ForbiddenCursor;
354     switch (action) {
355     case Qt::MoveAction:
356         shape = Qt::DragMoveCursor;
357         break;
358     case Qt::CopyAction:
359         shape = Qt::DragCopyCursor;
360         break;
361     case Qt::LinkAction:
362         shape = Qt::DragLinkCursor;
363         break;
364     default:
365         shape = Qt::ForbiddenCursor;
366     }
367     return QGuiApplicationPrivate::instance()->getPixmapCursor(shape);
368 }
369 
370 /*!
371     Returns the set of possible drop actions for this drag operation.
372 
373     \sa exec(), defaultAction()
374 */
supportedActions() const375 Qt::DropActions QDrag::supportedActions() const
376 {
377     Q_D(const QDrag);
378     return d->supported_actions;
379 }
380 
381 
382 /*!
383     Returns the default proposed drop action for this drag operation.
384 
385     \sa exec(), supportedActions()
386 */
defaultAction() const387 Qt::DropAction QDrag::defaultAction() const
388 {
389     Q_D(const QDrag);
390     return d->default_action;
391 }
392 
393 /*!
394     Cancels a drag operation initiated by Qt.
395 
396     \note This is currently implemented on Windows and X11.
397 
398     \since 5.7
399     \sa exec()
400 */
cancel()401 void QDrag::cancel()
402 {
403     if (QPlatformDrag *platformDrag = QGuiApplicationPrivate::platformIntegration()->drag())
404         platformDrag->cancelDrag();
405 }
406 
407 /*!
408     \fn void QDrag::actionChanged(Qt::DropAction action)
409 
410     This signal is emitted when the \a action associated with the
411     drag changes.
412 
413     \sa targetChanged()
414 */
415 
416 /*!
417     \fn void QDrag::targetChanged(QObject *newTarget)
418 
419     This signal is emitted when the target of the drag and drop
420     operation changes, with \a newTarget the new target.
421 
422     \sa target(), actionChanged()
423 */
424 
425 QT_END_NAMESPACE
426