comparison 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
comparison
equal deleted inserted replaced
44:ca5e494f2bbf 45:d8635bb48c7c
1 /******************************************************************************* 1 /*******************************************************************************
2 * Copyright (c) 2000, 2007 IBM Corporation and others. 2 * Copyright (c) 2000, 2008 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials 3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0 4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at 5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html 6 * http://www.eclipse.org/legal/epl-v10.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * IBM Corporation - initial API and implementation 9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/ 10 *******************************************************************************/
11 module dwt.dnd; 11 module dwt.dnd.DropTarget;
12 12
13 13 import dwt.dwthelper.utils;
14 import dwt.*; 14
15 import dwt.widgets.*; 15 import java.util.ArrayList;
16 import dwt.internal.*; 16
17 import dwt.internal.carbon.*; 17 import dwt.DWT;
18 import dwt.DWTError;
19 import dwt.DWTException;
20 import dwt.internal.Callback;
21 import dwt.internal.cocoa.NSApplication;
22 import dwt.internal.cocoa.NSArray;
23 import dwt.internal.cocoa.NSCursor;
24 import dwt.internal.cocoa.NSEvent;
25 import dwt.internal.cocoa.NSMutableArray;
26 import dwt.internal.cocoa.NSObject;
27 import dwt.internal.cocoa.NSPasteboard;
28 import dwt.internal.cocoa.NSPoint;
29 import dwt.internal.cocoa.NSRect;
30 import dwt.internal.cocoa.NSScreen;
31 import dwt.internal.cocoa.NSString;
32 import dwt.internal.cocoa.NSURL;
33 import dwt.internal.cocoa.OS;
34 import dwt.internal.cocoa.id;
35 import dwt.widgets.Control;
36 import dwt.widgets.Display;
37 import dwt.widgets.Event;
38 import dwt.widgets.Listener;
39 import dwt.widgets.Table;
40 import dwt.widgets.Tree;
41 import dwt.widgets.Widget;
18 42
19 /** 43 /**
20 * 44 *
21 * Class <code>DropTarget</code> defines the target object for a drag and drop transfer. 45 * Class <code>DropTarget</code> defines the target object for a drag and drop transfer.
22 * 46 *
54 * // A drop has occurred, copy over the data 78 * // A drop has occurred, copy over the data
55 * if (event.data is null) { // no data to copy, indicate failure in event.detail 79 * if (event.data is null) { // no data to copy, indicate failure in event.detail
56 * event.detail = DND.DROP_NONE; 80 * event.detail = DND.DROP_NONE;
57 * return; 81 * return;
58 * } 82 * }
59 * label.setText (cast(String) event.data); // data copied to label text 83 * label.setText ((String) event.data); // data copied to label text
60 * } 84 * }
61 * }); 85 * });
62 * </pre></code> 86 * </pre></code>
63 * 87 *
64 * <dl> 88 * <dl>
65 * <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd> 89 * <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd>
66 * <dt><b>Events</b></dt> <dd>DND.DragEnter, DND.DragLeave, DND.DragOver, DND.DragOperationChanged, 90 * <dt><b>Events</b></dt> <dd>DND.DragEnter, DND.DragLeave, DND.DragOver, DND.DragOperationChanged,
67 * DND.DropAccept, DND.Drop </dd> 91 * DND.DropAccept, DND.Drop </dd>
68 * </dl> 92 * </dl>
69 */ 93 *
70 public class DropTarget : Widget { 94 * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a>
95 * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: DNDExample</a>
96 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
97 */
98 public class DropTarget extends Widget {
99
100 static Callback dropTarget2Args, dropTarget3Args;
101 static int /*long*/ proc2Args, proc3Args;
102
103 static {
104 Class clazz = DropTarget.class;
105
106 dropTarget2Args = new Callback(clazz, "dropTargetProc", 2);
107 proc2Args = dropTarget2Args.getAddress();
108 if (proc2Args is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS);
109
110 dropTarget3Args = new Callback(clazz, "dropTargetProc", 3);
111 proc3Args = dropTarget3Args.getAddress();
112 if (proc3Args is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS);
113 }
71 114
72 Control control; 115 Control control;
73 Listener controlListener; 116 Listener controlListener;
74 Transfer[] transferAgents = new Transfer[0]; 117 Transfer[] transferAgents = new Transfer[0];
75 DropTargetEffect dropEffect; 118 DropTargetEffect dropEffect;
80 int selectedOperation; 123 int selectedOperation;
81 124
82 // workaround - There is no event for "operation changed" so track operation based on key state 125 // workaround - There is no event for "operation changed" so track operation based on key state
83 int keyOperation = -1; 126 int keyOperation = -1;
84 127
85 // workaround - Simulate events when mouse is not moving
86 long dragOverStart;
87 Runnable dragOverHeartbeat;
88 DNDEvent dragOverEvent;
89
90 // workaround - OS events are relative to the application, not the control.
91 // Track which control is the current target to determine when drag and
92 // drop enters or leaves a widget.
93 static DropTarget CurrentDropTarget = null;
94
95 static final String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$ 128 static final String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$
96 static final int DRAGOVER_HYSTERESIS = 50; 129
97 130 void addDragHandlers() {
98 static Callback DragTrackingHandler; 131 // Our strategy here is to dynamically add methods to the control's class that are required
99 static Callback DragReceiveHandler; 132 // by NSDraggingDestination. Then, when setTransfer is called, we just register
100 133 // the types with the Control's NSView and AppKit will call the methods in the protocol
101 static { 134 // when a drag goes over the view.
102 DragTrackingHandler = new Callback(DropTarget.class, "DragTrackingHandler", 4); //$NON-NLS-1$ 135
103 int dragTrackingHandlerAddress = DragTrackingHandler.getAddress(); 136 int /*long*/ cls = OS.object_getClass(control.view.id);
104 if (dragTrackingHandlerAddress is 0) DWT.error(DWT.ERROR_NO_MORE_CALLBACKS); 137
105 DragReceiveHandler = new Callback(DropTarget.class, "DragReceiveHandler", 3); //$NON-NLS-1$ 138 if (cls is 0) {
106 int dragReceiveHandlerAddress = DragReceiveHandler.getAddress();
107 if (dragReceiveHandlerAddress is 0) DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
108 OS.InstallTrackingHandler(dragTrackingHandlerAddress, 0, null);
109 OS.InstallReceiveHandler(dragReceiveHandlerAddress, 0, null);
110 }
111
112 /**
113 * Creates a new <code>DropTarget</code> to allow data to be dropped on the specified
114 * <code>Control</code>.
115 * Creating an instance of a DropTarget may cause system resources to be allocated
116 * depending on the platform. It is therefore mandatory that the DropTarget instance
117 * be disposed when no longer required.
118 *
119 * @param control the <code>Control</code> over which the user positions the cursor to drop the data
120 * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of
121 * DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK
122 *
123 * @exception DWTException <ul>
124 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
125 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
126 * </ul>
127 * @exception DWTError <ul>
128 * <li>ERROR_CANNOT_INIT_DROP - unable to initiate drop target; this will occur if more than one
129 * drop target is created for a control or if the operating system will not allow the creation
130 * of the drop target</li>
131 * </ul>
132 *
133 * <p>NOTE: ERROR_CANNOT_INIT_DROP should be an DWTException, since it is a
134 * recoverable error, but can not be changed due to backward compatibility.</p>
135 *
136 * @see Widget#dispose
137 * @see DropTarget#checkSubclass
138 * @see DND#DROP_NONE
139 * @see DND#DROP_COPY
140 * @see DND#DROP_MOVE
141 * @see DND#DROP_LINK
142 */
143 public this(Control control, int style) {
144 super(control, checkStyle(style));
145 this.control = control;
146 if (DragTrackingHandler is null || DragTrackingHandler is null) {
147 DND.error(DND.ERROR_CANNOT_INIT_DROP); 139 DND.error(DND.ERROR_CANNOT_INIT_DROP);
148 } 140 }
149 if (control.getData(DND.DROP_TARGET_KEY) !is null) { 141
150 DND.error(DND.ERROR_CANNOT_INIT_DROP); 142 // If we already added it, no need to do it again.
151 } 143 int /*long*/ procPtr = OS.class_getMethodImplementation(cls, OS.sel_draggingEnded_);
152 control.setData(DND.DROP_TARGET_KEY, this); 144 if (procPtr is proc3Args) return;
153 145
154 controlListener = new Listener () { 146 // Add the NSDraggingDestination callbacks
155 public void handleEvent (Event event) { 147 OS.class_addMethod(cls, OS.sel_draggingEntered_, proc3Args, "@:@");
156 if (!DropTarget.this.isDisposed()) { 148 OS.class_addMethod(cls, OS.sel_draggingUpdated_, proc3Args, "@:@");
157 DropTarget.this.dispose(); 149 OS.class_addMethod(cls, OS.sel_draggingExited_, proc3Args, "@:@");
158 } 150 OS.class_addMethod(cls, OS.sel_performDragOperation_, proc3Args, "@:@");
159 } 151 OS.class_addMethod(cls, OS.sel_wantsPeriodicDraggingUpdates, proc2Args, "@:");
160 }; 152 }
161 control.addListener (DWT.Dispose, controlListener); 153
162
163 this.addListener(DWT.Dispose, new Listener() {
164 public void handleEvent (Event event) {
165 onDispose();
166 }
167 });
168
169 Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT);
170 if ( null !is cast(DropTargetEffect)effect ) {
171 dropEffect = cast(DropTargetEffect) effect;
172 } else if ( null !is cast(Table)control ) {
173 dropEffect = new TableDropTargetEffect(cast(Table) control);
174 } else if ( null !is cast(Tree)control ) {
175 dropEffect = new TreeDropTargetEffect(cast(Tree) control);
176 }
177
178 dragOverHeartbeat = new Runnable() {
179 public void run() {
180 Control control = DropTarget.this.control;
181 if (control is null || control.isDisposed() || dragOverStart is 0) return;
182 long time = System.currentTimeMillis();
183 int delay = DRAGOVER_HYSTERESIS;
184 if (time < dragOverStart) {
185 delay = cast(int)(dragOverStart - time);
186 } else {
187 int allowedOperations = dragOverEvent.operations;
188 TransferData[] allowedTypes = dragOverEvent.dataTypes;
189 //pass a copy of data types in to listeners in case application modifies it
190 TransferData[] dataTypes = new TransferData[allowedTypes.length];
191 System.arraycopy(allowedTypes, 0, dataTypes, 0, dataTypes.length);
192
193 DNDEvent event = new DNDEvent();
194 event.widget = dragOverEvent.widget;
195 event.x = dragOverEvent.x;
196 event.y = dragOverEvent.y;
197 event.time = cast(int)time;
198 event.feedback = DND.FEEDBACK_SELECT;
199 event.dataTypes = dataTypes;
200 event.dataType = selectedDataType;
201 event.operations = dragOverEvent.operations;
202 event.detail = selectedOperation;
203 if (dropEffect !is null) {
204 event.item = dropEffect.getItem(event.x, event.y);
205 }
206 selectedDataType = null;
207 selectedOperation = DND.DROP_NONE;
208 notifyListeners(DND.DragOver, event);
209 if (event.dataType !is null) {
210 for (int i = 0; i < allowedTypes.length; i++) {
211 if (allowedTypes[i].type is event.dataType.type) {
212 selectedDataType = event.dataType;
213 break;
214 }
215 }
216 }
217 if (selectedDataType !is null && (event.detail & allowedOperations) !is 0) {
218 selectedOperation = event.detail;
219 }
220 }
221 control = DropTarget.this.control;
222 if (control is null || control.isDisposed()) return;
223 control.getDisplay().timerExec(delay, dragOverHeartbeat);
224 }
225 };
226 }
227
228 static int checkStyle (int style) {
229 if (style is DWT.NONE) return DND.DROP_MOVE;
230 return style;
231 }
232
233 static int DragReceiveHandler(int theWindow, int handlerRefCon, int theDrag) {
234 DropTarget target = FindDropTarget(theWindow, theDrag);
235 if (target is null) return OS.noErr;
236 return target.dragReceiveHandler(theWindow, handlerRefCon, theDrag);
237 }
238
239 static int DragTrackingHandler(int message, int theWindow, int handlerRefCon, int theDrag) {
240 if (message is OS.kDragTrackingLeaveHandler || message is OS.kDragTrackingEnterHandler) {
241 CurrentDropTarget = null;
242 return OS.noErr;
243 }
244 DropTarget target = FindDropTarget(theWindow, theDrag);
245 if (CurrentDropTarget !is null) {
246 if (target is null || CurrentDropTarget.control.handle !is target.control.handle) {
247 CurrentDropTarget.dragTrackingHandler(OS.kDragTrackingLeaveWindow, theWindow, handlerRefCon, theDrag);
248 CurrentDropTarget = target;
249 message = OS.kDragTrackingEnterWindow;
250 }
251 } else {
252 CurrentDropTarget = target;
253 message = OS.kDragTrackingEnterWindow;
254 }
255 if (target is null) return OS.noErr;
256 return target.dragTrackingHandler(message, theWindow, handlerRefCon, theDrag);
257 }
258
259 static DropTarget FindDropTarget(int theWindow, int theDrag) {
260 Display display = Display.findDisplay(Thread.currentThread());
261 if (display is null || display.isDisposed()) return null;
262 Point mouse = new Point();
263 OS.GetDragMouse(theDrag, mouse, null);
264 int[] theRoot = new int[1];
265 OS.GetRootControl(theWindow, theRoot);
266 int[] theControl = new int[1];
267 Rect rect = new Rect();
268 OS.GetWindowBounds (theWindow, cast(short) OS.kWindowContentRgn, rect);
269 CGPoint inPoint = new CGPoint();
270 inPoint.x = mouse.h - rect.left;
271 inPoint.y = mouse.v - rect.top;
272 OS.HIViewGetSubviewHit(theRoot[0], inPoint, true, theControl);
273 if (!OS.IsControlEnabled(theControl[0])) return null;
274 Widget widget = display.findWidget(theControl[0]);
275 if (widget is null) return null;
276 return cast(DropTarget)widget.getData(DND.DROP_TARGET_KEY);
277 }
278 /** 154 /**
279 * Adds the listener to the collection of listeners who will 155 * Adds the listener to the collection of listeners who will
280 * be notified when a drag and drop operation is in progress, by sending 156 * be notified when a drag and drop operation is in progress, by sending
281 * it one of the messages defined in the <code>DropTargetListener</code> 157 * it one of the messages defined in the <code>DropTargetListener</code>
282 * interface. 158 * interface.
302 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 178 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
303 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 179 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
304 * </ul> 180 * </ul>
305 * 181 *
306 * @see DropTargetListener 182 * @see DropTargetListener
183 * @see #getDropListeners
307 * @see #removeDropListener 184 * @see #removeDropListener
308 * @see DropTargetEvent 185 * @see DropTargetEvent
309 */ 186 */
310 public void addDropListener(DropTargetListener listener) { 187 public void addDropListener(DropTargetListener listener) {
311 if (listener is null) DND.error (DWT.ERROR_NULL_ARGUMENT); 188 if (listener is null) DND.error (DWT.ERROR_NULL_ARGUMENT);
317 addListener (DND.DragOperationChanged, typedListener); 194 addListener (DND.DragOperationChanged, typedListener);
318 addListener (DND.Drop, typedListener); 195 addListener (DND.Drop, typedListener);
319 addListener (DND.DropAccept, typedListener); 196 addListener (DND.DropAccept, typedListener);
320 } 197 }
321 198
199 static int checkStyle (int style) {
200 if (style is DWT.NONE) return DND.DROP_MOVE;
201 return style;
202 }
203
322 protected void checkSubclass () { 204 protected void checkSubclass () {
323 String name = getClass().getName (); 205 String name = getClass().getName ();
324 String validName = DropTarget.class.getName(); 206 String validName = DropTarget.class.getName();
325 if (!validName.opEquals(name)) { 207 if (!validName.equals(name)) {
326 DND.error (DWT.ERROR_INVALID_SUBCLASS); 208 DND.error (DWT.ERROR_INVALID_SUBCLASS);
327 } 209 }
328 } 210 }
329 211
330 int dragReceiveHandler(int theWindow, int handlerRefCon, int theDrag) { 212 int draggingEntered(NSObject sender) {
331 updateDragOverHover(0, null); 213 selectedDataType = null;
332 if (keyOperation is -1) return OS.dragNotAcceptedErr; 214 selectedOperation = 0;
333 215
216 DNDEvent event = new DNDEvent();
217 if (!setEventData(sender, event)) {
218 keyOperation = -1;
219 if (OS.PTR_SIZEOF is 4) OS.SetThemeCursor(OS.kThemeNotAllowedCursor);
220 return OS.NSDragOperationNone;
221 }
222
223 int allowedOperations = event.operations;
224 TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length];
225 System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length);
226
227 notifyListeners(DND.DragEnter, event);
228
229 if (event.detail is DND.DROP_DEFAULT) {
230 event.detail = (allowedOperations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE;
231 }
232
233 selectedDataType = null;
234 for (int i = 0; i < allowedDataTypes.length; i++) {
235 if (allowedDataTypes[i].type is event.dataType.type) {
236 selectedDataType = allowedDataTypes[i];
237 break;
238 }
239 }
240
241 selectedOperation = DND.DROP_NONE;
242 if (selectedDataType !is null && (allowedOperations & event.detail) !is 0) {
243 selectedOperation = event.detail;
244 }
245
246 int osOperation = opToOsOp(selectedOperation);
247
248 if (OS.PTR_SIZEOF is 4) {
249 switch (selectedOperation) {
250 case DND.DROP_COPY:
251 OS.SetThemeCursor(OS.kThemeCopyArrowCursor);
252 break;
253 case DND.DROP_LINK:
254 OS.SetThemeCursor(OS.kThemeAliasArrowCursor);
255 break;
256 case DND.DROP_MOVE:
257 NSCursor.arrowCursor().set();
258 break;
259 default:
260 OS.SetThemeCursor(OS.kThemeNotAllowedCursor);
261 }
262 }
263 return osOperation;
264 }
265
266 void draggingExited(NSObject sender) {
267 NSCursor.arrowCursor().set();
268 if (keyOperation is -1) return;
269 keyOperation = -1;
270
334 DNDEvent event = new DNDEvent(); 271 DNDEvent event = new DNDEvent();
335 event.widget = this; 272 event.widget = this;
336 event.time = cast(int)System.currentTimeMillis(); 273 event.time = (int)System.currentTimeMillis();
337 event.detail = DND.DROP_NONE; 274 event.detail = DND.DROP_NONE;
338 notifyListeners(DND.DragLeave, event); 275 notifyListeners(DND.DragLeave, event);
276 }
277
278 int draggingUpdated(NSObject sender) {
279 if (sender is null) return OS.NSDragOperationNone;
280 int oldKeyOperation = keyOperation;
281
282 DNDEvent event = new DNDEvent();
283 if (!setEventData(sender, event)) {
284 keyOperation = -1;
285 if (OS.PTR_SIZEOF is 4) OS.SetThemeCursor(OS.kThemeNotAllowedCursor);
286 return OS.NSDragOperationNone;
287 }
288
289 int allowedOperations = event.operations;
290 TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length];
291 System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length);
292
293 if (keyOperation is oldKeyOperation) {
294 event.type = DND.DragOver;
295 event.dataType = selectedDataType;
296 event.detail = selectedOperation;
297 } else {
298 event.type = DND.DragOperationChanged;
299 event.dataType = selectedDataType;
300 }
301 notifyListeners(event.type, event);
302 if (event.detail is DND.DROP_DEFAULT) {
303 event.detail = (allowedOperations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE;
304 }
305
306 selectedDataType = null;
307 for (int i = 0; i < allowedDataTypes.length; i++) {
308 if (allowedDataTypes[i].type is event.dataType.type) {
309 selectedDataType = allowedDataTypes[i];
310 break;
311 }
312 }
313
314 selectedOperation = DND.DROP_NONE;
315 if (selectedDataType !is null && ((allowedOperations & event.detail) is event.detail)) {
316 selectedOperation = event.detail;
317 }
318 return opToOsOp(selectedOperation);
319 }
320
321 /**
322 * Creates a new <code>DropTarget</code> to allow data to be dropped on the specified
323 * <code>Control</code>.
324 * Creating an instance of a DropTarget may cause system resources to be allocated
325 * depending on the platform. It is therefore mandatory that the DropTarget instance
326 * be disposed when no longer required.
327 *
328 * @param control the <code>Control</code> over which the user positions the cursor to drop the data
329 * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of
330 * DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK
331 *
332 * @exception DWTException <ul>
333 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
334 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
335 * </ul>
336 * @exception DWTError <ul>
337 * <li>ERROR_CANNOT_INIT_DROP - unable to initiate drop target; this will occur if more than one
338 * drop target is created for a control or if the operating system will not allow the creation
339 * of the drop target</li>
340 * </ul>
341 *
342 * <p>NOTE: ERROR_CANNOT_INIT_DROP should be an DWTException, since it is a
343 * recoverable error, but can not be changed due to backward compatibility.</p>
344 *
345 * @see Widget#dispose
346 * @see DropTarget#checkSubclass
347 * @see DND#DROP_NONE
348 * @see DND#DROP_COPY
349 * @see DND#DROP_MOVE
350 * @see DND#DROP_LINK
351 */
352 public DropTarget(Control control, int style) {
353 super(control, checkStyle(style));
354 this.control = control;
355
356 if (control.getData(DND.DROP_TARGET_KEY) !is null) {
357 DND.error(DND.ERROR_CANNOT_INIT_DROP);
358 }
359
360 control.setData(DND.DROP_TARGET_KEY, this);
361
362 controlListener = new Listener () {
363 public void handleEvent (Event event) {
364 if (!DropTarget.this.isDisposed()) {
365 DropTarget.this.dispose();
366 }
367 }
368 };
369 control.addListener (DWT.Dispose, controlListener);
370
371 this.addListener(DWT.Dispose, new Listener() {
372 public void handleEvent (Event event) {
373 onDispose();
374 }
375 });
376
377 Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT);
378 if (effect instanceof DropTargetEffect) {
379 dropEffect = (DropTargetEffect) effect;
380 } else if (control instanceof Table) {
381 dropEffect = new TableDropTargetEffect((Table) control);
382 } else if (control instanceof Tree) {
383 dropEffect = new TreeDropTargetEffect((Tree) control);
384 }
385
386 addDragHandlers();
387 }
388
389 static int /*long*/ dropTargetProc(int /*long*/ id, int /*long*/ sel) {
390 Display display = Display.findDisplay(Thread.currentThread());
391 if (display is null || display.isDisposed()) return 0;
392 Widget widget = display.findWidget(id);
393 if (widget is null) return 0;
394 DropTarget dt = (DropTarget)widget.getData(DND.DROP_TARGET_KEY);
395 if (dt is null) return 0;
396
397 if (sel is OS.sel_wantsPeriodicDraggingUpdates) {
398 return dt.wantsPeriodicDraggingUpdates() ? 1 : 0;
399 }
400
401 return 0;
402 }
403
404 static int /*long*/ dropTargetProc(int /*long*/ id, int /*long*/ sel, int /*long*/ arg0) {
405 Display display = Display.findDisplay(Thread.currentThread());
406 if (display is null || display.isDisposed()) return 0;
407 Widget widget = display.findWidget(id);
408 if (widget is null) return 0;
409 DropTarget dt = (DropTarget)widget.getData(DND.DROP_TARGET_KEY);
410 if (dt is null) return 0;
411
412 // arg0 is _always_ the sender, and implements NSDraggingInfo.
413 // Looks like an NSObject for our purposes, though.
414 NSObject sender = new NSObject(arg0);
415
416 if (sel is OS.sel_draggingEntered_) {
417 return dt.draggingEntered(sender);
418 } else if (sel is OS.sel_draggingUpdated_) {
419 return dt.draggingUpdated(sender);
420 } else if (sel is OS.sel_draggingExited_) {
421 dt.draggingExited(sender);
422 } else if (sel is OS.sel_performDragOperation_) {
423 return dt.performDragOperation(sender) ? 1 : 0;
424 }
425
426 return 0;
427 }
428
429 /**
430 * Returns the Control which is registered for this DropTarget. This is the control over which the
431 * user positions the cursor to drop the data.
432 *
433 * @return the Control which is registered for this DropTarget
434 */
435 public Control getControl () {
436 return control;
437 }
438
439 /**
440 * Returns an array of listeners who will be notified when a drag and drop
441 * operation is in progress, by sending it one of the messages defined in
442 * the <code>DropTargetListener</code> interface.
443 *
444 * @return the listeners who will be notified when a drag and drop
445 * operation is in progress
446 *
447 * @exception DWTException <ul>
448 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
449 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
450 * </ul>
451 *
452 * @see DropTargetListener
453 * @see #addDropListener
454 * @see #removeDropListener
455 * @see DropTargetEvent
456 *
457 * @since 3.4
458 */
459 public DropTargetListener[] getDropListeners() {
460 Listener[] listeners = getListeners(DND.DragEnter);
461 int length = listeners.length;
462 DropTargetListener[] dropListeners = new DropTargetListener[length];
463 int count = 0;
464 for (int i = 0; i < length; i++) {
465 Listener listener = listeners[i];
466 if (listener instanceof DNDListener) {
467 dropListeners[count] = (DropTargetListener) ((DNDListener) listener).getEventListener();
468 count++;
469 }
470 }
471 if (count is length) return dropListeners;
472 DropTargetListener[] result = new DropTargetListener[count];
473 System.arraycopy(dropListeners, 0, result, 0, count);
474 return result;
475 }
476
477 /**
478 * Returns the drop effect for this DropTarget. This drop effect will be
479 * used during a drag and drop to display the drag under effect on the
480 * target widget.
481 *
482 * @return the drop effect that is registered for this DropTarget
483 *
484 * @since 3.3
485 */
486 public DropTargetEffect getDropTargetEffect() {
487 return dropEffect;
488 }
489
490 int getOperationFromKeyState() {
491 // The NSDraggingInfo object already combined the modifier keys with the
492 // drag source's allowed events. This might be better accomplished by diffing
493 // the base drag source mask with the active drag state mask instead of snarfing
494 // the current event.
495
496 // See documentation on [NSDraggingInfo draggingSourceOperationMask] for the
497 // correct Cocoa behavior. Control + Option or Command is NSDragOperationGeneric,
498 // or DND.DROP_DEFAULT in the DWT.
499 NSEvent currEvent = NSApplication.sharedApplication().currentEvent();
500 int /*long*/ modifiers = currEvent.modifierFlags();
501 bool option = (modifiers & OS.NSAlternateKeyMask) is OS.NSAlternateKeyMask;
502 bool control = (modifiers & OS.NSControlKeyMask) is OS.NSControlKeyMask;
503 if (control && option) return DND.DROP_DEFAULT;
504 if (control) return DND.DROP_LINK;
505 if (option) return DND.DROP_COPY;
506 return DND.DROP_DEFAULT;
507 }
508
509 /**
510 * Returns a list of the data types that can be transferred to this DropTarget.
511 *
512 * @return a list of the data types that can be transferred to this DropTarget
513 */
514 public Transfer[] getTransfer() {
515 return transferAgents;
516 }
517
518 void onDispose () {
519 if (control is null)
520 return;
521 if (controlListener !is null)
522 control.removeListener(DWT.Dispose, controlListener);
523 controlListener = null;
524 control.setData(DND.DROP_TARGET_KEY, null);
525 transferAgents = null;
526
527 // Unregister the control as a drop target.
528 control.view.unregisterDraggedTypes();
529 control = null;
530 }
531
532 int opToOsOp(int operation) {
533 int osOperation = 0;
534 if ((operation & DND.DROP_COPY) !is 0){
535 osOperation |= OS.NSDragOperationCopy;
536 }
537 if ((operation & DND.DROP_LINK) !is 0) {
538 osOperation |= OS.NSDragOperationLink;
539 }
540 if ((operation & DND.DROP_MOVE) !is 0) {
541 osOperation |= OS.NSDragOperationMove;
542 }
543 if ((operation & DND.DROP_TARGET_MOVE) !is 0) {
544 osOperation |= OS.NSDragOperationDelete;
545 }
546 return osOperation;
547 }
548
549 int osOpToOp(int /*long*/ osOperation){
550 int operation = 0;
551 if ((osOperation & OS.NSDragOperationCopy) !is 0){
552 operation |= DND.DROP_COPY;
553 }
554 if ((osOperation & OS.NSDragOperationLink) !is 0) {
555 operation |= DND.DROP_LINK;
556 }
557 if ((osOperation & OS.NSDragOperationDelete) !is 0) {
558 operation |= DND.DROP_TARGET_MOVE;
559 }
560 if ((osOperation & OS.NSDragOperationMove) !is 0) {
561 operation |= DND.DROP_MOVE;
562 }
563 if (osOperation is OS.NSDragOperationEvery) {
564 operation = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
565 }
566 return operation;
567 }
568
569 bool performDragOperation(NSObject sender) {
570 DNDEvent event = new DNDEvent();
571 event.widget = this;
572 event.time = (int)System.currentTimeMillis();
573
574 if (dropEffect !is null) {
575 NSPoint mouseLocation = sender.draggingLocation();
576 NSPoint globalLoc = sender.draggingDestinationWindow().convertBaseToScreen(mouseLocation);
577 event.item = dropEffect.getItem((int)globalLoc.x, (int)globalLoc.y);
578 }
579
580 event.detail = DND.DROP_NONE;
581 notifyListeners(DND.DragLeave, event);
339 582
340 event = new DNDEvent(); 583 event = new DNDEvent();
341 if (!setEventData(theDrag, event)) { 584 if (!setEventData(sender, event)) {
342 return OS.dragNotAcceptedErr; 585 return false;
343 } 586 }
344 587
345 keyOperation = -1; 588 keyOperation = -1;
346 int allowedOperations = event.operations; 589 int allowedOperations = event.operations;
347 TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; 590 TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length];
348 System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, event.dataTypes.length); 591 System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, event.dataTypes.length);
349 event.dataType = selectedDataType; 592 event.dataType = selectedDataType;
350 event.detail = selectedOperation; 593 event.detail = selectedOperation;
594 notifyListeners(DND.DropAccept, event);
595
351 selectedDataType = null; 596 selectedDataType = null;
352 selectedOperation = DND.DROP_NONE;
353 notifyListeners(DND.DropAccept, event);
354
355 if (event.dataType !is null) { 597 if (event.dataType !is null) {
356 for (int i = 0; i < allowedDataTypes.length; i++) { 598 for (int i = 0; i < allowedDataTypes.length; i++) {
357 if (allowedDataTypes[i].type is event.dataType.type) { 599 if (allowedDataTypes[i].type is event.dataType.type) {
358 selectedDataType = allowedDataTypes[i]; 600 selectedDataType = allowedDataTypes[i];
359 break; 601 break;
360 } 602 }
361 } 603 }
362 } 604 }
605
606 selectedOperation = DND.DROP_NONE;
363 if (selectedDataType !is null && (event.detail & allowedOperations) !is 0) { 607 if (selectedDataType !is null && (event.detail & allowedOperations) !is 0) {
364 selectedOperation = event.detail; 608 selectedOperation = event.detail;
365 } 609 }
610
366 if (selectedOperation is DND.DROP_NONE) { 611 if (selectedOperation is DND.DROP_NONE) {
367 // this was not a successful drop 612 return false;
368 return OS.dragNotAcceptedErr; 613 }
369 } 614
370 // ask drag source for dropped data 615 // ask drag source for dropped data
371 byte[][] data = new byte[0][]; 616 NSPasteboard pasteboard = sender.draggingPasteboard();
372 // locate all the items with data of the desired type 617 NSObject data = null;
373 short[] numItems = new short[1]; 618 NSMutableArray types = NSMutableArray.arrayWithCapacity(10);
374 OS.CountDragItems(theDrag, numItems); 619
375 for (short i = 0; i < numItems[0]; i++) { 620 for (int i = 0; i < transferAgents.length; i++){
376 int[] theItemRef = new int[1]; 621 Transfer transfer = transferAgents[i];
377 OS.GetDragItemReferenceNumber(theDrag, cast(short) (i+1), theItemRef); 622 String[] typeNames = transfer.getTypeNames();
378 int[] size = new int[1]; 623 int[] typeIds = transfer.getTypeIds();
379 OS.GetFlavorDataSize(theDrag, theItemRef[0], selectedDataType.type, size); 624
380 if (size[0] > 0) { 625 for (int j = 0; j < typeNames.length; j++) {
381 byte[] buffer = new byte[size[0]]; 626 if (selectedDataType.type is typeIds[j]) {
382 OS.GetFlavorData(theDrag, theItemRef[0], selectedDataType.type, buffer, size, 0); 627 types.addObject(NSString.stringWith(typeNames[j]));
383 byte[][] newData = new byte[data.length + 1][]; 628 break;
384 System.arraycopy(data, 0, newData, 0, data.length); 629 }
385 newData[data.length] = buffer; 630 }
386 data = newData; 631 }
387 } 632
388 } 633 NSString type = pasteboard.availableTypeFromArray(types);
634 TransferData tdata = new TransferData();
635
636 if (type !is null) {
637 tdata.type = Transfer.registerType(type.getString());
638 if (type.isEqual(OS.NSStringPboardType) ||
639 type.isEqual(OS.NSHTMLPboardType) ||
640 type.isEqual(OS.NSRTFPboardType)) {
641 tdata.data = pasteboard.stringForType(type);
642 } else if (type.isEqual(OS.NSURLPboardType)) {
643 tdata.data = NSURL.URLFromPasteboard(pasteboard);
644 } else if (type.isEqual(OS.NSFilenamesPboardType)) {
645 tdata.data = new NSArray(pasteboard.propertyListForType(type).id);
646 } else {
647 tdata.data = pasteboard.dataForType(type);
648 }
649 }
650
651 if (tdata.data !is null) {
652 data = tdata.data;
653 }
654
389 // Get Data in a Java format 655 // Get Data in a Java format
390 Object object = null; 656 Object object = null;
391 for (int i = 0; i < transferAgents.length; i++) { 657 for (int i = 0; i < transferAgents.length; i++) {
392 Transfer transfer = transferAgents[i]; 658 Transfer transfer = transferAgents[i];
393 if (transfer !is null && transfer.isSupportedType(selectedDataType)) { 659 if (transfer !is null && transfer.isSupportedType(selectedDataType)) {
408 selectedOperation = DND.DROP_NONE; 674 selectedOperation = DND.DROP_NONE;
409 if ((allowedOperations & event.detail) is event.detail) { 675 if ((allowedOperations & event.detail) is event.detail) {
410 selectedOperation = event.detail; 676 selectedOperation = event.detail;
411 } 677 }
412 //notify source of action taken 678 //notify source of action taken
413 int action = opToOsOp(selectedOperation); 679 return (selectedOperation !is DND.DROP_NONE);
414 OS.SetDragDropAction(theDrag, action);
415 return (selectedOperation is DND.DROP_NONE) ? OS.dragNotAcceptedErr : OS.noErr;
416 }
417
418 int dragTrackingHandler(int message, int theWindow, int handlerRefCon, int theDrag) {
419
420 if (message is OS.kDragTrackingLeaveWindow) {
421 updateDragOverHover(0, null);
422 OS.SetThemeCursor(OS.kThemeArrowCursor);
423 if (keyOperation is -1) return OS.dragNotAcceptedErr;
424 keyOperation = -1;
425
426 DNDEvent event = new DNDEvent();
427 event.widget = this;
428 event.time = cast(int)System.currentTimeMillis();
429 event.detail = DND.DROP_NONE;
430 notifyListeners(DND.DragLeave, event);
431 return OS.noErr;
432 }
433
434 int oldKeyOperation = keyOperation;
435
436 if (message is OS.kDragTrackingEnterWindow) {
437 selectedDataType = null;
438 selectedOperation = 0;
439 }
440
441 DNDEvent event = new DNDEvent();
442 if (!setEventData(theDrag, event)) {
443 keyOperation = -1;
444 OS.SetThemeCursor(OS.kThemeNotAllowedCursor);
445 return OS.dragNotAcceptedErr;
446 }
447
448 int allowedOperations = event.operations;
449 TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length];
450 System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length);
451
452 switch (message) {
453 case OS.kDragTrackingEnterWindow:
454 event.type = DND.DragEnter;
455 break;
456 case OS.kDragTrackingInWindow:
457 if (keyOperation is oldKeyOperation) {
458 event.type = DND.DragOver;
459 event.dataType = selectedDataType;
460 event.detail = selectedOperation;
461 }else {
462 event.type = DND.DragOperationChanged;
463 event.dataType = selectedDataType;
464 }
465 break;
466 }
467
468 updateDragOverHover(DRAGOVER_HYSTERESIS, event);
469 selectedDataType = null;
470 selectedOperation = DND.DROP_NONE;
471 notifyListeners(event.type, event);
472
473 if (event.detail is DND.DROP_DEFAULT) {
474 event.detail = (allowedOperations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE;
475 }
476
477 if (event.dataType !is null) {
478 for (int i = 0; i < allowedDataTypes.length; i++) {
479 if (allowedDataTypes[i].type is event.dataType.type) {
480 selectedDataType = allowedDataTypes[i];
481 break;
482 }
483 }
484 }
485
486 if (selectedDataType !is null && (allowedOperations & event.detail) !is 0) {
487 selectedOperation = event.detail;
488 }
489
490 OS.SetDragDropAction(theDrag, opToOsOp(selectedOperation));
491
492 switch (selectedOperation) {
493 case DND.DROP_COPY:
494 OS.SetThemeCursor(OS.kThemeCopyArrowCursor);
495 break;
496 case DND.DROP_LINK:
497 OS.SetThemeCursor(OS.kThemeAliasArrowCursor);
498 break;
499 case DND.DROP_MOVE:
500 OS.SetThemeCursor(OS.kThemeArrowCursor);
501 break;
502 default:
503 OS.SetThemeCursor(OS.kThemeNotAllowedCursor);
504 }
505
506 if (message is OS.kDragTrackingEnterWindow) {
507 dragOverHeartbeat.run();
508 }
509 return OS.noErr;
510 }
511
512 /**
513 * Returns the Control which is registered for this DropTarget. This is the control over which the
514 * user positions the cursor to drop the data.
515 *
516 * @return the Control which is registered for this DropTarget
517 */
518 public Control getControl () {
519 return control;
520 }
521
522 /**
523 * Returns an array of listeners who will be notified when a drag and drop
524 * operation is in progress, by sending it one of the messages defined in
525 * the <code>DropTargetListener</code> interface.
526 *
527 * @exception DWTException <ul>
528 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
529 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
530 * </ul>
531 *
532 * @see DropTargetListener
533 * @see #addDropListener
534 * @see #removeDropListener
535 * @see DropTargetEvent
536 *
537 * @since 3.4
538 */
539 public DropTargetListener[] getDropListeners() {
540 Listener[] listeners = getListeners(DND.DragEnter);
541 int length = listeners.length;
542 DropTargetListener[] dropListeners = new DropTargetListener[length];
543 int count = 0;
544 for (int i = 0; i < length; i++) {
545 Listener listener = listeners[i];
546 if ( null !is cast(DNDListener)listener ) {
547 dropListeners[count] = cast(DropTargetListener) (cast(DNDListener) listener).getEventListener();
548 count++;
549 }
550 }
551 if (count is length) return dropListeners;
552 DropTargetListener[] result = new DropTargetListener[count];
553 System.arraycopy(dropListeners, 0, result, 0, count);
554 return result;
555 }
556
557 /**
558 * Returns the drop effect for this DropTarget. This drop effect will be
559 * used during a drag and drop to display the drag under effect on the
560 * target widget.
561 *
562 * @return the drop effect that is registered for this DropTarget
563 *
564 * @since 3.3
565 */
566 public DropTargetEffect getDropTargetEffect() {
567 return dropEffect;
568 }
569
570 int getOperationFromKeyState(int theDrag) {
571 short[] modifiers = new short[1];
572 OS.GetDragModifiers(theDrag, modifiers, null, null);
573 bool option = (modifiers[0] & OS.optionKey) is OS.optionKey;
574 bool command = (modifiers[0] & OS.cmdKey) is OS.cmdKey;
575 if (option && command) return DND.DROP_LINK;
576 if (option) return DND.DROP_COPY;
577 if (command) return DND.DROP_MOVE;
578 return DND.DROP_DEFAULT;
579 }
580
581 /**
582 * Returns a list of the data types that can be transferred to this DropTarget.
583 *
584 * @return a list of the data types that can be transferred to this DropTarget
585 */
586 public Transfer[] getTransfer() {
587 return transferAgents;
588 }
589
590 void onDispose () {
591 if (control is null)
592 return;
593 if (controlListener !is null)
594 control.removeListener(DWT.Dispose, controlListener);
595 controlListener = null;
596 control.setData(DND.DROP_TARGET_KEY, null);
597 transferAgents = null;
598 control = null;
599 }
600
601 int opToOsOp(int operation) {
602 int osOperation = 0;
603 if ((operation & DND.DROP_COPY) !is 0){
604 osOperation |= OS.kDragActionCopy;
605 }
606 if ((operation & DND.DROP_LINK) !is 0) {
607 osOperation |= OS.kDragActionAlias;
608 }
609 if ((operation & DND.DROP_MOVE) !is 0) {
610 osOperation |= OS.kDragActionMove;
611 }
612 return osOperation;
613 }
614
615 int osOpToOp(int osOperation){
616 int operation = 0;
617 if ((osOperation & OS.kDragActionCopy) !is 0){
618 operation |= DND.DROP_COPY;
619 }
620 if ((osOperation & OS.kDragActionAlias) !is 0) {
621 operation |= DND.DROP_LINK;
622 }
623 if ((osOperation & OS.kDragActionMove) !is 0) {
624 operation |= DND.DROP_MOVE;
625 }
626 if (osOperation is OS.kDragActionAll) {
627 operation = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
628 }
629 return operation;
630 } 680 }
631 681
632 /** 682 /**
633 * Removes the listener from the collection of listeners who will 683 * Removes the listener from the collection of listeners who will
634 * be notified when a drag and drop operation is in progress. 684 * be notified when a drag and drop operation is in progress.
635 * 685 *
636 * @param listener the listener which should be notified 686 * @param listener the listener which should no longer be notified
637 * 687 *
638 * @exception IllegalArgumentException <ul> 688 * @exception IllegalArgumentException <ul>
639 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 689 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
640 * </ul> 690 * </ul>
641 * @exception DWTException <ul> 691 * @exception DWTException <ul>
643 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 693 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
644 * </ul> 694 * </ul>
645 * 695 *
646 * @see DropTargetListener 696 * @see DropTargetListener
647 * @see #addDropListener 697 * @see #addDropListener
698 * @see #getDropListeners
648 */ 699 */
649 public void removeDropListener(DropTargetListener listener) { 700 public void removeDropListener(DropTargetListener listener) {
650 if (listener is null) DND.error (DWT.ERROR_NULL_ARGUMENT); 701 if (listener is null) DND.error (DWT.ERROR_NULL_ARGUMENT);
651 removeListener (DND.DragEnter, listener); 702 removeListener (DND.DragEnter, listener);
652 removeListener (DND.DragLeave, listener); 703 removeListener (DND.DragLeave, listener);
667 */ 718 */
668 public void setDropTargetEffect(DropTargetEffect effect) { 719 public void setDropTargetEffect(DropTargetEffect effect) {
669 dropEffect = effect; 720 dropEffect = effect;
670 } 721 }
671 722
672 bool setEventData(int theDrag, DNDEvent event) { 723 bool setEventData(NSObject draggingState, DNDEvent event) {
673 if (theDrag is 0) return false; 724 if (draggingState is null) return false;
674 725
675 // get allowed operations 726 // get allowed operations
676 int style = getStyle(); 727 int style = getStyle();
677 int[] outActions = new int[1]; 728 int /*long*/ allowedActions = draggingState.draggingSourceOperationMask();
678 OS.GetDragAllowableActions(theDrag, outActions); 729 int operations = osOpToOp(allowedActions) & style;
679 int operations = osOpToOp(outActions[0]) & style;
680 if (operations is DND.DROP_NONE) return false; 730 if (operations is DND.DROP_NONE) return false;
681 731
682 //get current operation 732 // get current operation
683 int operation = getOperationFromKeyState(theDrag); 733 int operation = getOperationFromKeyState();
684 keyOperation = operation; 734 keyOperation = operation;
685 if (operation is DND.DROP_DEFAULT) { 735 if (operation is DND.DROP_DEFAULT) {
686 if ((style & DND.DROP_DEFAULT) is 0) { 736 if ((style & DND.DROP_DEFAULT) is 0) {
687 operation = (operations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; 737 operation = (operations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE;
688 } 738 }
689 } else { 739 } else {
690 if ((operation & operations) is 0) operation = DND.DROP_NONE; 740 if ((operation & operations) is 0) operation = DND.DROP_NONE;
691 } 741 }
692 742
743
693 // get allowed transfer types 744 // get allowed transfer types
694 short[] numItems = new short[1]; 745 NSPasteboard dragPBoard = draggingState.draggingPasteboard();
695 OS.CountDragItems(theDrag, numItems); 746 NSArray draggedTypes = dragPBoard.types();
696 int[] flavors = new int[10]; 747 if (draggedTypes is null) return false;
748
749 int /*long*/ draggedTypeCount = draggedTypes.count();
750
751 TransferData[] dataTypes = new TransferData[(int)draggedTypeCount];
697 int index = -1; 752 int index = -1;
698 //Get a unique list of flavors 753 for (int i = 0; i < draggedTypeCount; i++) {
699 for (short i = 0; i < numItems[0]; i++) { 754 id draggedType = draggedTypes.objectAtIndex(i);
700 int[] theItemRef = new int[1]; 755 NSString nativeDataType = new NSString(draggedType);
701 OS.GetDragItemReferenceNumber(theDrag, cast(short) (i+1), theItemRef); 756 TransferData data = new TransferData();
702 short[] numFlavors = new short[1]; 757 data.type = Transfer.registerType(nativeDataType.getString());
703 OS.CountDragItemFlavors(theDrag, theItemRef[0], numFlavors); 758
704 int[] theType = new int[1]; 759 for (int j = 0; j < transferAgents.length; j++) {
705 for (int j = 0; j < numFlavors[0]; j++) { 760 Transfer transfer = transferAgents[j];
706 theType[0] = 0; 761 if (transfer !is null && transfer.isSupportedType(data)) {
707 if (OS.GetFlavorType(theDrag, theItemRef[0], cast(short) (j+1), theType) is OS.noErr) { 762 dataTypes[++index] = data;
708 bool unique = true; 763 break;
709 for (int k = 0; k < flavors.length; k++) {
710 if (flavors[k] is theType[0]) {
711 unique = false;
712 break;
713 }
714 }
715 if (unique) {
716 if (index is flavors.length - 1) {
717 int[] temp = new int[flavors.length + 10];
718 System.arraycopy(flavors, 0, temp, 0, flavors.length);
719 flavors = temp;
720 }
721 flavors[++index] = theType[0];
722 }
723 }
724 }
725 }
726 if (index is -1) return false;
727
728 TransferData[] dataTypes = new TransferData[index+1];
729 index = -1;
730 for (int i = 0; i < dataTypes.length; i++) {
731 if (flavors[i] !is 0) {
732 TransferData data = new TransferData();
733 data.type = flavors[i];
734 for (int j = 0; j < transferAgents.length; j++) {
735 Transfer transfer = transferAgents[j];
736 if (transfer !is null && transfer.isSupportedType(data)) {
737 dataTypes[++index] = data;
738 break;
739 }
740 } 764 }
741 } 765 }
742 } 766 }
743 if (index is -1) return false; 767 if (index is -1) return false;
744 768
746 TransferData[] temp = new TransferData[index + 1]; 770 TransferData[] temp = new TransferData[index + 1];
747 System.arraycopy(dataTypes, 0, temp, 0, index + 1); 771 System.arraycopy(dataTypes, 0, temp, 0, index + 1);
748 dataTypes = temp; 772 dataTypes = temp;
749 } 773 }
750 774
751 Point mouse = new Point(); 775 // Convert from window-relative to global coordinates, and flip it.
752 OS.GetDragMouse(theDrag, mouse, null); 776 NSPoint mouse = draggingState.draggingLocation();
777 NSPoint globalMouse = draggingState.draggingDestinationWindow().convertBaseToScreen(mouse);
778 NSArray screens = NSScreen.screens();
779 NSRect screenRect = new NSScreen(screens.objectAtIndex(0)).frame();
780 globalMouse.y = screenRect.height - globalMouse.y;
753 781
754 event.widget = this; 782 event.widget = this;
755 event.x = mouse.h; 783 event.x = (int)globalMouse.x;
756 event.y = mouse.v; 784 event.y = (int)globalMouse.y;
757 event.time = cast(int)System.currentTimeMillis(); 785 event.time = (int)System.currentTimeMillis();
758 event.feedback = DND.FEEDBACK_SELECT; 786 event.feedback = DND.FEEDBACK_SELECT;
759 event.dataTypes = dataTypes; 787 event.dataTypes = dataTypes;
760 event.dataType = dataTypes[0]; 788 event.dataType = dataTypes[0];
761 event.operations = operations; 789 event.operations = operations;
762 event.detail = operation; 790 event.detail = operation;
781 * </ul> 809 * </ul>
782 */ 810 */
783 public void setTransfer(Transfer[] transferAgents){ 811 public void setTransfer(Transfer[] transferAgents){
784 if (transferAgents is null) DND.error(DWT.ERROR_NULL_ARGUMENT); 812 if (transferAgents is null) DND.error(DWT.ERROR_NULL_ARGUMENT);
785 this.transferAgents = transferAgents; 813 this.transferAgents = transferAgents;
786 } 814
787 815
788 void updateDragOverHover(long delay, DNDEvent event) { 816 // Register the types as valid drop types in Cocoa.
789 if (delay is 0) { 817 // Accumulate all of the transfer types into a list.
790 dragOverStart = 0; 818 ArrayList typeStrings = new ArrayList();
791 dragOverEvent = null; 819
792 return; 820 for (int i = 0; i < this.transferAgents.length; i++) {
793 } 821 String[] types = transferAgents[i].getTypeNames();
794 dragOverStart = System.currentTimeMillis() + delay; 822
795 if (dragOverEvent is null) dragOverEvent = new DNDEvent(); 823 for (int j = 0; j < types.length; j++) {
796 dragOverEvent.x = event.x; 824 typeStrings.add(types[j]);
797 dragOverEvent.y = event.y; 825 }
798 dragOverEvent.dataTypes = event.dataTypes; 826 }
799 dragOverEvent.operations = event.operations; 827
800 dragOverEvent.dataType = event.dataType; 828 // Convert to an NSArray of NSStrings so we can register with the Control.
801 dragOverEvent.detail = event.detail; 829 int typeStringCount = typeStrings.size();
802 } 830 NSMutableArray nsTypeStrings = NSMutableArray.arrayWithCapacity(typeStringCount);
803 831
804 } 832 for (int i = 0; i < typeStringCount; i++) {
833 nsTypeStrings.addObject(NSString.stringWith((String)typeStrings.get(i)));
834 }
835
836 control.view.registerForDraggedTypes(nsTypeStrings);
837
838 }
839
840 // By returning true we get draggingUpdated messages even when the mouse isn't moving.
841 bool wantsPeriodicDraggingUpdates() {
842 return true;
843 }
844
845 }