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 41 import java.lang.Thread; 42 version(Tango){ 43 static import tango.stdc..string; 44 } else { // Phobos 45 } 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 ptrdiff_t drag_motion_handler; 122 ptrdiff_t drag_leave_handler; 123 ptrdiff_t drag_data_received_handler; 124 ptrdiff_t 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, x, y, data, info, 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, x, y, 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, 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, x, y, time) ? 1 : 0; 323 } 324 325 static DropTarget FindDropTarget(GtkWidget* handle) { 326 Display display = Display.findDisplay(Thread.currentThread()); 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 auto length = listeners.length; 611 DropTargetListener[] dropListeners = new DropTargetListener[length]; 612 int count = 0; 613 for (typeof(length) 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*) 755 OS.g_malloc(typeNames[j].length +1); 756 entry.target[ 0 .. typeNames[j].length ] = typeNames[j]; 757 entry.target[ typeNames[j].length ] = '\0'; 758 entry.info = typeIds[j]; 759 GtkTargetEntry*[] newTargets = new GtkTargetEntry*[targets.length + 1]; 760 SimpleType!(GtkTargetEntry*).arraycopy(targets, 0, newTargets, 761 0, targets.length); 762 newTargets[targets.length] = entry; 763 targets = newTargets; 764 } 765 } 766 } 767 768 auto pTargets = OS.g_malloc(targets.length * GtkTargetEntry.sizeof); 769 for (int i = 0; i < targets.length; i++) { 770 OS.memmove(pTargets + i*GtkTargetEntry.sizeof, targets[i], GtkTargetEntry.sizeof); 771 } 772 773 int actions = opToOsOp(getStyle()); 774 if ( auto c = cast(Combo)control ) { 775 if ((control.getStyle() & SWT.READ_ONLY) is 0) { 776 auto entryHandle = OS.gtk_bin_get_child (control.handle); 777 if (entryHandle !is null) { 778 OS.gtk_drag_dest_unset(entryHandle); 779 } 780 } 781 } 782 OS.gtk_drag_dest_set(control.handle, 0, pTargets, 783 cast(int)/*64bit*/targets.length, actions); 784 785 for (int i = 0; i < targets.length; i++) { 786 OS.g_free(targets[i].target); 787 } 788 } 789 790 /** 791 * Specifies the drop effect for this DropTarget. This drop effect will be 792 * used during a drag and drop to display the drag under effect on the 793 * target widget. 794 * 795 * @param effect the drop effect that is registered for this DropTarget 796 * 797 * @since 3.3 798 */ 799 public void setDropTargetEffect(DropTargetEffect effect) { 800 dropEffect = effect; 801 } 802 803 bool setEventData(GdkDragContext* dragContext, int x, int y, int time, DNDEvent event) { 804 if (dragContext is null) return false; 805 if (dragContext.targets is null) return false; 806 807 // get allowed operations 808 int style = getStyle(); 809 int operations = osOpToOp(dragContext.actions) & style; 810 if (operations is DND.DROP_NONE) return false; 811 812 // get current operation 813 int operation = getOperationFromKeyState(); 814 keyOperation = operation; 815 if (operation is DND.DROP_DEFAULT) { 816 if ((style & DND.DROP_DEFAULT) is 0) { 817 operation = (operations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; 818 } 819 } else { 820 if ((operation & operations) is 0) operation = DND.DROP_NONE; 821 } 822 823 // Get allowed transfer types 824 int length = OS.g_list_length(dragContext.targets); 825 TransferData[] dataTypes = new TransferData[0]; 826 for (int i = 0; i < length; i++) { 827 auto pData = OS.g_list_nth(dragContext.targets, i); 828 GtkTargetPair* gtkTargetPair = cast(GtkTargetPair*)pData; 829 TransferData data = new TransferData(); 830 data.type = gtkTargetPair.target; 831 for (int j = 0; j < transferAgents.length; j++) { 832 Transfer transfer = transferAgents[j]; 833 if (transfer !is null && transfer.isSupportedType(data)) { 834 TransferData[] newDataTypes = new TransferData[dataTypes.length + 1]; 835 System.arraycopy(dataTypes, 0, newDataTypes, 0, dataTypes.length); 836 newDataTypes[dataTypes.length] = data; 837 dataTypes = newDataTypes; 838 break; 839 } 840 } 841 } 842 if (dataTypes.length is 0) return false; 843 844 auto window = OS.GTK_WIDGET_WINDOW(control.handle); 845 int origin_x, origin_y; 846 OS.gdk_window_get_origin(window, &origin_x, &origin_y); 847 Point coordinates = new Point(origin_x + x, origin_y + y); 848 849 event.widget = this; 850 event.x = coordinates.x; 851 event.y = coordinates.y; 852 event.time = time; 853 event.feedback = DND.FEEDBACK_SELECT; 854 event.dataTypes = dataTypes; 855 event.dataType = dataTypes[0]; 856 event.operations = operations; 857 event.detail = operation; 858 if (dropEffect !is null) { 859 event.item = dropEffect.getItem(coordinates.x, coordinates.y); 860 } 861 return true; 862 } 863 864 void updateDragOverHover(long delay, DNDEvent event) { 865 if (delay is 0) { 866 dragOverStart = 0; 867 dragOverEvent = null; 868 return; 869 } 870 dragOverStart = System.currentTimeMillis() + delay; 871 if (dragOverEvent is null) dragOverEvent = new DNDEvent(); 872 dragOverEvent.x = event.x; 873 dragOverEvent.y = event.y; 874 TransferData[] dataTypes = new TransferData[ event.dataTypes.length]; 875 System.arraycopy( event.dataTypes, 0, dataTypes, 0, dataTypes.length); 876 dragOverEvent.dataTypes = dataTypes; 877 dragOverEvent.operations = event.operations; 878 dragOverEvent.time = event.time; 879 } 880 881 }