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

source code of qtbase/src/gui/kernel/qdrag.cpp