summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa/qnsview_dragging.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/cocoa/qnsview_dragging.mm')
-rw-r--r--src/plugins/platforms/cocoa/qnsview_dragging.mm302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/plugins/platforms/cocoa/qnsview_dragging.mm b/src/plugins/platforms/cocoa/qnsview_dragging.mm
new file mode 100644
index 0000000000..e77a26eed0
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qnsview_dragging.mm
@@ -0,0 +1,302 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// This file is included from qnsview.mm, and only used to organize the code
+
+@implementation QT_MANGLE_NAMESPACE(QNSView) (Dragging)
+
+-(void)registerDragTypes
+{
+ QMacAutoReleasePool pool;
+ QStringList customTypes = qt_mac_enabledDraggedTypes();
+ if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) {
+ if (currentCustomDragTypes == 0)
+ currentCustomDragTypes = new QStringList();
+ *currentCustomDragTypes = customTypes;
+ NSString * const mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
+ NSMutableArray<NSString *> *supportedTypes = [NSMutableArray<NSString *> arrayWithArray:@[
+ NSColorPboardType,
+ NSFilenamesPboardType, NSStringPboardType,
+ NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType,
+ NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType,
+ NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType,
+ NSRTFDPboardType, NSHTMLPboardType,
+ NSURLPboardType, NSPDFPboardType, NSVCardPboardType,
+ NSFilesPromisePboardType, NSInkTextPboardType,
+ NSMultipleTextSelectionPboardType, mimeTypeGeneric]];
+ // Add custom types supported by the application.
+ for (int i = 0; i < customTypes.size(); i++) {
+ [supportedTypes addObject:customTypes[i].toNSString()];
+ }
+ [self registerForDraggedTypes:supportedTypes];
+ }
+}
+
+static QWindow *findEventTargetWindow(QWindow *candidate)
+{
+ while (candidate) {
+ if (!(candidate->flags() & Qt::WindowTransparentForInput))
+ return candidate;
+ candidate = candidate->parent();
+ }
+ return candidate;
+}
+
+static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint point)
+{
+ return target->mapFromGlobal(source->mapToGlobal(point));
+}
+
+- (NSDragOperation)draggingSession:(NSDraggingSession *)session
+ sourceOperationMaskForDraggingContext:(NSDraggingContext)context
+{
+ Q_UNUSED(session);
+ Q_UNUSED(context);
+ QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
+ return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions());
+}
+
+- (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession *)session
+{
+ Q_UNUSED(session);
+ // According to the "Dragging Sources" chapter on Cocoa DnD Programming
+ // (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/DragandDrop/Concepts/dragsource.html),
+ // if the control, option, or command key is pressed, the source’s
+ // operation mask is filtered to only contain a reduced set of operations.
+ //
+ // Since Qt already takes care of tracking the keyboard modifiers, we
+ // don't need (or want) Cocoa to filter anything. Instead, we'll let
+ // the application do the actual filtering.
+ return YES;
+}
+
+- (BOOL)wantsPeriodicDraggingUpdates
+{
+ // From the documentation:
+ //
+ // "If the destination returns NO, these messages are sent only when the mouse moves
+ // or a modifier flag changes. Otherwise the destination gets the default behavior,
+ // where it receives periodic dragging-updated messages even if nothing changes."
+ //
+ // We do not want these constant drag update events while mouse is stationary,
+ // since we do all animations (autoscroll) with timers.
+ return NO;
+}
+
+
+- (BOOL)wantsPeriodicDraggingUpdates:(void *)dummy
+{
+ // This method never gets called. It's a workaround for Apple's
+ // bug: they first respondsToSelector : @selector(wantsPeriodicDraggingUpdates:)
+ // (note ':') and then call -wantsPeriodicDraggingUpdate (without colon).
+ // So, let's make them happy.
+ Q_UNUSED(dummy);
+
+ return NO;
+}
+
+- (void)updateCursorFromDragResponse:(QPlatformDragQtResponse)response drag:(QCocoaDrag *)drag
+{
+ const QPixmap pixmapCursor = drag->currentDrag()->dragCursor(response.acceptedAction());
+ NSCursor *nativeCursor = nil;
+
+ if (pixmapCursor.isNull()) {
+ switch (response.acceptedAction()) {
+ case Qt::CopyAction:
+ nativeCursor = [NSCursor dragCopyCursor];
+ break;
+ case Qt::LinkAction:
+ nativeCursor = [NSCursor dragLinkCursor];
+ break;
+ case Qt::IgnoreAction:
+ // Uncomment the next lines if forbiden cursor wanted on non droppable targets.
+ /*nativeCursor = [NSCursor operationNotAllowedCursor];
+ break;*/
+ case Qt::MoveAction:
+ default:
+ nativeCursor = [NSCursor arrowCursor];
+ break;
+ }
+ }
+ else {
+ NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor);
+ nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize());
+ nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint];
+ [nsimage release];
+ }
+
+ // change the cursor
+ [nativeCursor set];
+
+ // Make sure the cursor is updated correctly if the mouse does not move and window is under cursor
+ // by creating a fake move event
+ if (m_updatingDrag)
+ return;
+
+ const QPoint mousePos(QCursor::pos());
+ CGEventRef moveEvent(CGEventCreateMouseEvent(
+ NULL, kCGEventMouseMoved,
+ CGPointMake(mousePos.x(), mousePos.y()),
+ kCGMouseButtonLeft // ignored
+ ));
+ CGEventPost(kCGHIDEventTap, moveEvent);
+ CFRelease(moveEvent);
+}
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+ return [self handleDrag : sender];
+}
+
+- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
+{
+ m_updatingDrag = true;
+ const NSDragOperation ret([self handleDrag : sender]);
+ m_updatingDrag = false;
+
+ return ret;
+}
+
+// Sends drag update to Qt, return the action
+- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender
+{
+ if (!m_platformWindow)
+ return NSDragOperationNone;
+
+ NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
+ QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
+ Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
+
+ QWindow *target = findEventTargetWindow(m_platformWindow->window());
+ if (!target)
+ return NSDragOperationNone;
+
+ // update these so selecting move/copy/link works
+ QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers: [[NSApp currentEvent] modifierFlags]];
+
+ QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect());
+ QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
+ if (nativeDrag->currentDrag()) {
+ // The drag was started from within the application
+ response = QWindowSystemInterface::handleDrag(target, nativeDrag->dragMimeData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed);
+ [self updateCursorFromDragResponse:response drag:nativeDrag];
+ } else {
+ QCocoaDropData mimeData([sender draggingPasteboard]);
+ response = QWindowSystemInterface::handleDrag(target, &mimeData, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed);
+ }
+
+ return qt_mac_mapDropAction(response.acceptedAction());
+}
+
+- (void)draggingExited:(id <NSDraggingInfo>)sender
+{
+ if (!m_platformWindow)
+ return;
+
+ QWindow *target = findEventTargetWindow(m_platformWindow->window());
+ if (!target)
+ return;
+
+ NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
+ QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
+
+ // Send 0 mime data to indicate drag exit
+ QWindowSystemInterface::handleDrag(target, 0, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), Qt::IgnoreAction);
+}
+
+// called on drop, send the drop to Qt and return if it was accepted.
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
+{
+ if (!m_platformWindow)
+ return false;
+
+ QWindow *target = findEventTargetWindow(m_platformWindow->window());
+ if (!target)
+ return false;
+
+ NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil];
+ QPoint qt_windowPoint(windowPoint.x, windowPoint.y);
+ Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
+
+ QPlatformDropQtResponse response(false, Qt::IgnoreAction);
+ QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
+ if (nativeDrag->currentDrag()) {
+ // The drag was started from within the application
+ response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed);
+ } else {
+ QCocoaDropData mimeData([sender draggingPasteboard]);
+ response = QWindowSystemInterface::handleDrop(target, &mimeData, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed);
+ }
+ return response.isAccepted();
+}
+
+- (void)draggingSession:(NSDraggingSession *)session
+ endedAtPoint:(NSPoint)screenPoint
+ operation:(NSDragOperation)operation
+{
+ Q_UNUSED(session);
+ Q_UNUSED(operation);
+
+ if (!m_platformWindow)
+ return;
+
+ QWindow *target = findEventTargetWindow(m_platformWindow->window());
+ if (!target)
+ return;
+
+ QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
+ nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation));
+
+ // keep our state, and QGuiApplication state (buttons member) in-sync,
+ // or future mouse events will be processed incorrectly
+ NSUInteger pmb = [NSEvent pressedMouseButtons];
+ for (int buttonNumber = 0; buttonNumber < 32; buttonNumber++) { // see cocoaButton2QtButton() for the 32 value
+ if (!(pmb & (1 << buttonNumber)))
+ m_buttons &= ~cocoaButton2QtButton(buttonNumber);
+ }
+
+ NSPoint windowPoint = [self.window convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 1, 1)].origin;
+ NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; // NSView/QWindow coordinates
+ QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y);
+ QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint();
+
+ QWindowSystemInterface::handleMouseEvent(target, mapWindowCoordinates(m_platformWindow->window(), target, qtWindowPoint), qtScreenPoint, m_buttons);
+}
+
+@end