Mercurial > projects > dwt2
annotate org.eclipse.swt.gtk.linux.x86/src/org/eclipse/swt/dnd/DropTarget.d @ 47:65761bc28ab2
swt linux again compilable for d1.
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Fri, 27 Mar 2009 11:43:53 +0100 |
parents | f713da8bc051 |
children | ddbfe84d86df |
rev | line source |
---|---|
25 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 2008 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module org.eclipse.swt.dnd.DropTarget; | |
14 | |
15 | |
16 | |
17 import org.eclipse.swt.SWT; | |
18 import org.eclipse.swt.SWTError; | |
19 import org.eclipse.swt.SWTException; | |
20 import org.eclipse.swt.graphics.Point; | |
21 import org.eclipse.swt.internal.gtk.OS; | |
22 import org.eclipse.swt.widgets.Combo; | |
23 import org.eclipse.swt.widgets.Control; | |
24 import org.eclipse.swt.widgets.Display; | |
25 import org.eclipse.swt.widgets.Event; | |
26 import org.eclipse.swt.widgets.Listener; | |
27 import org.eclipse.swt.widgets.Table; | |
28 import org.eclipse.swt.widgets.Tree; | |
29 import org.eclipse.swt.widgets.Widget; | |
30 import org.eclipse.swt.dnd.DND; | |
31 import org.eclipse.swt.dnd.Transfer; | |
32 import org.eclipse.swt.dnd.DropTargetEffect; | |
33 import org.eclipse.swt.dnd.DNDEvent; | |
34 import org.eclipse.swt.dnd.DNDListener; | |
35 import org.eclipse.swt.dnd.TransferData; | |
36 import org.eclipse.swt.dnd.DropTargetListener; | |
37 import org.eclipse.swt.dnd.TableDropTargetEffect; | |
38 import org.eclipse.swt.dnd.TreeDropTargetEffect; | |
39 import java.lang.all; | |
40 | |
47
65761bc28ab2
swt linux again compilable for d1.
Frank Benoit <benoit@tionex.de>
parents:
25
diff
changeset
|
41 import java.lang.Thread; |
25 | 42 static import tango.stdc.string; |
43 | |
44 /** | |
45 * | |
46 * Class <code>DropTarget</code> defines the target object for a drag and drop transfer. | |
47 * | |
48 * IMPORTANT: This class is <em>not</em> intended to be subclassed. | |
49 * | |
50 * <p>This class identifies the <code>Control</code> over which the user must position the cursor | |
51 * in order to drop the data being transferred. It also specifies what data types can be dropped on | |
52 * this control and what operations can be performed. You may have several DropTragets in an | |
53 * application but there can only be a one to one mapping between a <code>Control</code> and a <code>DropTarget</code>. | |
54 * The DropTarget can receive data from within the same application or from other applications | |
55 * (such as text dragged from a text editor like Word).</p> | |
56 * | |
57 * <code><pre> | |
58 * int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK; | |
59 * Transfer[] types = new Transfer[] {TextTransfer.getInstance()}; | |
60 * DropTarget target = new DropTarget(label, operations); | |
61 * target.setTransfer(types); | |
62 * </code></pre> | |
63 * | |
64 * <p>The application is notified of data being dragged over this control and of when a drop occurs by | |
65 * implementing the interface <code>DropTargetListener</code> which uses the class | |
66 * <code>DropTargetEvent</code>. The application can modify the type of drag being performed | |
67 * on this Control at any stage of the drag by modifying the <code>event.detail</code> field or the | |
68 * <code>event.currentDataType</code> field. When the data is dropped, it is the responsibility of | |
69 * the application to copy this data for its own purposes. | |
70 * | |
71 * <code><pre> | |
72 * target.addDropListener (new DropTargetListener() { | |
73 * public void dragEnter(DropTargetEvent event) {}; | |
74 * public void dragOver(DropTargetEvent event) {}; | |
75 * public void dragLeave(DropTargetEvent event) {}; | |
76 * public void dragOperationChanged(DropTargetEvent event) {}; | |
77 * public void dropAccept(DropTargetEvent event) {} | |
78 * public void drop(DropTargetEvent event) { | |
79 * // A drop has occurred, copy over the data | |
80 * if (event.data is null) { // no data to copy, indicate failure in event.detail | |
81 * event.detail = DND.DROP_NONE; | |
82 * return; | |
83 * } | |
84 * label.setText ((String) event.data); // data copied to label text | |
85 * } | |
86 * }); | |
87 * </pre></code> | |
88 * | |
89 * <dl> | |
90 * <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd> | |
91 * <dt><b>Events</b></dt> <dd>DND.DragEnter, DND.DragLeave, DND.DragOver, DND.DragOperationChanged, | |
92 * DND.DropAccept, DND.Drop </dd> | |
93 * </dl> | |
94 * | |
95 * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a> | |
96 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: DNDExample</a> | |
97 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> | |
98 */ | |
99 public class DropTarget : Widget { | |
100 | |
101 Control control; | |
102 Listener controlListener; | |
103 Transfer[] transferAgents; | |
104 DropTargetEffect dropEffect; | |
105 | |
106 // Track application selections | |
107 TransferData selectedDataType; | |
108 int selectedOperation; | |
109 | |
110 // workaround - There is no event for "operation changed" so track operation based on key state | |
111 int keyOperation = -1; | |
112 | |
113 // workaround - Simulate events when the mouse is not moving | |
114 long dragOverStart; | |
115 Runnable dragOverHeartbeat; | |
116 DNDEvent dragOverEvent; | |
117 | |
118 int drag_motion_handler; | |
119 int drag_leave_handler; | |
120 int drag_data_received_handler; | |
121 int drag_drop_handler; | |
122 | |
123 static const String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$ | |
124 static const int DRAGOVER_HYSTERESIS = 50; | |
125 | |
126 // static Callback Drag_Motion; | |
127 // static Callback Drag_Leave; | |
128 // static Callback Drag_Data_Received; | |
129 // static Callback Drag_Drop; | |
130 // | |
131 // static this(){ | |
132 // Drag_Motion = new Callback(DropTarget.class, "Drag_Motion", 5); //$NON-NLS-1$ | |
133 // if (Drag_Motion.getAddress() is 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); | |
134 // Drag_Leave = new Callback(DropTarget.class, "Drag_Leave", 3); //$NON-NLS-1$ | |
135 // if (Drag_Leave.getAddress() is 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); | |
136 // Drag_Data_Received = new Callback(DropTarget.class, "Drag_Data_Received", 7); //$NON-NLS-1$ | |
137 // if (Drag_Data_Received.getAddress() is 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); | |
138 // Drag_Drop = new Callback(DropTarget.class, "Drag_Drop", 5); //$NON-NLS-1$ | |
139 // if (Drag_Drop.getAddress() is 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); | |
140 // } | |
141 | |
142 /** | |
143 * Creates a new <code>DropTarget</code> to allow data to be dropped on the specified | |
144 * <code>Control</code>. | |
145 * Creating an instance of a DropTarget may cause system resources to be allocated | |
146 * depending on the platform. It is therefore mandatory that the DropTarget instance | |
147 * be disposed when no longer required. | |
148 * | |
149 * @param control the <code>Control</code> over which the user positions the cursor to drop the data | |
150 * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of | |
151 * DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK | |
152 * | |
153 * @exception SWTException <ul> | |
154 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> | |
155 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> | |
156 * </ul> | |
157 * @exception SWTError <ul> | |
158 * <li>ERROR_CANNOT_INIT_DROP - unable to initiate drop target; this will occur if more than one | |
159 * drop target is created for a control or if the operating system will not allow the creation | |
160 * of the drop target</li> | |
161 * </ul> | |
162 * | |
163 * <p>NOTE: ERROR_CANNOT_INIT_DROP should be an SWTException, since it is a | |
164 * recoverable error, but can not be changed due to backward compatibility.</p> | |
165 * | |
166 * @see Widget#dispose | |
167 * @see DropTarget#checkSubclass | |
168 * @see DND#DROP_NONE | |
169 * @see DND#DROP_COPY | |
170 * @see DND#DROP_MOVE | |
171 * @see DND#DROP_LINK | |
172 */ | |
173 public this(Control control, int style) { | |
174 super(control, checkStyle(style)); | |
175 this.control = control; | |
176 // if (Drag_Motion is null || Drag_Leave is null || Drag_Data_Received is null || Drag_Drop is null) { | |
177 // DND.error(DND.ERROR_CANNOT_INIT_DROP); | |
178 // } | |
179 if (control.getData(DND.DROP_TARGET_KEY) !is null) { | |
180 DND.error(DND.ERROR_CANNOT_INIT_DROP); | |
181 } | |
182 control.setData(DND.DROP_TARGET_KEY, this); | |
183 | |
184 drag_motion_handler = OS.g_signal_connect(control.handle, OS.drag_motion.ptr, cast(GCallback)&Drag_Motion, null); | |
185 drag_leave_handler = OS.g_signal_connect(control.handle, OS.drag_leave.ptr, cast(GCallback)&Drag_Leave, null); | |
186 drag_data_received_handler = OS.g_signal_connect(control.handle, OS.drag_data_received.ptr, cast(GCallback)&Drag_Data_Received, null); | |
187 drag_drop_handler = OS.g_signal_connect(control.handle, OS.drag_drop.ptr, cast(GCallback)&Drag_Drop, null); | |
188 | |
189 // Dispose listeners | |
190 controlListener = new class() Listener{ | |
191 public void handleEvent(Event event){ | |
192 if (!this.outer.isDisposed()){ | |
193 this.outer.dispose(); | |
194 } | |
195 } | |
196 }; | |
197 control.addListener(SWT.Dispose, controlListener); | |
198 | |
199 this.addListener(SWT.Dispose, new class() Listener { | |
200 public void handleEvent(Event event){ | |
201 onDispose(); | |
202 } | |
203 }); | |
204 | |
205 Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT); | |
206 if ( auto de = cast(DropTargetEffect)effect ) { | |
207 dropEffect = de; | |
208 } else if ( auto table = cast(Table)control ) { | |
209 dropEffect = new TableDropTargetEffect(table); | |
210 } else if ( auto tree = cast(Tree) control ) { | |
211 dropEffect = new TreeDropTargetEffect(tree); | |
212 } | |
213 | |
214 dragOverHeartbeat = new class() Runnable { | |
215 public void run() { | |
216 Control control = this.outer.control; | |
217 if (control is null || control.isDisposed() || dragOverStart is 0) return; | |
218 long time = System.currentTimeMillis(); | |
219 int delay = DRAGOVER_HYSTERESIS; | |
220 if (time < dragOverStart) { | |
221 delay = cast(int)(dragOverStart - time); | |
222 } else { | |
223 dragOverEvent.time += DRAGOVER_HYSTERESIS; | |
224 int allowedOperations = dragOverEvent.operations; | |
225 TransferData[] allowedTypes = dragOverEvent.dataTypes; | |
226 //pass a copy of data types in to listeners in case application modifies it | |
227 TransferData[] dataTypes = new TransferData[allowedTypes.length]; | |
228 System.arraycopy(allowedTypes, 0, dataTypes, 0, dataTypes.length); | |
229 | |
230 DNDEvent event = new DNDEvent(); | |
231 event.widget = dragOverEvent.widget; | |
232 event.x = dragOverEvent.x; | |
233 event.y = dragOverEvent.y; | |
234 event.time = dragOverEvent.time; | |
235 event.feedback = DND.FEEDBACK_SELECT; | |
236 event.dataTypes = dataTypes; | |
237 event.dataType = selectedDataType; | |
238 event.operations = dragOverEvent.operations; | |
239 event.detail = selectedOperation; | |
240 if (dropEffect !is null) { | |
241 event.item = dropEffect.getItem(dragOverEvent.x, dragOverEvent.y); | |
242 } | |
243 selectedDataType = null; | |
244 selectedOperation = DND.DROP_NONE; | |
245 notifyListeners(DND.DragOver, event); | |
246 if (event.dataType !is null) { | |
247 for (int i = 0; i < allowedTypes.length; i++) { | |
248 if (allowedTypes[i].type is event.dataType.type) { | |
249 selectedDataType = event.dataType; | |
250 break; | |
251 } | |
252 } | |
253 } | |
254 if (selectedDataType !is null && (event.detail & allowedOperations) !is 0) { | |
255 selectedOperation = event.detail; | |
256 } | |
257 } | |
258 control = this.outer.control; | |
259 if (control is null || control.isDisposed()) return; | |
260 control.getDisplay().timerExec(delay, dragOverHeartbeat); | |
261 } | |
262 }; | |
263 } | |
264 | |
265 static int checkStyle (int style) { | |
266 if (style is SWT.NONE) return DND.DROP_MOVE; | |
267 return style; | |
268 } | |
269 | |
270 private static extern(C) void Drag_Data_Received ( | |
271 GtkWidget *widget, | |
272 GdkDragContext *context, | |
273 int x, | |
274 int y, | |
275 GtkSelectionData *data, | |
276 uint info, | |
277 uint time, | |
278 void* user_data) | |
279 { | |
280 DropTarget target = FindDropTarget(widget); | |
281 if (target is null) return; | |
282 target.drag_data_received (widget, context, cast(int)/*64*/x, cast(int)/*64*/y, data, cast(int)/*64*/info, cast(int)/*64*/time); | |
283 } | |
284 | |
285 private static extern(C) int Drag_Drop( | |
286 GtkWidget *widget, | |
287 GdkDragContext *context, | |
288 int x, | |
289 int y, | |
290 uint time, | |
291 void* user_data) | |
292 { | |
293 DropTarget target = FindDropTarget(widget); | |
294 if (target is null) return 0; | |
295 return target.drag_drop (widget, context, cast(int)/*64*/x, cast(int)/*64*/y, cast(int)/*64*/time) ? 1 : 0; | |
296 } | |
297 | |
298 private static extern(C) void Drag_Leave ( | |
299 GtkWidget *widget, | |
300 GdkDragContext *context, | |
301 uint time, | |
302 void* user_data) | |
303 { | |
304 DropTarget target = FindDropTarget(widget); | |
305 if (target is null) return; | |
306 target.drag_leave (widget, context, cast(int)/*64*/time); | |
307 } | |
308 | |
309 private static extern(C) int Drag_Motion ( | |
310 GtkWidget *widget, | |
311 GdkDragContext *context, | |
312 int x, | |
313 int y, | |
314 uint time, | |
315 void* user_data) | |
316 { | |
317 DropTarget target = FindDropTarget(widget); | |
318 if (target is null) return 0; | |
319 return target.drag_motion (widget, context, cast(int)/*64*/x, cast(int)/*64*/y, cast(int)/*64*/time) ? 1 : 0; | |
320 } | |
321 | |
322 static DropTarget FindDropTarget(GtkWidget* handle) { | |
47
65761bc28ab2
swt linux again compilable for d1.
Frank Benoit <benoit@tionex.de>
parents:
25
diff
changeset
|
323 Display display = Display.findDisplay(Thread.currentThread()); |
25 | 324 if (display is null || display.isDisposed()) return null; |
325 Widget widget = display.findWidget(handle); | |
326 if (widget is null) return null; | |
327 return cast(DropTarget)widget.getData(DND.DROP_TARGET_KEY); | |
328 } | |
329 | |
330 /** | |
331 * Adds the listener to the collection of listeners who will | |
332 * be notified when a drag and drop operation is in progress, by sending | |
333 * it one of the messages defined in the <code>DropTargetListener</code> | |
334 * interface. | |
335 * | |
336 * <p><ul> | |
337 * <li><code>dragEnter</code> is called when the cursor has entered the drop target boundaries | |
338 * <li><code>dragLeave</code> is called when the cursor has left the drop target boundaries and just before | |
339 * the drop occurs or is cancelled. | |
340 * <li><code>dragOperationChanged</code> is called when the operation being performed has changed | |
341 * (usually due to the user changing the selected modifier key(s) while dragging) | |
342 * <li><code>dragOver</code> is called when the cursor is moving over the drop target | |
343 * <li><code>dropAccept</code> is called just before the drop is performed. The drop target is given | |
344 * the chance to change the nature of the drop or veto the drop by setting the <code>event.detail</code> field | |
345 * <li><code>drop</code> is called when the data is being dropped | |
346 * </ul></p> | |
347 * | |
348 * @param listener the listener which should be notified | |
349 * | |
350 * @exception IllegalArgumentException <ul> | |
351 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
352 * </ul> | |
353 * @exception SWTException <ul> | |
354 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
355 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
356 * </ul> | |
357 * | |
358 * @see DropTargetListener | |
359 * @see #getDropListeners | |
360 * @see #removeDropListener | |
361 * @see DropTargetEvent | |
362 */ | |
363 public void addDropListener(DropTargetListener listener) { | |
364 if (listener is null) DND.error (SWT.ERROR_NULL_ARGUMENT); | |
365 DNDListener typedListener = new DNDListener (listener); | |
366 typedListener.dndWidget = this; | |
367 addListener (DND.DragEnter, typedListener); | |
368 addListener (DND.DragLeave, typedListener); | |
369 addListener (DND.DragOver, typedListener); | |
370 addListener (DND.DragOperationChanged, typedListener); | |
371 addListener (DND.Drop, typedListener); | |
372 addListener (DND.DropAccept, typedListener); | |
373 } | |
374 | |
375 protected override void checkSubclass () { | |
376 String name = this.classinfo.name; | |
377 String validName = DropTarget.classinfo.name; | |
378 if ( validName !=/*eq*/ name ) { | |
379 DND.error (SWT.ERROR_INVALID_SUBCLASS); | |
380 } | |
381 } | |
382 | |
383 void drag_data_received ( | |
384 GtkWidget *widget, | |
385 GdkDragContext *context, | |
386 int x, | |
387 int y, | |
388 GtkSelectionData *data, | |
389 uint info, | |
390 uint time ) | |
391 { | |
392 DNDEvent event = new DNDEvent(); | |
393 if (data is null || !setEventData(context, x, y, time, event)) { | |
394 keyOperation = -1; | |
395 return; | |
396 } | |
397 keyOperation = -1; | |
398 | |
399 int allowedOperations = event.operations; | |
400 | |
401 // Get data in a Java format | |
402 Object object = null; | |
403 TransferData transferData = new TransferData(); | |
404 if (data.data !is null) { | |
405 transferData.type = data.type; | |
406 transferData.length = data.length; | |
407 transferData.pValue = data.data; | |
408 transferData.format = data.format; | |
409 for (int i = 0; i < transferAgents.length; i++) { | |
410 Transfer transfer = transferAgents[i]; | |
411 if (transfer !is null && transfer.isSupportedType(transferData)) { | |
412 object = transfer.nativeToJava(transferData); | |
413 break; | |
414 } | |
415 } | |
416 } | |
417 if (object is null) { | |
418 selectedOperation = DND.DROP_NONE; | |
419 } | |
420 | |
421 event.detail = selectedOperation; | |
422 event.dataType = transferData; | |
423 event.data = object; | |
424 selectedOperation = DND.DROP_NONE; | |
425 notifyListeners(DND.Drop, event); | |
426 if ((allowedOperations & event.detail) is event.detail) { | |
427 selectedOperation = event.detail; | |
428 } | |
429 //stop native handler | |
430 OS.g_signal_stop_emission_by_name(widget, OS.drag_data_received.ptr); | |
431 | |
432 //notify source of action taken | |
433 OS.gtk_drag_finish(context, selectedOperation !is DND.DROP_NONE, selectedOperation is DND.DROP_MOVE, time); | |
434 return; | |
435 } | |
436 | |
437 bool drag_drop( | |
438 GtkWidget *widget, | |
439 GdkDragContext *context, | |
440 int x, | |
441 int y, | |
442 uint time) | |
443 { | |
444 DNDEvent event = new DNDEvent(); | |
445 if (!setEventData(context, x, y, time, event)) { | |
446 keyOperation = -1; | |
447 return false; | |
448 } | |
449 keyOperation = -1; | |
450 | |
451 int allowedOperations = event.operations; | |
452 TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; | |
453 System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); | |
454 | |
455 event.dataType = selectedDataType; | |
456 event.detail = selectedOperation; | |
457 selectedDataType = null; | |
458 selectedOperation = DND.DROP_NONE; | |
459 notifyListeners(DND.DropAccept,event); | |
460 if (event.dataType !is null) { | |
461 for (int i = 0; i < allowedDataTypes.length; i++) { | |
462 if (allowedDataTypes[i].type is event.dataType.type) { | |
463 selectedDataType = allowedDataTypes[i]; | |
464 break; | |
465 } | |
466 } | |
467 } | |
468 if (selectedDataType !is null && ((event.detail & allowedOperations) is event.detail)) { | |
469 selectedOperation = event.detail; | |
470 } | |
471 if (selectedOperation is DND.DROP_NONE) { | |
472 // this was not a successful drop | |
473 return false; | |
474 } | |
475 // ask drag source for dropped data | |
476 OS.gtk_drag_get_data(widget, context, selectedDataType.type, time); | |
477 return true; | |
478 } | |
479 | |
480 void drag_leave( | |
481 GtkWidget *widget, | |
482 GdkDragContext *context, | |
483 uint time ) | |
484 { | |
485 updateDragOverHover(0, null); | |
486 | |
487 if (keyOperation is -1) return; | |
488 keyOperation = -1; | |
489 | |
490 DNDEvent event = new DNDEvent(); | |
491 event.widget = this; | |
492 event.time = time; | |
493 event.detail = DND.DROP_NONE; | |
494 notifyListeners(DND.DragLeave, event); | |
495 } | |
496 | |
497 bool drag_motion ( | |
498 GtkWidget *widget, | |
499 GdkDragContext *context, | |
500 int x, | |
501 int y, | |
502 uint time) | |
503 { | |
504 int oldKeyOperation = keyOperation; | |
505 | |
506 if (oldKeyOperation is -1) { //drag enter | |
507 selectedDataType = null; | |
508 selectedOperation = DND.DROP_NONE; | |
509 } | |
510 | |
511 DNDEvent event = new DNDEvent(); | |
512 if (!setEventData(context, x, y, time, event)) { | |
513 keyOperation = -1; | |
514 OS.gdk_drag_status(context, 0, time); | |
515 return false; | |
516 } | |
517 | |
518 int allowedOperations = event.operations; | |
519 TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; | |
520 System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); | |
521 | |
522 if (oldKeyOperation is -1) { | |
523 event.type = DND.DragEnter; | |
524 } else { | |
525 if (keyOperation is oldKeyOperation) { | |
526 event.type = DND.DragOver; | |
527 event.dataType = selectedDataType; | |
528 event.detail = selectedOperation; | |
529 } else { | |
530 event.type = DND.DragOperationChanged; | |
531 event.dataType = selectedDataType; | |
532 } | |
533 } | |
534 updateDragOverHover(DRAGOVER_HYSTERESIS, event); | |
535 selectedDataType = null; | |
536 selectedOperation = DND.DROP_NONE; | |
537 notifyListeners(event.type, event); | |
538 if (event.detail is DND.DROP_DEFAULT) { | |
539 event.detail = (allowedOperations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; | |
540 } | |
541 if (event.dataType !is null) { | |
542 for (int i = 0; i < allowedDataTypes.length; i++) { | |
543 if (allowedDataTypes[i].type is event.dataType.type) { | |
544 selectedDataType = allowedDataTypes[i]; | |
545 break; | |
546 } | |
547 } | |
548 } | |
549 if (selectedDataType !is null && (allowedOperations & event.detail) !is 0) { | |
550 selectedOperation = event.detail; | |
551 } | |
552 | |
553 switch (selectedOperation) { | |
554 case DND.DROP_NONE: | |
555 OS.gdk_drag_status(context, 0, time); | |
556 break; | |
557 case DND.DROP_COPY: | |
558 OS.gdk_drag_status(context, OS.GDK_ACTION_COPY, time); | |
559 break; | |
560 case DND.DROP_MOVE: | |
561 OS.gdk_drag_status(context, OS.GDK_ACTION_MOVE, time); | |
562 break; | |
563 case DND.DROP_LINK: | |
564 OS.gdk_drag_status(context, OS.GDK_ACTION_LINK, time); | |
565 break; | |
566 default: | |
567 } | |
568 | |
569 if (oldKeyOperation is -1) { | |
570 dragOverHeartbeat.run(); | |
571 } | |
572 return true; | |
573 } | |
574 | |
575 /** | |
576 * Returns the Control which is registered for this DropTarget. This is the control over which the | |
577 * user positions the cursor to drop the data. | |
578 * | |
579 * @return the Control which is registered for this DropTarget | |
580 */ | |
581 public Control getControl () { | |
582 return control; | |
583 } | |
584 | |
585 /** | |
586 * Returns an array of listeners who will be notified when a drag and drop | |
587 * operation is in progress, by sending it one of the messages defined in | |
588 * the <code>DropTargetListener</code> interface. | |
589 * | |
590 * @return the listeners who will be notified when a drag and drop | |
591 * operation is in progress | |
592 * | |
593 * @exception SWTException <ul> | |
594 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
595 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
596 * </ul> | |
597 * | |
598 * @see DropTargetListener | |
599 * @see #addDropListener | |
600 * @see #removeDropListener | |
601 * @see DropTargetEvent | |
602 * | |
603 * @since 3.4 | |
604 */ | |
605 public DropTargetListener[] getDropListeners() { | |
606 Listener[] listeners = getListeners(DND.DragEnter); | |
607 int length = listeners.length; | |
608 DropTargetListener[] dropListeners = new DropTargetListener[length]; | |
609 int count = 0; | |
610 for (int i = 0; i < length; i++) { | |
611 Listener listener = listeners[i]; | |
612 if ( auto l = cast(DNDListener)listener ) { | |
613 dropListeners[count] = cast(DropTargetListener) (l.getEventListener()); | |
614 count++; | |
615 } | |
616 } | |
617 if (count is length) return dropListeners; | |
618 DropTargetListener[] result = new DropTargetListener[count]; | |
619 SimpleType!(DropTargetListener).arraycopy(dropListeners, 0, result, 0, count); | |
620 return result; | |
621 } | |
622 | |
623 /** | |
624 * Returns the drop effect for this DropTarget. This drop effect will be | |
625 * used during a drag and drop to display the drag under effect on the | |
626 * target widget. | |
627 * | |
628 * @return the drop effect that is registered for this DropTarget | |
629 * | |
630 * @since 3.3 | |
631 */ | |
632 public DropTargetEffect getDropTargetEffect() { | |
633 return dropEffect; | |
634 } | |
635 | |
636 int getOperationFromKeyState() { | |
637 int state; | |
638 OS.gdk_window_get_pointer(null, null, null, &state); | |
639 bool ctrl = (state & OS.GDK_CONTROL_MASK) !is 0; | |
640 bool shift = (state & OS.GDK_SHIFT_MASK) !is 0; | |
641 if (ctrl && shift) return DND.DROP_LINK; | |
642 if (ctrl)return DND.DROP_COPY; | |
643 if (shift)return DND.DROP_MOVE; | |
644 return DND.DROP_DEFAULT; | |
645 } | |
646 | |
647 /** | |
648 * Returns a list of the data types that can be transferred to this DropTarget. | |
649 * | |
650 * @return a list of the data types that can be transferred to this DropTarget | |
651 */ | |
652 public Transfer[] getTransfer() { | |
653 return transferAgents; | |
654 } | |
655 | |
656 void onDispose(){ | |
657 if (control is null) return; | |
658 OS.g_signal_handler_disconnect(control.handle, drag_motion_handler); | |
659 OS.g_signal_handler_disconnect(control.handle, drag_leave_handler); | |
660 OS.g_signal_handler_disconnect(control.handle, drag_data_received_handler); | |
661 OS.g_signal_handler_disconnect(control.handle, drag_drop_handler); | |
662 if (transferAgents.length !is 0) | |
663 OS.gtk_drag_dest_unset(control.handle); | |
664 transferAgents = null; | |
665 if (controlListener !is null) | |
666 control.removeListener(SWT.Dispose, controlListener); | |
667 control.setData(DND.DROP_TARGET_KEY, null); | |
668 control = null; | |
669 controlListener = null; | |
670 } | |
671 | |
672 int opToOsOp(int operation){ | |
673 int osOperation = 0; | |
674 if ((operation & DND.DROP_COPY) is DND.DROP_COPY) | |
675 osOperation |= OS.GDK_ACTION_COPY; | |
676 if ((operation & DND.DROP_MOVE) is DND.DROP_MOVE) | |
677 osOperation |= OS.GDK_ACTION_MOVE; | |
678 if ((operation & DND.DROP_LINK) is DND.DROP_LINK) | |
679 osOperation |= OS.GDK_ACTION_LINK; | |
680 return osOperation; | |
681 } | |
682 | |
683 int osOpToOp(int osOperation){ | |
684 int operation = DND.DROP_NONE; | |
685 if ((osOperation & OS.GDK_ACTION_COPY) is OS.GDK_ACTION_COPY) | |
686 operation |= DND.DROP_COPY; | |
687 if ((osOperation & OS.GDK_ACTION_MOVE) is OS.GDK_ACTION_MOVE) | |
688 operation |= DND.DROP_MOVE; | |
689 if ((osOperation & OS.GDK_ACTION_LINK) is OS.GDK_ACTION_LINK) | |
690 operation |= DND.DROP_LINK; | |
691 return operation; | |
692 } | |
693 | |
694 /** | |
695 * Removes the listener from the collection of listeners who will | |
696 * be notified when a drag and drop operation is in progress. | |
697 * | |
698 * @param listener the listener which should no longer be notified | |
699 * | |
700 * @exception IllegalArgumentException <ul> | |
701 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
702 * </ul> | |
703 * @exception SWTException <ul> | |
704 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
705 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
706 * </ul> | |
707 * | |
708 * @see DropTargetListener | |
709 * @see #addDropListener | |
710 * @see #getDropListeners | |
711 */ | |
712 public void removeDropListener(DropTargetListener listener) { | |
713 if (listener is null) DND.error (SWT.ERROR_NULL_ARGUMENT); | |
714 removeListener (DND.DragEnter, listener); | |
715 removeListener (DND.DragLeave, listener); | |
716 removeListener (DND.DragOver, listener); | |
717 removeListener (DND.DragOperationChanged, listener); | |
718 removeListener (DND.Drop, listener); | |
719 removeListener (DND.DropAccept, listener); | |
720 } | |
721 | |
722 /** | |
723 * Specifies the data types that can be transferred to this DropTarget. If data is | |
724 * being dragged that does not match one of these types, the drop target will be notified of | |
725 * the drag and drop operation but the currentDataType will be null and the operation | |
726 * will be DND.NONE. | |
727 * | |
728 * @param transferAgents a list of Transfer objects which define the types of data that can be | |
729 * dropped on this target | |
730 * | |
731 * @exception IllegalArgumentException <ul> | |
732 * <li>ERROR_NULL_ARGUMENT - if transferAgents is null</li> | |
733 * </ul> | |
734 */ | |
735 public void setTransfer(Transfer[] transferAgents){ | |
736 if (transferAgents is null) DND.error(SWT.ERROR_NULL_ARGUMENT); | |
737 | |
738 if (this.transferAgents.length !is 0) { | |
739 OS.gtk_drag_dest_unset(control.handle); | |
740 } | |
741 this.transferAgents = transferAgents; | |
742 | |
743 GtkTargetEntry*[] targets; | |
744 for (int i = 0; i < transferAgents.length; i++) { | |
745 Transfer transfer = transferAgents[i]; | |
746 if (transfer !is null) { | |
747 int[] typeIds = transfer.getTypeIds(); | |
748 String[] typeNames = transfer.getTypeNames(); | |
749 for (int j = 0; j < typeIds.length; j++) { | |
750 GtkTargetEntry* entry = new GtkTargetEntry(); | |
751 entry.target = cast(char*)OS.g_malloc(typeNames[j].length +1); | |
752 entry.target[ 0 .. typeNames[j].length ] = typeNames[j]; | |
753 entry.target[ typeNames[j].length ] = '\0'; | |
754 entry.info = typeIds[j]; | |
755 GtkTargetEntry*[] newTargets = new GtkTargetEntry*[targets.length + 1]; | |
756 SimpleType!(GtkTargetEntry*).arraycopy(targets, 0, newTargets, 0, targets.length); | |
757 newTargets[targets.length] = entry; | |
758 targets = newTargets; | |
759 } | |
760 } | |
761 } | |
762 | |
763 auto pTargets = OS.g_malloc(targets.length * GtkTargetEntry.sizeof); | |
764 for (int i = 0; i < targets.length; i++) { | |
765 tango.stdc.string.memmove(pTargets + i*GtkTargetEntry.sizeof, targets[i], GtkTargetEntry.sizeof); | |
766 } | |
767 | |
768 int actions = opToOsOp(getStyle()); | |
769 if ( auto c = cast(Combo)control ) { | |
770 if ((control.getStyle() & SWT.READ_ONLY) is 0) { | |
771 auto entryHandle = OS.gtk_bin_get_child (control.handle); | |
772 if (entryHandle !is null) { | |
773 OS.gtk_drag_dest_unset(entryHandle); | |
774 } | |
775 } | |
776 } | |
777 OS.gtk_drag_dest_set(control.handle, 0, pTargets, targets.length, actions); | |
778 | |
779 for (int i = 0; i < targets.length; i++) { | |
780 OS.g_free(targets[i].target); | |
781 } | |
782 } | |
783 | |
784 /** | |
785 * Specifies the drop effect for this DropTarget. This drop effect will be | |
786 * used during a drag and drop to display the drag under effect on the | |
787 * target widget. | |
788 * | |
789 * @param effect the drop effect that is registered for this DropTarget | |
790 * | |
791 * @since 3.3 | |
792 */ | |
793 public void setDropTargetEffect(DropTargetEffect effect) { | |
794 dropEffect = effect; | |
795 } | |
796 | |
797 bool setEventData(GdkDragContext* dragContext, int x, int y, int time, DNDEvent event) { | |
798 if (dragContext is null) return false; | |
799 if (dragContext.targets is null) return false; | |
800 | |
801 // get allowed operations | |
802 int style = getStyle(); | |
803 int operations = osOpToOp(dragContext.actions) & style; | |
804 if (operations is DND.DROP_NONE) return false; | |
805 | |
806 // get current operation | |
807 int operation = getOperationFromKeyState(); | |
808 keyOperation = operation; | |
809 if (operation is DND.DROP_DEFAULT) { | |
810 if ((style & DND.DROP_DEFAULT) is 0) { | |
811 operation = (operations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; | |
812 } | |
813 } else { | |
814 if ((operation & operations) is 0) operation = DND.DROP_NONE; | |
815 } | |
816 | |
817 // Get allowed transfer types | |
818 int length = OS.g_list_length(dragContext.targets); | |
819 TransferData[] dataTypes = new TransferData[0]; | |
820 for (int i = 0; i < length; i++) { | |
821 auto pData = OS.g_list_nth(dragContext.targets, i); | |
822 GtkTargetPair* gtkTargetPair = cast(GtkTargetPair*)pData; | |
823 TransferData data = new TransferData(); | |
824 data.type = gtkTargetPair.target; | |
825 for (int j = 0; j < transferAgents.length; j++) { | |
826 Transfer transfer = transferAgents[j]; | |
827 if (transfer !is null && transfer.isSupportedType(data)) { | |
828 TransferData[] newDataTypes = new TransferData[dataTypes.length + 1]; | |
829 System.arraycopy(dataTypes, 0, newDataTypes, 0, dataTypes.length); | |
830 newDataTypes[dataTypes.length] = data; | |
831 dataTypes = newDataTypes; | |
832 break; | |
833 } | |
834 } | |
835 } | |
836 if (dataTypes.length is 0) return false; | |
837 | |
838 auto window = OS.GTK_WIDGET_WINDOW(control.handle); | |
839 int origin_x, origin_y; | |
840 OS.gdk_window_get_origin(window, &origin_x, &origin_y); | |
841 Point coordinates = new Point(origin_x + x, origin_y + y); | |
842 | |
843 event.widget = this; | |
844 event.x = coordinates.x; | |
845 event.y = coordinates.y; | |
846 event.time = time; | |
847 event.feedback = DND.FEEDBACK_SELECT; | |
848 event.dataTypes = dataTypes; | |
849 event.dataType = dataTypes[0]; | |
850 event.operations = operations; | |
851 event.detail = operation; | |
852 if (dropEffect !is null) { | |
853 event.item = dropEffect.getItem(coordinates.x, coordinates.y); | |
854 } | |
855 return true; | |
856 } | |
857 | |
858 void updateDragOverHover(long delay, DNDEvent event) { | |
859 if (delay is 0) { | |
860 dragOverStart = 0; | |
861 dragOverEvent = null; | |
862 return; | |
863 } | |
864 dragOverStart = System.currentTimeMillis() + delay; | |
865 if (dragOverEvent is null) dragOverEvent = new DNDEvent(); | |
866 dragOverEvent.x = event.x; | |
867 dragOverEvent.y = event.y; | |
868 TransferData[] dataTypes = new TransferData[ event.dataTypes.length]; | |
869 System.arraycopy( event.dataTypes, 0, dataTypes, 0, dataTypes.length); | |
870 dragOverEvent.dataTypes = dataTypes; | |
871 dragOverEvent.operations = event.operations; | |
872 dragOverEvent.time = event.time; | |
873 } | |
874 | |
875 } |