5#include <AppKit/AppKit.h>
6#include <UniformTypeIdentifiers/UTCoreTypes.h>
12#include <QtGui/qfont.h>
13#include <QtGui/qfontmetrics.h>
14#include <QtGui/qpainter.h>
15#include <QtGui/qutimimeconverter.h>
16#include <QtGui/private/qcoregraphics_p.h>
17#include <QtGui/private/qdnd_p.h>
19#include <QtCore/qeventloop.h>
20#include <QtCore/private/qcore_mac_p.h>
26using namespace Qt::StringLiterals;
39 [m_lastEvent release];
42void QCocoaDrag::setLastInputEvent(NSEvent *event, NSView *view)
44 [m_lastEvent release];
45 m_lastEvent = [event copy];
46 if (view != m_lastView)
53 return m_drag->mimeData();
59 Qt::KeyboardModifiers modifiers)
const
61 Qt::DropAction default_action = Qt::IgnoreAction;
64 default_action = currentDrag()->defaultAction();
65 possibleActions = currentDrag()->supportedActions();
68 if (default_action == Qt::IgnoreAction) {
71 default_action = Qt::CopyAction;
74 if (modifiers & Qt::ControlModifier && modifiers & Qt::AltModifier)
75 default_action = Qt::LinkAction;
76 else if (modifiers & Qt::AltModifier)
77 default_action = Qt::CopyAction;
78 else if (modifiers & Qt::ControlModifier)
79 default_action = Qt::MoveAction;
82 qDebug(
"possible actions : %s", dragActionsToString(possibleActions).latin1());
86 if (!(possibleActions & default_action)) {
87 if (possibleActions & Qt::CopyAction)
88 default_action = Qt::CopyAction;
89 else if (possibleActions & Qt::MoveAction)
90 default_action = Qt::MoveAction;
91 else if (possibleActions & Qt::LinkAction)
92 default_action = Qt::LinkAction;
94 default_action = Qt::IgnoreAction;
98 qDebug(
"default action : %s", dragActionsToString(default_action).latin1());
101 return default_action;
107 m_executed_drop_action = Qt::IgnoreAction;
109 [m_lastEvent release];
111 return m_executed_drop_action;
115 QMacPasteboard dragBoard(CFStringRef(NSPasteboardNameDrag), QUtiMimeConverter::HandlerScopeFlag::DnD);
116 m_drag->mimeData()->setData(
"application/x-qt-mime-type-name"_L1, QByteArray(
"dummy"));
119 if (maybeDragMultipleItems())
120 return m_executed_drop_action;
122 QPoint hotSpot = m_drag->hotSpot();
123 QPixmap pm = dragPixmap(m_drag, hotSpot);
124 NSImage *dragImage = [NSImage imageFromQImage:pm.toImage()];
127 NSPoint event_location = [m_lastEvent locationInWindow];
128 NSWindow *theWindow = [m_lastEvent window];
130 event_location.x -= hotSpot.x();
131 CGFloat flippedY = dragImage.size.height - hotSpot.y();
132 event_location.y -= flippedY;
133 NSSize mouseOffset_unused = NSMakeSize(0.0, 0.0);
134 NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSPasteboardNameDrag];
136 [theWindow dragImage:dragImage
138 offset:mouseOffset_unused
145 return m_executed_drop_action;
150 Q_ASSERT(m_drag && m_drag->mimeData());
151 Q_ASSERT(m_executed_drop_action == Qt::IgnoreAction);
153 const QMacAutoReleasePool pool;
155 NSView *view = m_lastView ?
static_cast<NSView*>(m_lastView) : m_lastEvent.window.contentView;
156 if (![view respondsToSelector:@selector(draggingSession:sourceOperationMaskForDraggingContext:)])
159 auto *sourceView =
static_cast<NSView<NSDraggingSource>*>(view);
161 const auto &qtUrls = m_drag->mimeData()->urls();
162 NSPasteboard *dragBoard = [NSPasteboard pasteboardWithName:NSPasteboardNameDrag];
164 if (qtUrls.size() <= 1) {
169 std::vector<NSPasteboardItem *> nonUrls;
170 for (NSPasteboardItem *item in dragBoard.pasteboardItems) {
172 for (NSPasteboardType type in item.types) {
173 if ([type isEqualToString:UTTypeFileURL.identifier]) {
180 nonUrls.push_back(item);
183 QPoint hotSpot = m_drag->hotSpot();
184 const auto pixmap = dragPixmap(m_drag, hotSpot);
185 NSImage *dragImage = [NSImage imageFromQImage:pixmap.toImage()];
188 NSMutableArray<NSDraggingItem *> *dragItems = [[[NSMutableArray alloc] init] autorelease];
189 const NSPoint itemLocation = m_drag->hotSpot().toCGPoint();
195 auto imageOrNil = dragImage;
196 for (
const auto &qtUrl : qtUrls) {
197 if (!qtUrl.isValid())
200 if (qtUrl.isRelative())
203 NSURL *nsUrl = qtUrl.toNSURL();
204 auto *newItem = [[[NSDraggingItem alloc] initWithPasteboardWriter:nsUrl] autorelease];
205 const NSRect itemFrame = NSMakeRect(itemLocation.x, itemLocation.y,
206 dragImage.size.width,
207 dragImage.size.height);
209 [newItem setDraggingFrame:itemFrame contents:imageOrNil];
211 [dragItems addObject:newItem];
214 for (
auto *pbItem : nonUrls) {
215 auto *newItem = [[[NSDraggingItem alloc] initWithPasteboardWriter:pbItem] autorelease];
216 const NSRect itemFrame = NSMakeRect(itemLocation.x, itemLocation.y,
217 dragImage.size.width,
218 dragImage.size.height);
219 [newItem setDraggingFrame:itemFrame contents:imageOrNil];
220 [dragItems addObject:newItem];
223 [sourceView beginDraggingSessionWithItems:dragItems event:m_lastEvent source:sourceView];
224 QEventLoop eventLoop;
225 QScopedValueRollback updateGuard(m_internalDragLoop, &eventLoop);
232 m_executed_drop_action = act;
237 if (m_internalDragLoop) {
238 Q_ASSERT(m_internalDragLoop->isRunning());
239 m_internalDragLoop->exit();
244QPixmap
QCocoaDrag::dragPixmap(QDrag *drag, QPoint &hotSpot)
const
246 const QMimeData* data = drag->mimeData();
247 QPixmap pm = drag->pixmap();
250 QFont f(qApp->font());
254 if (data->hasImage()) {
255 QImage img = data->imageData().value<QImage>();
257 pm = QPixmap::fromImage(std::move(img)).scaledToWidth(dragImageMaxChars *fm.averageCharWidth());
261 if (pm.isNull() && (data->hasText() || data->hasUrls()) ) {
262 QString s = data->hasText() ? data->text() : data->urls().constFirst().toString();
266 const int width = fm.horizontalAdvance(s);
267 const int height = fm.height();
268 if (width > 0 && height > 0) {
270 QWindow *window = qobject_cast<QWindow *>(drag->source());
271 if (!window && drag->source()->metaObject()->indexOfMethod(
"_q_closestWindowHandle()") != -1) {
272 QMetaObject::invokeMethod(drag->source(),
"_q_closestWindowHandle",
273 Q_RETURN_ARG(QWindow *, window));
276 window = qApp->focusWindow();
279 dpr = window->devicePixelRatio();
281 pm = QPixmap(width * dpr, height * dpr);
282 pm.setDevicePixelRatio(dpr);
284 p.fillRect(0, 0, pm.width(), pm.height(), Qt::color0);
285 p.setPen(Qt::color1);
287 p.drawText(0, fm.ascent(), s);
289 hotSpot = QPoint(pm.width() / 2, pm.height() / 2);
296 pm = defaultPixmap();
303 dropPasteboard =
reinterpret_cast<CFStringRef>(
const_cast<
const NSString *>([pasteboard name]));
304 CFRetain(dropPasteboard);
309 CFRelease(dropPasteboard);
316 if (PasteboardCreate(dropPasteboard, &board) != noErr) {
317 qDebug(
"DnD: Cannot get PasteBoard!");
320 formats = QMacPasteboard(board, QUtiMimeConverter::HandlerScopeFlag::DnD).formats();
328 if (PasteboardCreate(dropPasteboard, &board) != noErr) {
329 qDebug(
"DnD: Cannot get PasteBoard!");
332 data = QMacPasteboard(board, QUtiMimeConverter::HandlerScopeFlag::DnD).retrieveData(mimeType);
341 if (PasteboardCreate(dropPasteboard, &board) != noErr) {
342 qDebug(
"DnD: Cannot get PasteBoard!");
345 has = QMacPasteboard(board, QUtiMimeConverter::HandlerScopeFlag::DnD).hasFormat(mimeType);
Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const override
Qt::DropAction drag(QDrag *m_drag) override
void setAcceptedAction(Qt::DropAction act)
QMimeData * dragMimeData()
QVariant retrieveData_sys(const QString &mimeType, QMetaType type) const
QStringList formats_sys() const
QCocoaDropData(NSPasteboard *pasteboard)
bool hasFormat_sys(const QString &mimeType) const
void setMimeData(QMimeData *mime, DataRequestType dataRequestType=EagerRequest)
static const int dragImageMaxChars