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;
 }
 
 }