Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcocoadrag.mm
Go to the documentation of this file.
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 <AppKit/AppKit.h>
5#include <UniformTypeIdentifiers/UTCoreTypes.h>
6
7#include "qcocoadrag.h"
8#include "qmacclipboard.h"
9#include "qcocoahelpers.h"
10#include <QtGui/private/qcoregraphics_p.h>
11#include <QtGui/qutimimeconverter.h>
12#include <QtCore/qsysinfo.h>
13#include <QtCore/private/qcore_mac_p.h>
14
15#include <vector>
16
18
19using namespace Qt::StringLiterals;
20
21static const int dragImageMaxChars = 26;
22
24 m_drag(nullptr)
25{
26 m_lastEvent = nil;
27 m_lastView = nil;
28}
29
31{
32 [m_lastEvent release];
33}
34
36{
37 [m_lastEvent release];
38 m_lastEvent = [event copy];
39 m_lastView = view;
40}
41
43{
44 if (m_drag)
45 return m_drag->mimeData();
46
47 return nullptr;
48}
49
50Qt::DropAction QCocoaDrag::defaultAction(Qt::DropActions possibleActions,
51 Qt::KeyboardModifiers modifiers) const
52{
53 Qt::DropAction default_action = Qt::IgnoreAction;
54
55 if (currentDrag()) {
56 default_action = currentDrag()->defaultAction();
57 possibleActions = currentDrag()->supportedActions();
58 }
59
60 if (default_action == Qt::IgnoreAction) {
61 //This means that the drag was initiated by QDrag::start and we need to
62 //preserve the old behavior
63 default_action = Qt::CopyAction;
64 }
65
67 default_action = Qt::LinkAction;
68 else if (modifiers & Qt::AltModifier)
69 default_action = Qt::CopyAction;
71 default_action = Qt::MoveAction;
72
73#ifdef QDND_DEBUG
74 qDebug("possible actions : %s", dragActionsToString(possibleActions).latin1());
75#endif
76
77 // Check if the action determined is allowed
78 if (!(possibleActions & default_action)) {
79 if (possibleActions & Qt::CopyAction)
80 default_action = Qt::CopyAction;
81 else if (possibleActions & Qt::MoveAction)
82 default_action = Qt::MoveAction;
83 else if (possibleActions & Qt::LinkAction)
84 default_action = Qt::LinkAction;
85 else
86 default_action = Qt::IgnoreAction;
87 }
88
89#ifdef QDND_DEBUG
90 qDebug("default action : %s", dragActionsToString(default_action).latin1());
91#endif
92
93 return default_action;
94}
95
96
98{
99 m_drag = o;
100 m_executed_drop_action = Qt::IgnoreAction;
101
102 QMacPasteboard dragBoard(CFStringRef(NSPasteboardNameDrag), QUtiMimeConverter::HandlerScopeFlag::DnD);
103 m_drag->mimeData()->setData("application/x-qt-mime-type-name"_L1, QByteArray("dummy"));
104 dragBoard.setMimeData(m_drag->mimeData(), QMacPasteboard::LazyRequest);
105
106 if (maybeDragMultipleItems())
107 return m_executed_drop_action;
108
109 QPoint hotSpot = m_drag->hotSpot();
110 QPixmap pm = dragPixmap(m_drag, hotSpot);
111 NSImage *dragImage = [NSImage imageFromQImage:pm.toImage()];
112 Q_ASSERT(dragImage);
113
114 NSPoint event_location = [m_lastEvent locationInWindow];
115 NSWindow *theWindow = [m_lastEvent window];
116 Q_ASSERT(theWindow);
117 event_location.x -= hotSpot.x();
118 CGFloat flippedY = dragImage.size.height - hotSpot.y();
119 event_location.y -= flippedY;
120 NSSize mouseOffset_unused = NSMakeSize(0.0, 0.0);
121 NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSPasteboardNameDrag];
122
123 [theWindow dragImage:dragImage
124 at:event_location
125 offset:mouseOffset_unused
126 event:m_lastEvent
127 pasteboard:pboard
128 source:m_lastView
129 slideBack:YES];
130
131 m_drag = nullptr;
132 return m_executed_drop_action;
133}
134
135bool QCocoaDrag::maybeDragMultipleItems()
136{
137 Q_ASSERT(m_drag && m_drag->mimeData());
138 Q_ASSERT(m_executed_drop_action == Qt::IgnoreAction);
139
141 // -dragImage: stopped working in 10.14 first.
142 return false;
143 }
144
146
147 NSView *view = m_lastView ? m_lastView : m_lastEvent.window.contentView;
148 if (![view respondsToSelector:@selector(draggingSession:sourceOperationMaskForDraggingContext:)])
150
151 auto *sourceView = static_cast<NSView<NSDraggingSource>*>(view);
152
153 const auto &qtUrls = m_drag->mimeData()->urls();
154 NSPasteboard *dragBoard = [NSPasteboard pasteboardWithName:NSPasteboardNameDrag];
155
156 if (qtUrls.size() <= 1) {
157 // Good old -dragImage: works perfectly for this ...
158 return false;
159 }
160
161 std::vector<NSPasteboardItem *> nonUrls;
162 for (NSPasteboardItem *item in dragBoard.pasteboardItems) {
163 bool isUrl = false;
164 for (NSPasteboardType type in item.types) {
165 if ([type isEqualToString:UTTypeFileURL.identifier]) {
166 isUrl = true;
167 break;
168 }
169 }
170
171 if (!isUrl)
172 nonUrls.push_back(item);
173 }
174
175 QPoint hotSpot = m_drag->hotSpot();
176 const auto pixmap = dragPixmap(m_drag, hotSpot);
177 NSImage *dragImage = [NSImage imageFromQImage:pixmap.toImage()];
178 Q_ASSERT(dragImage);
179
180 NSMutableArray<NSDraggingItem *> *dragItems = [[[NSMutableArray alloc] init] autorelease];
181 const NSPoint itemLocation = m_drag->hotSpot().toCGPoint();
182 // 0. We start from URLs, which can be actually in a list (thus technically
183 // only ONE item in the pasteboard. The fact it's only one does not help, we are
184 // still getting an exception because of the number of items/images mismatch ...
185 // We only set the image for the first item and nil for the rest, the image already
186 // contains a combined picture for all urls we drag.
187 auto imageOrNil = dragImage;
188 for (const auto &qtUrl : qtUrls) {
189 if (!qtUrl.isValid())
190 continue;
191
192 if (qtUrl.isRelative()) // NSPasteboardWriting rejects such items.
193 continue;
194
195 NSURL *nsUrl = qtUrl.toNSURL();
196 auto *newItem = [[[NSDraggingItem alloc] initWithPasteboardWriter:nsUrl] autorelease];
197 const NSRect itemFrame = NSMakeRect(itemLocation.x, itemLocation.y,
198 dragImage.size.width,
199 dragImage.size.height);
200
201 [newItem setDraggingFrame:itemFrame contents:imageOrNil];
202 imageOrNil = nil;
203 [dragItems addObject:newItem];
204 }
205 // 1. Repeat for non-url items, if any:
206 for (auto *pbItem : nonUrls) {
207 auto *newItem = [[[NSDraggingItem alloc] initWithPasteboardWriter:pbItem] autorelease];
208 const NSRect itemFrame = NSMakeRect(itemLocation.x, itemLocation.y,
209 dragImage.size.width,
210 dragImage.size.height);
211 [newItem setDraggingFrame:itemFrame contents:imageOrNil];
212 [dragItems addObject:newItem];
213 }
214
215 [sourceView beginDraggingSessionWithItems:dragItems event:m_lastEvent source:sourceView];
216 QEventLoop eventLoop;
217 QScopedValueRollback updateGuard(m_internalDragLoop, &eventLoop);
218 eventLoop.exec();
219 return true;
220}
221
223{
224 m_executed_drop_action = act;
225}
226
228{
229 if (m_internalDragLoop) {
230 Q_ASSERT(m_internalDragLoop->isRunning());
231 m_internalDragLoop->exit();
232 }
233}
234
235
236QPixmap QCocoaDrag::dragPixmap(QDrag *drag, QPoint &hotSpot) const
237{
238 const QMimeData* data = drag->mimeData();
239 QPixmap pm = drag->pixmap();
240
241 if (pm.isNull()) {
242 QFont f(qApp->font());
243 f.setPointSize(12);
244 QFontMetrics fm(f);
245
246 if (data->hasImage()) {
247 QImage img = data->imageData().value<QImage>();
248 if (!img.isNull()) {
249 pm = QPixmap::fromImage(std::move(img)).scaledToWidth(dragImageMaxChars *fm.averageCharWidth());
250 }
251 }
252
253 if (pm.isNull() && (data->hasText() || data->hasUrls()) ) {
254 QString s = data->hasText() ? data->text() : data->urls().constFirst().toString();
255 if (s.length() > dragImageMaxChars)
256 s = s.left(dragImageMaxChars -3) + QChar(0x2026);
257 if (!s.isEmpty()) {
258 const int width = fm.horizontalAdvance(s);
259 const int height = fm.height();
260 if (width > 0 && height > 0) {
261 qreal dpr = 1.0;
263 if (!window && drag->source()->metaObject()->indexOfMethod("_q_closestWindowHandle()") != -1) {
264 QMetaObject::invokeMethod(drag->source(), "_q_closestWindowHandle",
266 }
267 if (!window)
268 window = qApp->focusWindow();
269
270 if (window)
271 dpr = window->devicePixelRatio();
272
273 pm = QPixmap(width * dpr, height * dpr);
275 QPainter p(&pm);
276 p.fillRect(0, 0, pm.width(), pm.height(), Qt::color0);
277 p.setPen(Qt::color1);
278 p.setFont(f);
279 p.drawText(0, fm.ascent(), s);
280 p.end();
281 hotSpot = QPoint(pm.width() / 2, pm.height() / 2);
282 }
283 }
284 }
285 }
286
287 if (pm.isNull())
288 pm = defaultPixmap();
289
290 return pm;
291}
292
293QCocoaDropData::QCocoaDropData(NSPasteboard *pasteboard)
294{
295 dropPasteboard = reinterpret_cast<CFStringRef>(const_cast<const NSString *>([pasteboard name]));
296 CFRetain(dropPasteboard);
297}
298
303
305{
307 PasteboardRef board;
308 if (PasteboardCreate(dropPasteboard, &board) != noErr) {
309 qDebug("DnD: Cannot get PasteBoard!");
310 return formats;
311 }
313 return formats;
314}
315
317{
319 PasteboardRef board;
320 if (PasteboardCreate(dropPasteboard, &board) != noErr) {
321 qDebug("DnD: Cannot get PasteBoard!");
322 return data;
323 }
325 CFRelease(board);
326 return data;
327}
328
330{
331 bool has = false;
332 PasteboardRef board;
333 if (PasteboardCreate(dropPasteboard, &board) != noErr) {
334 qDebug("DnD: Cannot get PasteBoard!");
335 return has;
336 }
338 CFRelease(board);
339 return has;
340}
341
342
344
\inmodule QtCore
void setLastMouseEvent(NSEvent *event, NSView *view)
Definition qcocoadrag.mm:35
Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const override
Definition qcocoadrag.mm:50
Qt::DropAction drag(QDrag *m_drag) override
Definition qcocoadrag.mm:97
void setAcceptedAction(Qt::DropAction act)
QMimeData * dragMimeData()
Definition qcocoadrag.mm:42
void exitDragLoop()
QVariant retrieveData_sys(const QString &mimeType, QMetaType type) const
QStringList formats_sys() const
QCocoaDropData(NSPasteboard *pasteboard)
bool hasFormat_sys(const QString &mimeType) const
CFStringRef dropPasteboard
Definition qcocoadrag.h:64
\inmodule QtGui
Definition qdrag.h:22
QMimeData * mimeData() const
Returns the MIME data that is encapsulated by the drag object.
Definition qdrag.cpp:114
Qt::DropAction defaultAction() const
Returns the default proposed drop action for this drag operation.
Definition qdrag.cpp:322
QPoint hotSpot() const
Returns the position of the hot spot relative to the top-left corner of the cursor.
Definition qdrag.cpp:158
Qt::DropActions supportedActions() const
Returns the set of possible drop actions for this drag operation.
Definition qdrag.cpp:310
\inmodule QtCore
Definition qeventloop.h:16
int exec(ProcessEventsFlags flags=AllEvents)
Enters the main event loop and waits until exit() is called.
void exit(int returnCode=0)
Tells the event loop to exit with a return code.
bool isRunning() const
Returns true if the event loop is running; otherwise returns false.
\reentrant \inmodule QtGui
\reentrant
Definition qfont.h:22
\inmodule QtGui
Definition qimage.h:37
QStringList formats() const override
Returns a list of formats supported by the object.
void push_back(parameter_type t)
Definition qlist.h:675
QStringList formats() const
QVariant retrieveData(const QString &format) const
bool hasFormat(const QString &format) const
\inmodule QtCore
Definition qmetatype.h:341
\inmodule QtCore
Definition qmimedata.h:16
void setData(const QString &mimetype, const QByteArray &data)
Sets the data associated with the MIME type given by mimeType to the specified data.
QList< QUrl > urls() const
Returns a list of URLs contained within the MIME data object.
static Q_CORE_EXPORT QOperatingSystemVersionBase current()
static constexpr QOperatingSystemVersionBase MacOSMojave
\variable QOperatingSystemVersion::MacOSMojave
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
int height() const
Returns the height of the pixmap.
Definition qpixmap.cpp:480
QImage toImage() const
Converts the pixmap to a QImage.
Definition qpixmap.cpp:408
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:456
int width() const
Returns the width of the pixmap.
Definition qpixmap.cpp:468
void setDevicePixelRatio(qreal scaleFactor)
Sets the device pixel ratio for the pixmap.
Definition qpixmap.cpp:604
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags=Qt::AutoColor)
Converts the given image to a pixmap using the specified flags to control the conversion.
Definition qpixmap.cpp:1437
QDrag * currentDrag() const
static QPixmap defaultPixmap()
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
\inmodule QtCore
Definition qvariant.h:65
\inmodule QtGui
Definition qwindow.h:63
EGLImageKHR int int EGLuint64KHR * modifiers
EGLint EGLint * formats
Combined button and popup list for selecting options.
@ color1
Definition qnamespace.h:29
@ color0
Definition qnamespace.h:28
@ ControlModifier
@ AltModifier
DropAction
@ CopyAction
@ IgnoreAction
@ MoveAction
@ LinkAction
static jboolean copy(JNIEnv *, jobject)
static const int dragImageMaxChars
Definition qcocoadrag.mm:21
float CGFloat
#define qApp
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
const char * mimeType
#define qDebug
[1]
Definition qlogging.h:164
#define Q_RETURN_ARG(Type, data)
Definition qobjectdefs.h:64
GLint GLsizei GLsizei height
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLint GLsizei width
GLenum type
GLenum GLuint GLintptr offset
GLuint name
GLsizei GLsizei GLchar * source
struct _cl_event * event
GLdouble s
[6]
Definition qopenglext.h:235
GLint void * img
Definition qopenglext.h:233
GLuint in
GLfloat GLfloat p
[1]
static QT_BEGIN_NAMESPACE qreal dpr(const QWindow *w)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
double qreal
Definition qtypes.h:187
QWindow * qobject_cast< QWindow * >(QObject *o)
Definition qwindow.h:367
QFileSelector selector
[1]
QObject::connect nullptr
sem release()
QGraphicsItem * item
widget render & pixmap
aWidget window() -> setWindowTitle("New Window Title")
[2]
QAction * at
QQuickView * view
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...