Mercurial > projects > dwt-mac
diff dwt/dnd/DropTarget.d @ 0:380af2bdd8e5
Upload of whole dwt tree
author | Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com> |
---|---|
date | Sat, 09 Aug 2008 17:00:02 +0200 |
parents | |
children | 1a8b3cb347e0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DropTarget.d Sat Aug 09 17:00:02 2008 +0200 @@ -0,0 +1,804 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module dwt.dnd; + + +import dwt.*; +import dwt.widgets.*; +import dwt.internal.*; +import dwt.internal.carbon.*; + +/** + * + * Class <code>DropTarget</code> defines the target object for a drag and drop transfer. + * + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * + * <p>This class identifies the <code>Control</code> over which the user must position the cursor + * in order to drop the data being transferred. It also specifies what data types can be dropped on + * this control and what operations can be performed. You may have several DropTragets in an + * application but there can only be a one to one mapping between a <code>Control</code> and a <code>DropTarget</code>. + * The DropTarget can receive data from within the same application or from other applications + * (such as text dragged from a text editor like Word).</p> + * + * <code><pre> + * int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK; + * Transfer[] types = new Transfer[] {TextTransfer.getInstance()}; + * DropTarget target = new DropTarget(label, operations); + * target.setTransfer(types); + * </code></pre> + * + * <p>The application is notified of data being dragged over this control and of when a drop occurs by + * implementing the interface <code>DropTargetListener</code> which uses the class + * <code>DropTargetEvent</code>. The application can modify the type of drag being performed + * on this Control at any stage of the drag by modifying the <code>event.detail</code> field or the + * <code>event.currentDataType</code> field. When the data is dropped, it is the responsibility of + * the application to copy this data for its own purposes. + * + * <code><pre> + * target.addDropListener (new DropTargetListener() { + * public void dragEnter(DropTargetEvent event) {}; + * public void dragOver(DropTargetEvent event) {}; + * public void dragLeave(DropTargetEvent event) {}; + * public void dragOperationChanged(DropTargetEvent event) {}; + * public void dropAccept(DropTargetEvent event) {} + * public void drop(DropTargetEvent event) { + * // A drop has occurred, copy over the data + * if (event.data is null) { // no data to copy, indicate failure in event.detail + * event.detail = DND.DROP_NONE; + * return; + * } + * label.setText ((String) event.data); // data copied to label text + * } + * }); + * </pre></code> + * + * <dl> + * <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd> + * <dt><b>Events</b></dt> <dd>DND.DragEnter, DND.DragLeave, DND.DragOver, DND.DragOperationChanged, + * DND.DropAccept, DND.Drop </dd> + * </dl> + */ +public class DropTarget : Widget { + + Control control; + Listener controlListener; + Transfer[] transferAgents = new Transfer[0]; + DropTargetEffect dropEffect; + int feedback = DND.FEEDBACK_NONE; + + // Track application selections + TransferData selectedDataType; + int selectedOperation; + + // 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; + + // 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; + + static final String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$ + static final int DRAGOVER_HYSTERESIS = 50; + + static Callback DragTrackingHandler; + static Callback DragReceiveHandler; + + 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); + } + +/** + * Creates a new <code>DropTarget</code> to allow data to be dropped on the specified + * <code>Control</code>. + * Creating an instance of a DropTarget may cause system resources to be allocated + * depending on the platform. It is therefore mandatory that the DropTarget instance + * be disposed when no longer required. + * + * @param control the <code>Control</code> over which the user positions the cursor to drop the data + * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of + * DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK + * + * @exception DWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * @exception DWTError <ul> + * <li>ERROR_CANNOT_INIT_DROP - unable to initiate drop target; this will occur if more than one + * drop target is created for a control or if the operating system will not allow the creation + * of the drop target</li> + * </ul> + * + * <p>NOTE: ERROR_CANNOT_INIT_DROP should be an DWTException, since it is a + * recoverable error, but can not be changed due to backward compatibility.</p> + * + * @see Widget#dispose + * @see DropTarget#checkSubclass + * @see DND#DROP_NONE + * @see DND#DROP_COPY + * @see DND#DROP_MOVE + * @see DND#DROP_LINK + */ +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 () { + public void handleEvent (Event event) { + if (!DropTarget.this.isDisposed()) { + DropTarget.this.dispose(); + } + } + }; + control.addListener (DWT.Dispose, controlListener); + + this.addListener(DWT.Dispose, new Listener() { + public void handleEvent (Event event) { + onDispose(); + } + }); + + Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT); + 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 = (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 = (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); + } + }; +} + +static int checkStyle (int style) { + if (style is DWT.NONE) return DND.DROP_MOVE; + return style; +} + +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; + } + 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); +} + +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, (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 (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> + * + * @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 #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); +} + +protected void checkSubclass () { + String name = getClass().getName (); + String validName = DropTarget.class.getName(); + if (!validName.opEquals(name)) { + DND.error (DWT.ERROR_INVALID_SUBCLASS); + } +} + +int dragReceiveHandler(int theWindow, int handlerRefCon, int theDrag) { + updateDragOverHover(0, null); + if (keyOperation is -1) return OS.dragNotAcceptedErr; + + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = (int)System.currentTimeMillis(); + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragLeave, event); + + event = new DNDEvent(); + if (!setEventData(theDrag, event)) { + return OS.dragNotAcceptedErr; + } + + keyOperation = -1; + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, event.dataTypes.length); + event.dataType = selectedDataType; + event.detail = selectedOperation; + 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) { + selectedDataType = allowedDataTypes[i]; + break; + } + } + } + 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; + } + // 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, (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; + } + } + // Get Data in a Java format + Object object = null; + for (int i = 0; i < transferAgents.length; i++) { + Transfer transfer = transferAgents[i]; + if (transfer !is null && transfer.isSupportedType(selectedDataType)) { + selectedDataType.data = data; + object = transfer.nativeToJava(selectedDataType); + break; + } + } + + if (object is null) { + selectedOperation = DND.DROP_NONE; + } + + event.dataType = selectedDataType; + event.detail = selectedOperation; + event.data = object; + notifyListeners(DND.Drop, event); + selectedOperation = DND.DROP_NONE; + if ((allowedOperations & event.detail) is event.detail) { + 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 = (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 (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(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; +} + +/** + * 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 + * + * @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 #addDropListener + */ +public void removeDropListener(DropTargetListener listener) { + if (listener is null) DND.error (DWT.ERROR_NULL_ARGUMENT); + removeListener (DND.DragEnter, listener); + removeListener (DND.DragLeave, listener); + removeListener (DND.DragOver, listener); + removeListener (DND.DragOperationChanged, listener); + removeListener (DND.Drop, listener); + removeListener (DND.DropAccept, listener); +} + +/** + * Specifies 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. + * + * @param effect the drop effect that is registered for this DropTarget + * + * @since 3.3 + */ +public void setDropTargetEffect(DropTargetEffect effect) { + dropEffect = effect; +} + +bool setEventData(int theDrag, DNDEvent event) { + if (theDrag is 0) return false; + + // get allowed operations + int style = getStyle(); + int[] outActions = new int[1]; + OS.GetDragAllowableActions(theDrag, outActions); + int operations = osOpToOp(outActions[0]) & style; + if (operations is DND.DROP_NONE) return false; + + //get current operation + int operation = getOperationFromKeyState(theDrag); + keyOperation = operation; + if (operation is DND.DROP_DEFAULT) { + if ((style & DND.DROP_DEFAULT) is 0) { + operation = (operations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + } else { + 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]; + 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, (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], (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; + } + } + } + } + if (index is -1) return false; + + if (index < dataTypes.length - 1) { + TransferData[] temp = new TransferData[index + 1]; + System.arraycopy(dataTypes, 0, temp, 0, index + 1); + dataTypes = temp; + } + + Point mouse = new Point(); + OS.GetDragMouse(theDrag, mouse, null); + + event.widget = this; + event.x = mouse.h; + event.y = mouse.v; + event.time = (int)System.currentTimeMillis(); + event.feedback = DND.FEEDBACK_SELECT; + event.dataTypes = dataTypes; + event.dataType = dataTypes[0]; + event.operations = operations; + event.detail = operation; + if (dropEffect !is null) { + event.item = dropEffect.getItem(event.x, event.y); + } + + return true; +} + +/** + * Specifies the data types that can be transferred to this DropTarget. If data is + * being dragged that does not match one of these types, the drop target will be notified of + * the drag and drop operation but the currentDataType will be null and the operation + * will be DND.NONE. + * + * @param transferAgents a list of Transfer objects which define the types of data that can be + * dropped on this target + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if transferAgents is null</li> + * </ul> + */ +public void setTransfer(Transfer[] transferAgents){ + if (transferAgents is null) DND.error(DWT.ERROR_NULL_ARGUMENT); + this.transferAgents = transferAgents; +} + +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; +} + +}