Create a native implementation of QCocoaDrag, using the 10.6 (and earlier) Cocoa
dragging API. This matches the implementation in Qt4 closely for the moment.
In the future it may be desirable to create an alternative implementation using
the new (non-blocking) drag API introduced in 10.7, but that will require deeper
changes to the mime-data handling.
This changes makes one more method on QPlatformDrag virtual, since the
Cocoa behaviour diverges from the base version: ::defaultAction is customised.
Change-Id: I1843293a62b2b4973a07b5e75ea3c312dc064018
Reviewed-by: Christoph Schleifenbaum <christoph.schleifenbaum@kdab.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
virtual Qt::DropAction drag(QDrag *m_drag) = 0;
void updateAction(Qt::DropAction action);
- Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const;
+ virtual Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const;
static QPixmap defaultPixmap();
QT_BEGIN_NAMESPACE
-class QCocoaDrag : public QSimpleDrag
+class QCocoaDrag : public QPlatformDrag
{
public:
+ QCocoaDrag();
+
+ virtual QMimeData *platformDropData();
+ virtual Qt::DropAction drag(QDrag *m_drag);
+
+ virtual Qt::DropAction defaultAction(Qt::DropActions possibleActions,
+ Qt::KeyboardModifiers modifiers) const;
+
+ /**
+ * to meet NSView dragImage:at guarantees, we need to record the original
+ * event and view when handling an event in QNSView
+ */
+ void setLastMouseEvent(NSEvent *event, NSView *view);
+
+ void setAcceptedAction(Qt::DropAction act);
private:
+ QDrag *m_drag;
+ NSEvent *m_lastEvent;
+ NSView *m_lastView;
+ Qt::DropAction m_executed_drop_action;
};
class QCocoaDropData : public QInternalMimeData
#include "qcocoadrag.h"
#include "qmacmime.h"
#include "qmacclipboard.h"
+#include "qcocoahelpers.h"
QT_BEGIN_NAMESPACE
+QCocoaDrag::QCocoaDrag() :
+ m_drag(0)
+{
+ m_lastEvent = 0;
+ m_lastView = 0;
+}
+
+void QCocoaDrag::setLastMouseEvent(NSEvent *event, NSView *view)
+{
+ m_lastEvent = event;
+ m_lastView = view;
+}
+
+QMimeData *QCocoaDrag::platformDropData()
+{
+ if (m_drag)
+ return m_drag->mimeData();
+
+ return 0;
+}
+
+Qt::DropAction QCocoaDrag::defaultAction(Qt::DropActions possibleActions,
+ Qt::KeyboardModifiers modifiers) const
+{
+ Qt::DropAction default_action = Qt::IgnoreAction;
+
+ if (currentDrag()) {
+ default_action = currentDrag()->defaultAction();
+ possibleActions = currentDrag()->supportedActions();
+ }
+
+ if (default_action == Qt::IgnoreAction) {
+ //This means that the drag was initiated by QDrag::start and we need to
+ //preserve the old behavior
+ default_action = Qt::CopyAction;
+ }
+
+ if (modifiers & Qt::ControlModifier && modifiers & Qt::AltModifier)
+ default_action = Qt::LinkAction;
+ else if (modifiers & Qt::AltModifier)
+ default_action = Qt::CopyAction;
+ else if (modifiers & Qt::ControlModifier)
+ default_action = Qt::MoveAction;
+
+#ifdef QDND_DEBUG
+ qDebug("possible actions : %s", dragActionsToString(possibleActions).latin1());
+#endif
+
+ // Check if the action determined is allowed
+ if (!(possibleActions & default_action)) {
+ if (possibleActions & Qt::CopyAction)
+ default_action = Qt::CopyAction;
+ else if (possibleActions & Qt::MoveAction)
+ default_action = Qt::MoveAction;
+ else if (possibleActions & Qt::LinkAction)
+ default_action = Qt::LinkAction;
+ else
+ default_action = Qt::IgnoreAction;
+ }
+
+#ifdef QDND_DEBUG
+ qDebug("default action : %s", dragActionsToString(default_action).latin1());
+#endif
+
+ return default_action;
+}
+
+
+Qt::DropAction QCocoaDrag::drag(QDrag *o)
+{
+ m_drag = o;
+ m_executed_drop_action = Qt::IgnoreAction;
+
+ NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(m_drag->pixmap()));
+
+ QMacPasteboard dragBoard((CFStringRef) NSDragPboard, QMacPasteboardMime::MIME_DND);
+ m_drag->mimeData()->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray("dummy"));
+ dragBoard.setMimeData(m_drag->mimeData());
+
+ NSPoint event_location = [m_lastEvent locationInWindow];
+ NSPoint local_point = [m_lastView convertPoint:event_location fromView:nil];
+ local_point.x -= m_drag->hotSpot().x();
+ CGFloat flippedY = m_drag->pixmap().height() - m_drag->hotSpot().y();
+ local_point.y += flippedY;
+ NSSize mouseOffset = NSMakeSize(0.0, 0.0);
+ NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+
+ [m_lastView dragImage:nsimage
+ at:local_point
+ offset:mouseOffset
+ event:m_lastEvent
+ pasteboard:pboard
+ source:m_lastView
+ slideBack:YES];
+
+ m_drag = 0;
+ return m_executed_drop_action;
+}
+
+void QCocoaDrag::setAcceptedAction(Qt::DropAction act)
+{
+ m_executed_drop_action = act;
+}
+
QCocoaDropData::QCocoaDropData(NSPasteboard *pasteboard)
{
dropPasteboard = reinterpret_cast<CFStringRef>(const_cast<const NSString *>([pasteboard name]));
Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
{
Qt::DropActions actions = Qt::IgnoreAction;
+
for (int i=0; dnd_enums[i].mac_code; i++) {
+ if ((dnd_enums[i].mac_code == NSDragOperationEvery))
+ continue;
+
if (nsActions & dnd_enums[i].mac_code)
actions |= dnd_enums[i].qt_code;
}
#include "qcocoaautoreleasepool.h"
#include "qmultitouch_mac_p.h"
#include "qcocoadrag.h"
+#include <qpa/qplatformintegration.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/QTextFormat>
#include <QtCore/QDebug>
+#include <private/qguiapplication_p.h>
#ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
#include <accessibilityinspector.h>
qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
}
ulong timestamp = [theEvent timestamp] * 1000;
+
+ QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
+ nativeDrag->setLastMouseEvent(theEvent, self);
+
QWindowSystemInterface::handleMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons);
}
}
}
+- (NSDragOperation) draggingSourceOperationMaskForLocal:(BOOL)isLocal
+{
+ Q_UNUSED(isLocal);
+ QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
+ return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions());
+}
+
+- (BOOL) ignoreModifierKeysWhileDragging
+{
+ return NO;
+}
+
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
return [self handleDrag : sender];
Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]);
QCocoaDropData mimeData([sender draggingPasteboard]);
+ // update these so selecting move/copy/link works
+ QGuiApplicationPrivate::modifier_buttons = [self convertKeyModifiers: [[NSApp currentEvent] modifierFlags]];
+
QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_window, &mimeData, qt_windowPoint, qtAllowed);
return qt_mac_mapDropAction(response.acceptedAction());
}
return response.isAccepted();
}
+- (void)draggedImage:(NSImage*) img endedAt:(NSPoint) point operation:(NSDragOperation) operation
+{
+ QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->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
+ m_buttons &= QFlag(~int(Qt::LeftButton));
+
+ NSPoint windowPoint = [self convertPoint: point fromView: nil];
+ QPoint qtWindowPoint(point.x, point.y);
+
+ NSWindow *window = [self window];
+ NSPoint screenPoint = [window convertBaseToScreen :point];
+ QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
+
+ QWindowSystemInterface::handleMouseEvent(m_window, qtWindowPoint, qtScreenPoint, m_buttons);
+}
+
@end