Mercurial > projects > dwt-mac
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 } |