Mercurial > projects > dwt-mac
diff dwt/dnd/DropTarget.d @ 45:d8635bb48c7c
Merge with SWT 3.5
author | Jacob Carlborg <doob@me.com> |
---|---|
date | Mon, 01 Dec 2008 17:07:00 +0100 |
parents | a9ab4c738ed8 |
children | d32621bf0f90 |
line wrap: on
line diff
--- a/dwt/dnd/DropTarget.d Tue Oct 21 15:20:04 2008 +0200 +++ b/dwt/dnd/DropTarget.d Mon Dec 01 17:07:00 2008 +0100 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2007 IBM Corporation and others. + * Copyright (c) 2000, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,13 +8,37 @@ * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ -module dwt.dnd; +module dwt.dnd.DropTarget; +import dwt.dwthelper.utils; + +import java.util.ArrayList; -import dwt.*; -import dwt.widgets.*; -import dwt.internal.*; -import dwt.internal.carbon.*; +import dwt.DWT; +import dwt.DWTError; +import dwt.DWTException; +import dwt.internal.Callback; +import dwt.internal.cocoa.NSApplication; +import dwt.internal.cocoa.NSArray; +import dwt.internal.cocoa.NSCursor; +import dwt.internal.cocoa.NSEvent; +import dwt.internal.cocoa.NSMutableArray; +import dwt.internal.cocoa.NSObject; +import dwt.internal.cocoa.NSPasteboard; +import dwt.internal.cocoa.NSPoint; +import dwt.internal.cocoa.NSRect; +import dwt.internal.cocoa.NSScreen; +import dwt.internal.cocoa.NSString; +import dwt.internal.cocoa.NSURL; +import dwt.internal.cocoa.OS; +import dwt.internal.cocoa.id; +import dwt.widgets.Control; +import dwt.widgets.Display; +import dwt.widgets.Event; +import dwt.widgets.Listener; +import dwt.widgets.Table; +import dwt.widgets.Tree; +import dwt.widgets.Widget; /** * @@ -56,7 +80,7 @@ * event.detail = DND.DROP_NONE; * return; * } - * label.setText (cast(String) event.data); // data copied to label text + * label.setText ((String) event.data); // data copied to label text * } * }); * </pre></code> @@ -66,8 +90,27 @@ * <dt><b>Events</b></dt> <dd>DND.DragEnter, DND.DragLeave, DND.DragOver, DND.DragOperationChanged, * DND.DropAccept, DND.Drop </dd> * </dl> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: DNDExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> */ -public class DropTarget : Widget { +public class DropTarget extends Widget { + + static Callback dropTarget2Args, dropTarget3Args; + static int /*long*/ proc2Args, proc3Args; + + static { + Class clazz = DropTarget.class; + + dropTarget2Args = new Callback(clazz, "dropTargetProc", 2); + proc2Args = dropTarget2Args.getAddress(); + if (proc2Args is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS); + + dropTarget3Args = new Callback(clazz, "dropTargetProc", 3); + proc3Args = dropTarget3Args.getAddress(); + if (proc3Args is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS); + } Control control; Listener controlListener; @@ -82,33 +125,199 @@ // workaround - There is no event for "operation changed" so track operation based on key state int keyOperation = -1; - // workaround - Simulate events when mouse is not moving - long dragOverStart; - Runnable dragOverHeartbeat; - DNDEvent dragOverEvent; + static final String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$ - // workaround - OS events are relative to the application, not the control. - // Track which control is the current target to determine when drag and - // drop enters or leaves a widget. - static DropTarget CurrentDropTarget = null; +void addDragHandlers() { + // Our strategy here is to dynamically add methods to the control's class that are required + // by NSDraggingDestination. Then, when setTransfer is called, we just register + // the types with the Control's NSView and AppKit will call the methods in the protocol + // when a drag goes over the view. + + int /*long*/ cls = OS.object_getClass(control.view.id); + + if (cls is 0) { + DND.error(DND.ERROR_CANNOT_INIT_DROP); + } + + // If we already added it, no need to do it again. + int /*long*/ procPtr = OS.class_getMethodImplementation(cls, OS.sel_draggingEnded_); + if (procPtr is proc3Args) return; + + // Add the NSDraggingDestination callbacks + OS.class_addMethod(cls, OS.sel_draggingEntered_, proc3Args, "@:@"); + OS.class_addMethod(cls, OS.sel_draggingUpdated_, proc3Args, "@:@"); + OS.class_addMethod(cls, OS.sel_draggingExited_, proc3Args, "@:@"); + OS.class_addMethod(cls, OS.sel_performDragOperation_, proc3Args, "@:@"); + OS.class_addMethod(cls, OS.sel_wantsPeriodicDraggingUpdates, proc2Args, "@:"); +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when a drag and drop operation is in progress, by sending + * it one of the messages defined in the <code>DropTargetListener</code> + * interface. + * + * <p><ul> + * <li><code>dragEnter</code> is called when the cursor has entered the drop target boundaries + * <li><code>dragLeave</code> is called when the cursor has left the drop target boundaries and just before + * the drop occurs or is cancelled. + * <li><code>dragOperationChanged</code> is called when the operation being performed has changed + * (usually due to the user changing the selected modifier key(s) while dragging) + * <li><code>dragOver</code> is called when the cursor is moving over the drop target + * <li><code>dropAccept</code> is called just before the drop is performed. The drop target is given + * the chance to change the nature of the drop or veto the drop by setting the <code>event.detail</code> field + * <li><code>drop</code> is called when the data is being dropped + * </ul></p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception DWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DropTargetListener + * @see #getDropListeners + * @see #removeDropListener + * @see DropTargetEvent + */ +public void addDropListener(DropTargetListener listener) { + if (listener is null) DND.error (DWT.ERROR_NULL_ARGUMENT); + DNDListener typedListener = new DNDListener (listener); + typedListener.dndWidget = this; + addListener (DND.DragEnter, typedListener); + addListener (DND.DragLeave, typedListener); + addListener (DND.DragOver, typedListener); + addListener (DND.DragOperationChanged, typedListener); + addListener (DND.Drop, typedListener); + addListener (DND.DropAccept, typedListener); +} + +static int checkStyle (int style) { + if (style is DWT.NONE) return DND.DROP_MOVE; + return style; +} + +protected void checkSubclass () { + String name = getClass().getName (); + String validName = DropTarget.class.getName(); + if (!validName.equals(name)) { + DND.error (DWT.ERROR_INVALID_SUBCLASS); + } +} + +int draggingEntered(NSObject sender) { + selectedDataType = null; + selectedOperation = 0; - static final String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$ - static final int DRAGOVER_HYSTERESIS = 50; + DNDEvent event = new DNDEvent(); + if (!setEventData(sender, event)) { + keyOperation = -1; + if (OS.PTR_SIZEOF is 4) OS.SetThemeCursor(OS.kThemeNotAllowedCursor); + return OS.NSDragOperationNone; + } - static Callback DragTrackingHandler; - static Callback DragReceiveHandler; + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + + notifyListeners(DND.DragEnter, event); + + if (event.detail is DND.DROP_DEFAULT) { + event.detail = (allowedOperations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + + selectedDataType = null; + for (int i = 0; i < allowedDataTypes.length; i++) { + if (allowedDataTypes[i].type is event.dataType.type) { + selectedDataType = allowedDataTypes[i]; + break; + } + } + + selectedOperation = DND.DROP_NONE; + if (selectedDataType !is null && (allowedOperations & event.detail) !is 0) { + selectedOperation = event.detail; + } + + int osOperation = opToOsOp(selectedOperation); + + if (OS.PTR_SIZEOF is 4) { + switch (selectedOperation) { + case DND.DROP_COPY: + OS.SetThemeCursor(OS.kThemeCopyArrowCursor); + break; + case DND.DROP_LINK: + OS.SetThemeCursor(OS.kThemeAliasArrowCursor); + break; + case DND.DROP_MOVE: + NSCursor.arrowCursor().set(); + break; + default: + OS.SetThemeCursor(OS.kThemeNotAllowedCursor); + } + } + return osOperation; +} + +void draggingExited(NSObject sender) { + NSCursor.arrowCursor().set(); + if (keyOperation is -1) return; + keyOperation = -1; - static { - DragTrackingHandler = new Callback(DropTarget.class, "DragTrackingHandler", 4); //$NON-NLS-1$ - int dragTrackingHandlerAddress = DragTrackingHandler.getAddress(); - if (dragTrackingHandlerAddress is 0) DWT.error(DWT.ERROR_NO_MORE_CALLBACKS); - DragReceiveHandler = new Callback(DropTarget.class, "DragReceiveHandler", 3); //$NON-NLS-1$ - int dragReceiveHandlerAddress = DragReceiveHandler.getAddress(); - if (dragReceiveHandlerAddress is 0) DWT.error(DWT.ERROR_NO_MORE_CALLBACKS); - OS.InstallTrackingHandler(dragTrackingHandlerAddress, 0, null); - OS.InstallReceiveHandler(dragReceiveHandlerAddress, 0, null); + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = (int)System.currentTimeMillis(); + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragLeave, event); +} + +int draggingUpdated(NSObject sender) { + if (sender is null) return OS.NSDragOperationNone; + int oldKeyOperation = keyOperation; + + DNDEvent event = new DNDEvent(); + if (!setEventData(sender, event)) { + keyOperation = -1; + if (OS.PTR_SIZEOF is 4) OS.SetThemeCursor(OS.kThemeNotAllowedCursor); + return OS.NSDragOperationNone; } + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + + if (keyOperation is oldKeyOperation) { + event.type = DND.DragOver; + event.dataType = selectedDataType; + event.detail = selectedOperation; + } else { + event.type = DND.DragOperationChanged; + event.dataType = selectedDataType; + } + notifyListeners(event.type, event); + if (event.detail is DND.DROP_DEFAULT) { + event.detail = (allowedOperations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + + selectedDataType = null; + for (int i = 0; i < allowedDataTypes.length; i++) { + if (allowedDataTypes[i].type is event.dataType.type) { + selectedDataType = allowedDataTypes[i]; + break; + } + } + + selectedOperation = DND.DROP_NONE; + if (selectedDataType !is null && ((allowedOperations & event.detail) is event.detail)) { + selectedOperation = event.detail; + } + return opToOsOp(selectedOperation); +} + /** * Creates a new <code>DropTarget</code> to allow data to be dropped on the specified * <code>Control</code>. @@ -140,15 +349,14 @@ * @see DND#DROP_MOVE * @see DND#DROP_LINK */ -public this(Control control, int style) { +public DropTarget(Control control, int style) { super(control, checkStyle(style)); this.control = control; - if (DragTrackingHandler is null || DragTrackingHandler is null) { - DND.error(DND.ERROR_CANNOT_INIT_DROP); - } + if (control.getData(DND.DROP_TARGET_KEY) !is null) { DND.error(DND.ERROR_CANNOT_INIT_DROP); } + control.setData(DND.DROP_TARGET_KEY, this); controlListener = new Listener () { @@ -167,179 +375,214 @@ }); Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT); - if ( null !is cast(DropTargetEffect)effect ) { - dropEffect = cast(DropTargetEffect) effect; - } else if ( null !is cast(Table)control ) { - dropEffect = new TableDropTargetEffect(cast(Table) control); - } else if ( null !is cast(Tree)control ) { - dropEffect = new TreeDropTargetEffect(cast(Tree) control); + if (effect instanceof DropTargetEffect) { + dropEffect = (DropTargetEffect) effect; + } else if (control instanceof Table) { + dropEffect = new TableDropTargetEffect((Table) control); + } else if (control instanceof Tree) { + dropEffect = new TreeDropTargetEffect((Tree) control); } - dragOverHeartbeat = new Runnable() { - public void run() { - Control control = DropTarget.this.control; - if (control is null || control.isDisposed() || dragOverStart is 0) return; - long time = System.currentTimeMillis(); - int delay = DRAGOVER_HYSTERESIS; - if (time < dragOverStart) { - delay = cast(int)(dragOverStart - time); - } else { - int allowedOperations = dragOverEvent.operations; - TransferData[] allowedTypes = dragOverEvent.dataTypes; - //pass a copy of data types in to listeners in case application modifies it - TransferData[] dataTypes = new TransferData[allowedTypes.length]; - System.arraycopy(allowedTypes, 0, dataTypes, 0, dataTypes.length); - - DNDEvent event = new DNDEvent(); - event.widget = dragOverEvent.widget; - event.x = dragOverEvent.x; - event.y = dragOverEvent.y; - event.time = cast(int)time; - event.feedback = DND.FEEDBACK_SELECT; - event.dataTypes = dataTypes; - event.dataType = selectedDataType; - event.operations = dragOverEvent.operations; - event.detail = selectedOperation; - if (dropEffect !is null) { - event.item = dropEffect.getItem(event.x, event.y); - } - selectedDataType = null; - selectedOperation = DND.DROP_NONE; - notifyListeners(DND.DragOver, event); - if (event.dataType !is null) { - for (int i = 0; i < allowedTypes.length; i++) { - if (allowedTypes[i].type is event.dataType.type) { - selectedDataType = event.dataType; - break; - } - } - } - if (selectedDataType !is null && (event.detail & allowedOperations) !is 0) { - selectedOperation = event.detail; - } - } - control = DropTarget.this.control; - if (control is null || control.isDisposed()) return; - control.getDisplay().timerExec(delay, dragOverHeartbeat); - } - }; + addDragHandlers(); } -static int checkStyle (int style) { - if (style is DWT.NONE) return DND.DROP_MOVE; - return style; +static int /*long*/ dropTargetProc(int /*long*/ id, int /*long*/ sel) { + Display display = Display.findDisplay(Thread.currentThread()); + if (display is null || display.isDisposed()) return 0; + Widget widget = display.findWidget(id); + if (widget is null) return 0; + DropTarget dt = (DropTarget)widget.getData(DND.DROP_TARGET_KEY); + if (dt is null) return 0; + + if (sel is OS.sel_wantsPeriodicDraggingUpdates) { + return dt.wantsPeriodicDraggingUpdates() ? 1 : 0; + } + + return 0; } -static int DragReceiveHandler(int theWindow, int handlerRefCon, int theDrag) { - DropTarget target = FindDropTarget(theWindow, theDrag); - if (target is null) return OS.noErr; - return target.dragReceiveHandler(theWindow, handlerRefCon, theDrag); -} - -static int DragTrackingHandler(int message, int theWindow, int handlerRefCon, int theDrag) { - if (message is OS.kDragTrackingLeaveHandler || message is OS.kDragTrackingEnterHandler) { - CurrentDropTarget = null; - return OS.noErr; +static int /*long*/ dropTargetProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) { + Display display = Display.findDisplay(Thread.currentThread()); + if (display is null || display.isDisposed()) return 0; + Widget widget = display.findWidget(id); + if (widget is null) return 0; + DropTarget dt = (DropTarget)widget.getData(DND.DROP_TARGET_KEY); + if (dt is null) return 0; + + // arg0 is _always_ the sender, and implements NSDraggingInfo. + // Looks like an NSObject for our purposes, though. + NSObject sender = new NSObject(arg0); + + if (sel is OS.sel_draggingEntered_) { + return dt.draggingEntered(sender); + } else if (sel is OS.sel_draggingUpdated_) { + return dt.draggingUpdated(sender); + } else if (sel is OS.sel_draggingExited_) { + dt.draggingExited(sender); + } else if (sel is OS.sel_performDragOperation_) { + return dt.performDragOperation(sender) ? 1 : 0; } - DropTarget target = FindDropTarget(theWindow, theDrag); - if (CurrentDropTarget !is null) { - if (target is null || CurrentDropTarget.control.handle !is target.control.handle) { - CurrentDropTarget.dragTrackingHandler(OS.kDragTrackingLeaveWindow, theWindow, handlerRefCon, theDrag); - CurrentDropTarget = target; - message = OS.kDragTrackingEnterWindow; - } - } else { - CurrentDropTarget = target; - message = OS.kDragTrackingEnterWindow; - } - if (target is null) return OS.noErr; - return target.dragTrackingHandler(message, theWindow, handlerRefCon, theDrag); + + return 0; } -static DropTarget FindDropTarget(int theWindow, int theDrag) { - Display display = Display.findDisplay(Thread.currentThread()); - if (display is null || display.isDisposed()) return null; - Point mouse = new Point(); - OS.GetDragMouse(theDrag, mouse, null); - int[] theRoot = new int[1]; - OS.GetRootControl(theWindow, theRoot); - int[] theControl = new int[1]; - Rect rect = new Rect(); - OS.GetWindowBounds (theWindow, cast(short) OS.kWindowContentRgn, rect); - CGPoint inPoint = new CGPoint(); - inPoint.x = mouse.h - rect.left; - inPoint.y = mouse.v - rect.top; - OS.HIViewGetSubviewHit(theRoot[0], inPoint, true, theControl); - if (!OS.IsControlEnabled(theControl[0])) return null; - Widget widget = display.findWidget(theControl[0]); - if (widget is null) return null; - return cast(DropTarget)widget.getData(DND.DROP_TARGET_KEY); -} /** - * Adds the listener to the collection of listeners who will - * be notified when a drag and drop operation is in progress, by sending - * it one of the messages defined in the <code>DropTargetListener</code> - * interface. - * - * <p><ul> - * <li><code>dragEnter</code> is called when the cursor has entered the drop target boundaries - * <li><code>dragLeave</code> is called when the cursor has left the drop target boundaries and just before - * the drop occurs or is cancelled. - * <li><code>dragOperationChanged</code> is called when the operation being performed has changed - * (usually due to the user changing the selected modifier key(s) while dragging) - * <li><code>dragOver</code> is called when the cursor is moving over the drop target - * <li><code>dropAccept</code> is called just before the drop is performed. The drop target is given - * the chance to change the nature of the drop or veto the drop by setting the <code>event.detail</code> field - * <li><code>drop</code> is called when the data is being dropped - * </ul></p> + * Returns the Control which is registered for this DropTarget. This is the control over which the + * user positions the cursor to drop the data. * - * @param listener the listener which should be notified + * @return the Control which is registered for this DropTarget + */ +public Control getControl () { + return control; +} + +/** + * Returns an array of listeners who will be notified when a drag and drop + * operation is in progress, by sending it one of the messages defined in + * the <code>DropTargetListener</code> interface. * - * @exception IllegalArgumentException <ul> - * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> - * </ul> + * @return the listeners who will be notified when a drag and drop + * operation is in progress + * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see DropTargetListener + * @see #addDropListener * @see #removeDropListener * @see DropTargetEvent + * + * @since 3.4 */ -public void addDropListener(DropTargetListener listener) { - if (listener is null) DND.error (DWT.ERROR_NULL_ARGUMENT); - DNDListener typedListener = new DNDListener (listener); - typedListener.dndWidget = this; - addListener (DND.DragEnter, typedListener); - addListener (DND.DragLeave, typedListener); - addListener (DND.DragOver, typedListener); - addListener (DND.DragOperationChanged, typedListener); - addListener (DND.Drop, typedListener); - addListener (DND.DropAccept, typedListener); +public DropTargetListener[] getDropListeners() { + Listener[] listeners = getListeners(DND.DragEnter); + int length = listeners.length; + DropTargetListener[] dropListeners = new DropTargetListener[length]; + int count = 0; + for (int i = 0; i < length; i++) { + Listener listener = listeners[i]; + if (listener instanceof DNDListener) { + dropListeners[count] = (DropTargetListener) ((DNDListener) listener).getEventListener(); + count++; + } + } + if (count is length) return dropListeners; + DropTargetListener[] result = new DropTargetListener[count]; + System.arraycopy(dropListeners, 0, result, 0, count); + return result; +} + +/** + * Returns the drop effect for this DropTarget. This drop effect will be + * used during a drag and drop to display the drag under effect on the + * target widget. + * + * @return the drop effect that is registered for this DropTarget + * + * @since 3.3 + */ +public DropTargetEffect getDropTargetEffect() { + return dropEffect; +} + +int getOperationFromKeyState() { + // The NSDraggingInfo object already combined the modifier keys with the + // drag source's allowed events. This might be better accomplished by diffing + // the base drag source mask with the active drag state mask instead of snarfing + // the current event. + + // See documentation on [NSDraggingInfo draggingSourceOperationMask] for the + // correct Cocoa behavior. Control + Option or Command is NSDragOperationGeneric, + // or DND.DROP_DEFAULT in the DWT. + NSEvent currEvent = NSApplication.sharedApplication().currentEvent(); + int /*long*/ modifiers = currEvent.modifierFlags(); + bool option = (modifiers & OS.NSAlternateKeyMask) is OS.NSAlternateKeyMask; + bool control = (modifiers & OS.NSControlKeyMask) is OS.NSControlKeyMask; + if (control && option) return DND.DROP_DEFAULT; + if (control) return DND.DROP_LINK; + if (option) return DND.DROP_COPY; + return DND.DROP_DEFAULT; } -protected void checkSubclass () { - String name = getClass().getName (); - String validName = DropTarget.class.getName(); - if (!validName.opEquals(name)) { - DND.error (DWT.ERROR_INVALID_SUBCLASS); - } +/** + * Returns a list of the data types that can be transferred to this DropTarget. + * + * @return a list of the data types that can be transferred to this DropTarget + */ +public Transfer[] getTransfer() { + return transferAgents; +} + +void onDispose () { + if (control is null) + return; + if (controlListener !is null) + control.removeListener(DWT.Dispose, controlListener); + controlListener = null; + control.setData(DND.DROP_TARGET_KEY, null); + transferAgents = null; + + // Unregister the control as a drop target. + control.view.unregisterDraggedTypes(); + control = null; } -int dragReceiveHandler(int theWindow, int handlerRefCon, int theDrag) { - updateDragOverHover(0, null); - if (keyOperation is -1) return OS.dragNotAcceptedErr; +int opToOsOp(int operation) { + int osOperation = 0; + if ((operation & DND.DROP_COPY) !is 0){ + osOperation |= OS.NSDragOperationCopy; + } + if ((operation & DND.DROP_LINK) !is 0) { + osOperation |= OS.NSDragOperationLink; + } + if ((operation & DND.DROP_MOVE) !is 0) { + osOperation |= OS.NSDragOperationMove; + } + if ((operation & DND.DROP_TARGET_MOVE) !is 0) { + osOperation |= OS.NSDragOperationDelete; + } + return osOperation; +} +int osOpToOp(int /*long*/ osOperation){ + int operation = 0; + if ((osOperation & OS.NSDragOperationCopy) !is 0){ + operation |= DND.DROP_COPY; + } + if ((osOperation & OS.NSDragOperationLink) !is 0) { + operation |= DND.DROP_LINK; + } + if ((osOperation & OS.NSDragOperationDelete) !is 0) { + operation |= DND.DROP_TARGET_MOVE; + } + if ((osOperation & OS.NSDragOperationMove) !is 0) { + operation |= DND.DROP_MOVE; + } + if (osOperation is OS.NSDragOperationEvery) { + operation = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK; + } + return operation; +} + +bool performDragOperation(NSObject sender) { DNDEvent event = new DNDEvent(); event.widget = this; - event.time = cast(int)System.currentTimeMillis(); + event.time = (int)System.currentTimeMillis(); + + if (dropEffect !is null) { + NSPoint mouseLocation = sender.draggingLocation(); + NSPoint globalLoc = sender.draggingDestinationWindow().convertBaseToScreen(mouseLocation); + event.item = dropEffect.getItem((int)globalLoc.x, (int)globalLoc.y); + } + event.detail = DND.DROP_NONE; notifyListeners(DND.DragLeave, event); event = new DNDEvent(); - if (!setEventData(theDrag, event)) { - return OS.dragNotAcceptedErr; + if (!setEventData(sender, event)) { + return false; } keyOperation = -1; @@ -348,10 +591,9 @@ System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, event.dataTypes.length); event.dataType = selectedDataType; event.detail = selectedOperation; + notifyListeners(DND.DropAccept, event); + selectedDataType = null; - selectedOperation = DND.DROP_NONE; - notifyListeners(DND.DropAccept, event); - if (event.dataType !is null) { for (int i = 0; i < allowedDataTypes.length; i++) { if (allowedDataTypes[i].type is event.dataType.type) { @@ -360,32 +602,56 @@ } } } + + selectedOperation = DND.DROP_NONE; if (selectedDataType !is null && (event.detail & allowedOperations) !is 0) { selectedOperation = event.detail; } + if (selectedOperation is DND.DROP_NONE) { - // this was not a successful drop - return OS.dragNotAcceptedErr; + return false; } + // ask drag source for dropped data - byte[][] data = new byte[0][]; - // locate all the items with data of the desired type - short[] numItems = new short[1]; - OS.CountDragItems(theDrag, numItems); - for (short i = 0; i < numItems[0]; i++) { - int[] theItemRef = new int[1]; - OS.GetDragItemReferenceNumber(theDrag, cast(short) (i+1), theItemRef); - int[] size = new int[1]; - OS.GetFlavorDataSize(theDrag, theItemRef[0], selectedDataType.type, size); - if (size[0] > 0) { - byte[] buffer = new byte[size[0]]; - OS.GetFlavorData(theDrag, theItemRef[0], selectedDataType.type, buffer, size, 0); - byte[][] newData = new byte[data.length + 1][]; - System.arraycopy(data, 0, newData, 0, data.length); - newData[data.length] = buffer; - data = newData; + NSPasteboard pasteboard = sender.draggingPasteboard(); + NSObject data = null; + NSMutableArray types = NSMutableArray.arrayWithCapacity(10); + + for (int i = 0; i < transferAgents.length; i++){ + Transfer transfer = transferAgents[i]; + String[] typeNames = transfer.getTypeNames(); + int[] typeIds = transfer.getTypeIds(); + + for (int j = 0; j < typeNames.length; j++) { + if (selectedDataType.type is typeIds[j]) { + types.addObject(NSString.stringWith(typeNames[j])); + break; + } } } + + NSString type = pasteboard.availableTypeFromArray(types); + TransferData tdata = new TransferData(); + + if (type !is null) { + tdata.type = Transfer.registerType(type.getString()); + if (type.isEqual(OS.NSStringPboardType) || + type.isEqual(OS.NSHTMLPboardType) || + type.isEqual(OS.NSRTFPboardType)) { + tdata.data = pasteboard.stringForType(type); + } else if (type.isEqual(OS.NSURLPboardType)) { + tdata.data = NSURL.URLFromPasteboard(pasteboard); + } else if (type.isEqual(OS.NSFilenamesPboardType)) { + tdata.data = new NSArray(pasteboard.propertyListForType(type).id); + } else { + tdata.data = pasteboard.dataForType(type); + } + } + + if (tdata.data !is null) { + data = tdata.data; + } + // Get Data in a Java format Object object = null; for (int i = 0; i < transferAgents.length; i++) { @@ -410,230 +676,14 @@ selectedOperation = event.detail; } //notify source of action taken - int action = opToOsOp(selectedOperation); - OS.SetDragDropAction(theDrag, action); - return (selectedOperation is DND.DROP_NONE) ? OS.dragNotAcceptedErr : OS.noErr; -} - -int dragTrackingHandler(int message, int theWindow, int handlerRefCon, int theDrag) { - - if (message is OS.kDragTrackingLeaveWindow) { - updateDragOverHover(0, null); - OS.SetThemeCursor(OS.kThemeArrowCursor); - if (keyOperation is -1) return OS.dragNotAcceptedErr; - keyOperation = -1; - - DNDEvent event = new DNDEvent(); - event.widget = this; - event.time = cast(int)System.currentTimeMillis(); - event.detail = DND.DROP_NONE; - notifyListeners(DND.DragLeave, event); - return OS.noErr; - } - - int oldKeyOperation = keyOperation; - - if (message is OS.kDragTrackingEnterWindow) { - selectedDataType = null; - selectedOperation = 0; - } - - DNDEvent event = new DNDEvent(); - if (!setEventData(theDrag, event)) { - keyOperation = -1; - OS.SetThemeCursor(OS.kThemeNotAllowedCursor); - return OS.dragNotAcceptedErr; - } - - int allowedOperations = event.operations; - TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; - System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); - - switch (message) { - case OS.kDragTrackingEnterWindow: - event.type = DND.DragEnter; - break; - case OS.kDragTrackingInWindow: - if (keyOperation is oldKeyOperation) { - event.type = DND.DragOver; - event.dataType = selectedDataType; - event.detail = selectedOperation; - }else { - event.type = DND.DragOperationChanged; - event.dataType = selectedDataType; - } - break; - } - - updateDragOverHover(DRAGOVER_HYSTERESIS, event); - selectedDataType = null; - selectedOperation = DND.DROP_NONE; - notifyListeners(event.type, event); - - if (event.detail is DND.DROP_DEFAULT) { - event.detail = (allowedOperations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; - } - - if (event.dataType !is null) { - for (int i = 0; i < allowedDataTypes.length; i++) { - if (allowedDataTypes[i].type is event.dataType.type) { - selectedDataType = allowedDataTypes[i]; - break; - } - } - } - - if (selectedDataType !is null && (allowedOperations & event.detail) !is 0) { - selectedOperation = event.detail; - } - - OS.SetDragDropAction(theDrag, opToOsOp(selectedOperation)); - - switch (selectedOperation) { - case DND.DROP_COPY: - OS.SetThemeCursor(OS.kThemeCopyArrowCursor); - break; - case DND.DROP_LINK: - OS.SetThemeCursor(OS.kThemeAliasArrowCursor); - break; - case DND.DROP_MOVE: - OS.SetThemeCursor(OS.kThemeArrowCursor); - break; - default: - OS.SetThemeCursor(OS.kThemeNotAllowedCursor); - } - - if (message is OS.kDragTrackingEnterWindow) { - dragOverHeartbeat.run(); - } - return OS.noErr; -} - -/** - * Returns the Control which is registered for this DropTarget. This is the control over which the - * user positions the cursor to drop the data. - * - * @return the Control which is registered for this DropTarget - */ -public Control getControl () { - return control; -} - -/** - * Returns an array of listeners who will be notified when a drag and drop - * operation is in progress, by sending it one of the messages defined in - * the <code>DropTargetListener</code> interface. - * - * @exception DWTException <ul> - * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> - * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> - * </ul> - * - * @see DropTargetListener - * @see #addDropListener - * @see #removeDropListener - * @see DropTargetEvent - * - * @since 3.4 - */ -public DropTargetListener[] getDropListeners() { - Listener[] listeners = getListeners(DND.DragEnter); - int length = listeners.length; - DropTargetListener[] dropListeners = new DropTargetListener[length]; - int count = 0; - for (int i = 0; i < length; i++) { - Listener listener = listeners[i]; - if ( null !is cast(DNDListener)listener ) { - dropListeners[count] = cast(DropTargetListener) (cast(DNDListener) listener).getEventListener(); - count++; - } - } - if (count is length) return dropListeners; - DropTargetListener[] result = new DropTargetListener[count]; - System.arraycopy(dropListeners, 0, result, 0, count); - return result; -} - -/** - * Returns the drop effect for this DropTarget. This drop effect will be - * used during a drag and drop to display the drag under effect on the - * target widget. - * - * @return the drop effect that is registered for this DropTarget - * - * @since 3.3 - */ -public DropTargetEffect getDropTargetEffect() { - return dropEffect; -} - -int getOperationFromKeyState(int theDrag) { - short[] modifiers = new short[1]; - OS.GetDragModifiers(theDrag, modifiers, null, null); - bool option = (modifiers[0] & OS.optionKey) is OS.optionKey; - bool command = (modifiers[0] & OS.cmdKey) is OS.cmdKey; - if (option && command) return DND.DROP_LINK; - if (option) return DND.DROP_COPY; - if (command) return DND.DROP_MOVE; - return DND.DROP_DEFAULT; -} - -/** - * Returns a list of the data types that can be transferred to this DropTarget. - * - * @return a list of the data types that can be transferred to this DropTarget - */ -public Transfer[] getTransfer() { - return transferAgents; -} - -void onDispose () { - if (control is null) - return; - if (controlListener !is null) - control.removeListener(DWT.Dispose, controlListener); - controlListener = null; - control.setData(DND.DROP_TARGET_KEY, null); - transferAgents = null; - control = null; -} - -int opToOsOp(int operation) { - int osOperation = 0; - if ((operation & DND.DROP_COPY) !is 0){ - osOperation |= OS.kDragActionCopy; - } - if ((operation & DND.DROP_LINK) !is 0) { - osOperation |= OS.kDragActionAlias; - } - if ((operation & DND.DROP_MOVE) !is 0) { - osOperation |= OS.kDragActionMove; - } - return osOperation; -} - -int osOpToOp(int osOperation){ - int operation = 0; - if ((osOperation & OS.kDragActionCopy) !is 0){ - operation |= DND.DROP_COPY; - } - if ((osOperation & OS.kDragActionAlias) !is 0) { - operation |= DND.DROP_LINK; - } - if ((osOperation & OS.kDragActionMove) !is 0) { - operation |= DND.DROP_MOVE; - } - if (osOperation is OS.kDragActionAll) { - operation = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK; - } - return operation; + return (selectedOperation !is DND.DROP_NONE); } /** * Removes the listener from the collection of listeners who will * be notified when a drag and drop operation is in progress. * - * @param listener the listener which should be notified + * @param listener the listener which should no longer be notified * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> @@ -645,6 +695,7 @@ * * @see DropTargetListener * @see #addDropListener + * @see #getDropListeners */ public void removeDropListener(DropTargetListener listener) { if (listener is null) DND.error (DWT.ERROR_NULL_ARGUMENT); @@ -669,18 +720,17 @@ dropEffect = effect; } -bool setEventData(int theDrag, DNDEvent event) { - if (theDrag is 0) return false; +bool setEventData(NSObject draggingState, DNDEvent event) { + if (draggingState is null) return false; // get allowed operations int style = getStyle(); - int[] outActions = new int[1]; - OS.GetDragAllowableActions(theDrag, outActions); - int operations = osOpToOp(outActions[0]) & style; + int /*long*/ allowedActions = draggingState.draggingSourceOperationMask(); + int operations = osOpToOp(allowedActions) & style; if (operations is DND.DROP_NONE) return false; - - //get current operation - int operation = getOperationFromKeyState(theDrag); + + // get current operation + int operation = getOperationFromKeyState(); keyOperation = operation; if (operation is DND.DROP_DEFAULT) { if ((style & DND.DROP_DEFAULT) is 0) { @@ -690,53 +740,27 @@ if ((operation & operations) is 0) operation = DND.DROP_NONE; } + // get allowed transfer types - short[] numItems = new short[1]; - OS.CountDragItems(theDrag, numItems); - int[] flavors = new int[10]; + NSPasteboard dragPBoard = draggingState.draggingPasteboard(); + NSArray draggedTypes = dragPBoard.types(); + if (draggedTypes is null) return false; + + int /*long*/ draggedTypeCount = draggedTypes.count(); + + TransferData[] dataTypes = new TransferData[(int)draggedTypeCount]; int index = -1; - //Get a unique list of flavors - for (short i = 0; i < numItems[0]; i++) { - int[] theItemRef = new int[1]; - OS.GetDragItemReferenceNumber(theDrag, cast(short) (i+1), theItemRef); - short[] numFlavors = new short[1]; - OS.CountDragItemFlavors(theDrag, theItemRef[0], numFlavors); - int[] theType = new int[1]; - for (int j = 0; j < numFlavors[0]; j++) { - theType[0] = 0; - if (OS.GetFlavorType(theDrag, theItemRef[0], cast(short) (j+1), theType) is OS.noErr) { - bool unique = true; - for (int k = 0; k < flavors.length; k++) { - if (flavors[k] is theType[0]) { - unique = false; - break; - } - } - if (unique) { - if (index is flavors.length - 1) { - int[] temp = new int[flavors.length + 10]; - System.arraycopy(flavors, 0, temp, 0, flavors.length); - flavors = temp; - } - flavors[++index] = theType[0]; - } - } - } - } - if (index is -1) return false; - - TransferData[] dataTypes = new TransferData[index+1]; - index = -1; - for (int i = 0; i < dataTypes.length; i++) { - if (flavors[i] !is 0) { - TransferData data = new TransferData(); - data.type = flavors[i]; - for (int j = 0; j < transferAgents.length; j++) { - Transfer transfer = transferAgents[j]; - if (transfer !is null && transfer.isSupportedType(data)) { - dataTypes[++index] = data; - break; - } + for (int i = 0; i < draggedTypeCount; i++) { + id draggedType = draggedTypes.objectAtIndex(i); + NSString nativeDataType = new NSString(draggedType); + TransferData data = new TransferData(); + data.type = Transfer.registerType(nativeDataType.getString()); + + for (int j = 0; j < transferAgents.length; j++) { + Transfer transfer = transferAgents[j]; + if (transfer !is null && transfer.isSupportedType(data)) { + dataTypes[++index] = data; + break; } } } @@ -748,13 +772,17 @@ dataTypes = temp; } - Point mouse = new Point(); - OS.GetDragMouse(theDrag, mouse, null); + // Convert from window-relative to global coordinates, and flip it. + NSPoint mouse = draggingState.draggingLocation(); + NSPoint globalMouse = draggingState.draggingDestinationWindow().convertBaseToScreen(mouse); + NSArray screens = NSScreen.screens(); + NSRect screenRect = new NSScreen(screens.objectAtIndex(0)).frame(); + globalMouse.y = screenRect.height - globalMouse.y; event.widget = this; - event.x = mouse.h; - event.y = mouse.v; - event.time = cast(int)System.currentTimeMillis(); + event.x = (int)globalMouse.x; + event.y = (int)globalMouse.y; + event.time = (int)System.currentTimeMillis(); event.feedback = DND.FEEDBACK_SELECT; event.dataTypes = dataTypes; event.dataType = dataTypes[0]; @@ -783,22 +811,35 @@ public void setTransfer(Transfer[] transferAgents){ if (transferAgents is null) DND.error(DWT.ERROR_NULL_ARGUMENT); this.transferAgents = transferAgents; + + + // Register the types as valid drop types in Cocoa. + // Accumulate all of the transfer types into a list. + ArrayList typeStrings = new ArrayList(); + + for (int i = 0; i < this.transferAgents.length; i++) { + String[] types = transferAgents[i].getTypeNames(); + + for (int j = 0; j < types.length; j++) { + typeStrings.add(types[j]); + } + } + + // Convert to an NSArray of NSStrings so we can register with the Control. + int typeStringCount = typeStrings.size(); + NSMutableArray nsTypeStrings = NSMutableArray.arrayWithCapacity(typeStringCount); + + for (int i = 0; i < typeStringCount; i++) { + nsTypeStrings.addObject(NSString.stringWith((String)typeStrings.get(i))); + } + + control.view.registerForDraggedTypes(nsTypeStrings); + } -void updateDragOverHover(long delay, DNDEvent event) { - if (delay is 0) { - dragOverStart = 0; - dragOverEvent = null; - return; - } - dragOverStart = System.currentTimeMillis() + delay; - if (dragOverEvent is null) dragOverEvent = new DNDEvent(); - dragOverEvent.x = event.x; - dragOverEvent.y = event.y; - dragOverEvent.dataTypes = event.dataTypes; - dragOverEvent.operations = event.operations; - dragOverEvent.dataType = event.dataType; - dragOverEvent.detail = event.detail; +// By returning true we get draggingUpdated messages even when the mouse isn't moving. +bool wantsPeriodicDraggingUpdates() { + return true; } }