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