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.DragSource; 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.Image; 21 import org.eclipse.swt.graphics.ImageData; 22 import org.eclipse.swt.internal.gtk.OS; 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.Transfer; 31 import org.eclipse.swt.dnd.DragSourceEffect; 32 import org.eclipse.swt.dnd.DragSourceListener; 33 import org.eclipse.swt.dnd.DND; 34 import org.eclipse.swt.dnd.TreeDragSourceEffect; 35 import org.eclipse.swt.dnd.TableDragSourceEffect; 36 import org.eclipse.swt.dnd.DNDListener; 37 import org.eclipse.swt.dnd.DNDEvent; 38 import org.eclipse.swt.dnd.TransferData; 39 import java.lang.all; 40 41 import java.lang.Thread; 42 43 /** 44 * 45 * <code>DragSource</code> defines the source object for a drag and drop transfer. 46 * 47 * <p>IMPORTANT: This class is <em>not</em> intended to be subclassed.</p> 48 * 49 * <p>A drag source is the object which originates a drag and drop operation. For the specified widget, 50 * it defines the type of data that is available for dragging and the set of operations that can 51 * be performed on that data. The operations can be any bit-wise combination of DND.MOVE, DND.COPY or 52 * DND.LINK. The type of data that can be transferred is specified by subclasses of Transfer such as 53 * TextTransfer or FileTransfer. The type of data transferred can be a predefined system type or it 54 * can be a type defined by the application. For instructions on how to define your own transfer type, 55 * refer to <code>ByteArrayTransfer</code>.</p> 56 * 57 * <p>You may have several DragSources in an application but you can only have one DragSource 58 * per Control. Data dragged from this DragSource can be dropped on a site within this application 59 * or it can be dropped on another application such as an external Text editor.</p> 60 * 61 * <p>The application supplies the content of the data being transferred by implementing the 62 * <code>DragSourceListener</code> and associating it with the DragSource via DragSource#addDragListener.</p> 63 * 64 * <p>When a successful move operation occurs, the application is required to take the appropriate 65 * action to remove the data from its display and remove any associated operating system resources or 66 * internal references. Typically in a move operation, the drop target makes a copy of the data 67 * and the drag source deletes the original. However, sometimes copying the data can take a long 68 * time (such as copying a large file). Therefore, on some platforms, the drop target may actually 69 * move the data in the operating system rather than make a copy. This is usually only done in 70 * file transfers. In this case, the drag source is informed in the DragEnd event that a 71 * DROP_TARGET_MOVE was performed. It is the responsibility of the drag source at this point to clean 72 * up its displayed information. No action needs to be taken on the operating system resources.</p> 73 * 74 * <p> The following example shows a Label widget that allows text to be dragged from it.</p> 75 * 76 * <code><pre> 77 * // Enable a label as a Drag Source 78 * Label label = new Label(shell, SWT.NONE); 79 * // This example will allow text to be dragged 80 * Transfer[] types = new Transfer[] {TextTransfer.getInstance()}; 81 * // This example will allow the text to be copied or moved to the drop target 82 * int operations = DND.DROP_MOVE | DND.DROP_COPY; 83 * 84 * DragSource source = new DragSource(label, operations); 85 * source.setTransfer(types); 86 * source.addDragListener(new DragSourceListener() { 87 * public void dragStart(DragSourceEvent e) { 88 * // Only start the drag if there is actually text in the 89 * // label - this text will be what is dropped on the target. 90 * if (label.getText().length() is 0) { 91 * event.doit = false; 92 * } 93 * }; 94 * public void dragSetData(DragSourceEvent event) { 95 * // A drop has been performed, so provide the data of the 96 * // requested type. 97 * // (Checking the type of the requested data is only 98 * // necessary if the drag source supports more than 99 * // one data type but is shown here as an example). 100 * if (TextTransfer.getInstance().isSupportedType(event.dataType)){ 101 * event.data = label.getText(); 102 * } 103 * } 104 * public void dragFinished(DragSourceEvent event) { 105 * // A Move operation has been performed so remove the data 106 * // from the source 107 * if (event.detail is DND.DROP_MOVE) 108 * label.setText(""); 109 * } 110 * }); 111 * </pre></code> 112 * 113 * 114 * <dl> 115 * <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd> 116 * <dt><b>Events</b></dt> <dd>DND.DragStart, DND.DragSetData, DND.DragEnd</dd> 117 * </dl> 118 * 119 * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a> 120 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: DNDExample</a> 121 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 122 */ 123 public class DragSource : Widget { 124 125 // info for registering as a drag source 126 Control control; 127 Listener controlListener; 128 Transfer[] transferAgents; 129 DragSourceEffect dragEffect; 130 131 void* targetList; 132 133 //workaround - remember action performed for DragEnd 134 bool moveData = false; 135 136 static const String DEFAULT_DRAG_SOURCE_EFFECT = "DEFAULT_DRAG_SOURCE_EFFECT"; //$NON-NLS-1$ 137 138 // static Callback DragGetData; 139 // static Callback DragEnd; 140 // static Callback DragDataDelete; 141 // static this() { 142 // DragGetData = new Callback(DragSource.class, "DragGetData", 5); //$NON-NLS-1$ 143 // if (DragGetData.getAddress() is 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); 144 // DragEnd = new Callback(DragSource.class, "DragEnd", 2); //$NON-NLS-1$ 145 // if (DragEnd.getAddress() is 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); 146 // DragDataDelete = new Callback(DragSource.class, "DragDataDelete", 2); //$NON-NLS-1$ 147 // if (DragDataDelete.getAddress() is 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); 148 // } 149 150 /** 151 * Creates a new <code>DragSource</code> to handle dragging from the specified <code>Control</code>. 152 * Creating an instance of a DragSource may cause system resources to be allocated depending on the platform. 153 * It is therefore mandatory that the DragSource instance be disposed when no longer required. 154 * 155 * @param control the <code>Control</code> that the user clicks on to initiate the drag 156 * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of 157 * DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK 158 * 159 * @exception SWTException <ul> 160 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 161 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 162 * </ul> 163 * @exception SWTError <ul> 164 * <li>ERROR_CANNOT_INIT_DRAG - unable to initiate drag source; this will occur if more than one 165 * drag source is created for a control or if the operating system will not allow the creation 166 * of the drag source</li> 167 * </ul> 168 * 169 * <p>NOTE: ERROR_CANNOT_INIT_DRAG should be an SWTException, since it is a 170 * recoverable error, but can not be changed due to backward compatibility.</p> 171 * 172 * @see Widget#dispose 173 * @see DragSource#checkSubclass 174 * @see DND#DROP_NONE 175 * @see DND#DROP_COPY 176 * @see DND#DROP_MOVE 177 * @see DND#DROP_LINK 178 */ 179 public this(Control control, int style) { 180 super (control, checkStyle(style)); 181 this.control = control; 182 // if (DragGetData is null || DragEnd is null || DragDataDelete is null) { 183 // DND.error(DND.ERROR_CANNOT_INIT_DRAG); 184 // } 185 if (control.getData(DND.DRAG_SOURCE_KEY) !is null) { 186 DND.error(DND.ERROR_CANNOT_INIT_DRAG); 187 } 188 control.setData(DND.DRAG_SOURCE_KEY, this); 189 190 OS.g_signal_connect(control.handle, OS.drag_data_get.ptr, cast(GCallback)&DragGetData, null); 191 OS.g_signal_connect(control.handle, OS.drag_end.ptr, cast(GCallback)&DragEnd, null); 192 OS.g_signal_connect(control.handle, OS.drag_data_delete.ptr, cast(GCallback)&DragDataDelete, null); 193 194 controlListener = new class() Listener { 195 public void handleEvent (Event event) { 196 if (event.type is SWT.Dispose) { 197 if (!this.outer.isDisposed()) { 198 this.outer.dispose(); 199 } 200 } 201 if (event.type is SWT.DragDetect) { 202 if (!this.outer.isDisposed()) { 203 this.outer.drag(event); 204 } 205 } 206 } 207 }; 208 control.addListener (SWT.Dispose, controlListener); 209 control.addListener (SWT.DragDetect, controlListener); 210 211 Object effect = control.getData(DEFAULT_DRAG_SOURCE_EFFECT); 212 if ( auto de = cast(DragSourceEffect)effect ) { 213 dragEffect = de; 214 } else if ( auto tree = cast(Tree)control) { 215 dragEffect = new TreeDragSourceEffect(tree); 216 } else if ( auto table = cast(Table)control ) { 217 dragEffect = new TableDragSourceEffect(table); 218 } 219 220 this.addListener(SWT.Dispose, new class() Listener { 221 public void handleEvent(Event e) { 222 onDispose(); 223 } 224 }); 225 } 226 227 static int checkStyle (int style) { 228 if (style is SWT.NONE) return DND.DROP_MOVE; 229 return style; 230 } 231 232 private static extern(C) void DragDataDelete( 233 GtkWidget *widget, 234 GdkDragContext *drag_context, 235 void* user_data ) 236 { 237 DragSource source = FindDragSource(widget); 238 if (source is null) return; 239 source.dragDataDelete(widget, drag_context); 240 return; 241 } 242 243 private static extern(C) void DragEnd ( 244 GtkWidget *widget, 245 GdkDragContext *drag_context, 246 void* user_data) 247 { 248 DragSource source = FindDragSource(widget); 249 if (source is null) return; 250 source.dragEnd(widget, drag_context); 251 return; 252 } 253 254 private static extern(C) void DragGetData( 255 GtkWidget *widget, 256 GdkDragContext *context, 257 GtkSelectionData *selection_data, 258 uint info, 259 uint time, 260 void* user_data ) 261 { 262 DragSource source = FindDragSource(widget); 263 if (source is null) return; 264 source.dragGetData(widget, context, selection_data, info, time); 265 return; 266 } 267 268 static DragSource FindDragSource(GtkWidget* handle) { 269 Display display = Display.findDisplay(Thread.currentThread()); 270 if (display is null || display.isDisposed()) return null; 271 Widget widget = display.findWidget(handle); 272 if (widget is null) return null; 273 return cast(DragSource)widget.getData(DND.DRAG_SOURCE_KEY); 274 } 275 276 /** 277 * Adds the listener to the collection of listeners who will 278 * be notified when a drag and drop operation is in progress, by sending 279 * it one of the messages defined in the <code>DragSourceListener</code> 280 * interface. 281 * 282 * <p><ul> 283 * <li><code>dragStart</code> is called when the user has begun the actions required to drag the widget. 284 * This event gives the application the chance to decide if a drag should be started. 285 * <li><code>dragSetData</code> is called when the data is required from the drag source. 286 * <li><code>dragFinished</code> is called when the drop has successfully completed (mouse up 287 * over a valid target) or has been terminated (such as hitting the ESC key). Perform cleanup 288 * such as removing data from the source side on a successful move operation. 289 * </ul></p> 290 * 291 * @param listener the listener which should be notified 292 * 293 * @exception IllegalArgumentException <ul> 294 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 295 * </ul> 296 * @exception SWTException <ul> 297 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 298 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 299 * </ul> 300 * 301 * @see DragSourceListener 302 * @see #getDragListeners 303 * @see #removeDragListener 304 * @see DragSourceEvent 305 */ 306 public void addDragListener(DragSourceListener listener) { 307 if (listener is null) DND.error (SWT.ERROR_NULL_ARGUMENT); 308 DNDListener typedListener = new DNDListener (listener); 309 typedListener.dndWidget = this; 310 addListener (DND.DragStart, typedListener); 311 addListener (DND.DragSetData, typedListener); 312 addListener (DND.DragEnd, typedListener); 313 } 314 315 protected override void checkSubclass () { 316 String name = this.classinfo.name; 317 String validName = DragSource.classinfo.name; 318 if ( validName !=/*eq*/ name ) { 319 DND.error (SWT.ERROR_INVALID_SUBCLASS); 320 } 321 } 322 323 void drag(Event dragEvent) { 324 moveData = false; 325 DNDEvent event = new DNDEvent(); 326 event.widget = this; 327 event.x = dragEvent.x; 328 event.y = dragEvent.y; 329 event.time = dragEvent.time; 330 event.doit = true; 331 notifyListeners(DND.DragStart, event); 332 if (!event.doit || transferAgents is null || transferAgents.length is 0) return; 333 if (targetList is null) return; 334 335 int actions = opToOsOp(getStyle()); 336 Image image = event.image; 337 auto context = OS.gtk_drag_begin(control.handle, targetList, actions, 1, null); 338 if (context !is null && image !is null) { 339 auto pixbuf = createPixbuf(image); 340 OS.gtk_drag_set_icon_pixbuf(context, pixbuf, 0, 0); 341 OS.g_object_unref(pixbuf); 342 } 343 } 344 345 void dragEnd( 346 GtkWidget *widget, 347 GdkDragContext *context ) 348 { 349 /* 350 * Bug in GTK. If a drag is initiated using gtk_drag_begin and the 351 * mouse is released immediately, the mouse and keyboard remain 352 * grabbed. The fix is to release the grab on the mouse and keyboard 353 * whenever the drag is terminated. 354 * 355 * NOTE: We believe that it is never an error to ungrab when 356 * a drag is finished. 357 */ 358 OS.gdk_pointer_ungrab(OS.GDK_CURRENT_TIME); 359 OS.gdk_keyboard_ungrab(OS.GDK_CURRENT_TIME); 360 361 int operation = DND.DROP_NONE; 362 if (context !is null) { 363 GdkDragContext* gdkDragContext = context; 364 if (gdkDragContext.dest_window !is null) { //NOTE: if dest_window is 0, drag was aborted 365 if (moveData) { 366 operation = DND.DROP_MOVE; 367 } else { 368 operation = osOpToOp(gdkDragContext.action); 369 if (operation is DND.DROP_MOVE) operation = DND.DROP_NONE; 370 } 371 } 372 } 373 374 DNDEvent event = new DNDEvent(); 375 event.widget = this; 376 //event.time = ??? 377 event.doit = operation !is 0; 378 event.detail = operation; 379 notifyListeners(DND.DragEnd, event); 380 moveData = false; 381 } 382 383 void dragGetData( 384 GtkWidget *widget, 385 GdkDragContext *context, 386 GtkSelectionData *gtkSelectionData, 387 uint info, 388 uint time ) 389 { 390 if (gtkSelectionData is null) return; 391 if (gtkSelectionData.target is null) return; 392 393 TransferData transferData = new TransferData(); 394 transferData.type = gtkSelectionData.target; 395 transferData.pValue = gtkSelectionData.data; 396 transferData.length = gtkSelectionData.length; 397 transferData.format = gtkSelectionData.format; 398 399 DNDEvent event = new DNDEvent(); 400 event.widget = this; 401 event.time = time; 402 event.dataType = transferData; 403 notifyListeners(DND.DragSetData, event); 404 405 Transfer transfer = null; 406 for (int i = 0; i < transferAgents.length; i++) { 407 Transfer transferAgent = transferAgents[i]; 408 if (transferAgent !is null && transferAgent.isSupportedType(transferData)) { 409 transfer = transferAgent; 410 break; 411 } 412 } 413 if (transfer is null) return; 414 transfer.javaToNative(event.data, transferData); 415 if (transferData.result !is 1) return; 416 OS.gtk_selection_data_set(gtkSelectionData, transferData.type, transferData.format, transferData.pValue, transferData.length); 417 OS.g_free(transferData.pValue); 418 return; 419 } 420 421 void dragDataDelete( 422 GtkWidget *widget, 423 GdkDragContext *drag_context) 424 { 425 moveData = true; 426 } 427 428 /** 429 * Returns the Control which is registered for this DragSource. This is the control that the 430 * user clicks in to initiate dragging. 431 * 432 * @return the Control which is registered for this DragSource 433 */ 434 public Control getControl () { 435 return control; 436 } 437 438 /** 439 * Returns an array of listeners who will be notified when a drag and drop 440 * operation is in progress, by sending it one of the messages defined in 441 * the <code>DragSourceListener</code> interface. 442 * 443 * @return the listeners who will be notified when a drag and drop 444 * operation is in progress 445 * 446 * @exception SWTException <ul> 447 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 448 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 449 * </ul> 450 * 451 * @see DragSourceListener 452 * @see #addDragListener 453 * @see #removeDragListener 454 * @see DragSourceEvent 455 * 456 * @since 3.4 457 */ 458 public DragSourceListener[] getDragListeners() { 459 Listener[] listeners = getListeners(DND.DragStart); 460 auto length = listeners.length; 461 DragSourceListener[] dragListeners = new DragSourceListener[length]; 462 int count = 0; 463 for (typeof(length) i = 0; i < length; i++) { 464 Listener listener = listeners[i]; 465 if ( auto l = cast(DNDListener)listener ) { 466 dragListeners[count] = cast(DragSourceListener) (l.getEventListener()); 467 count++; 468 } 469 } 470 if (count is length) return dragListeners; 471 DragSourceListener[] result = new DragSourceListener[count]; 472 SimpleType!(DragSourceListener).arraycopy(dragListeners, 0, result, 0, count); 473 return result; 474 } 475 476 /** 477 * Returns the drag effect that is registered for this DragSource. This drag 478 * effect will be used during a drag and drop operation. 479 * 480 * @return the drag effect that is registered for this DragSource 481 * 482 * @since 3.3 483 */ 484 public DragSourceEffect getDragSourceEffect() { 485 return dragEffect; 486 } 487 488 /** 489 * Returns the list of data types that can be transferred by this DragSource. 490 * 491 * @return the list of data types that can be transferred by this DragSource 492 */ 493 public Transfer[] getTransfer(){ 494 return transferAgents; 495 } 496 497 void onDispose() { 498 if (control is null) return; 499 if (targetList !is null) { 500 OS.gtk_target_list_unref(targetList); 501 } 502 targetList = null; 503 if (controlListener !is null) { 504 control.removeListener(SWT.Dispose, controlListener); 505 control.removeListener(SWT.DragDetect, controlListener); 506 } 507 controlListener = null; 508 control.setData(DND.DRAG_SOURCE_KEY, null); 509 control = null; 510 transferAgents = null; 511 } 512 513 int opToOsOp(int operation){ 514 int osOperation = 0; 515 516 if ((operation & DND.DROP_COPY) is DND.DROP_COPY) 517 osOperation |= OS.GDK_ACTION_COPY; 518 if ((operation & DND.DROP_MOVE) is DND.DROP_MOVE) 519 osOperation |= OS.GDK_ACTION_MOVE; 520 if ((operation & DND.DROP_LINK) is DND.DROP_LINK) 521 osOperation |= OS.GDK_ACTION_LINK; 522 523 return osOperation; 524 } 525 526 int osOpToOp(ptrdiff_t osOperation){ 527 int operation = DND.DROP_NONE; 528 529 if ((osOperation & OS.GDK_ACTION_COPY) is OS.GDK_ACTION_COPY) 530 operation |= DND.DROP_COPY; 531 if ((osOperation & OS.GDK_ACTION_MOVE) is OS.GDK_ACTION_MOVE) 532 operation |= DND.DROP_MOVE; 533 if ((osOperation & OS.GDK_ACTION_LINK) is OS.GDK_ACTION_LINK) 534 operation |= DND.DROP_LINK; 535 536 return operation; 537 } 538 539 /** 540 * Removes the listener from the collection of listeners who will 541 * be notified when a drag and drop operation is in progress. 542 * 543 * @param listener the listener which should no longer be notified 544 * 545 * @exception IllegalArgumentException <ul> 546 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 547 * </ul> 548 * @exception SWTException <ul> 549 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 550 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 551 * </ul> 552 * 553 * @see DragSourceListener 554 * @see #addDragListener 555 * @see #getDragListeners 556 */ 557 public void removeDragListener(DragSourceListener listener) { 558 if (listener is null) DND.error (SWT.ERROR_NULL_ARGUMENT); 559 removeListener (DND.DragStart, listener); 560 removeListener (DND.DragSetData, listener); 561 removeListener (DND.DragEnd, listener); 562 } 563 564 /** 565 * Specifies the drag effect for this DragSource. This drag effect will be 566 * used during a drag and drop operation. 567 * 568 * @param effect the drag effect that is registered for this DragSource 569 * 570 * @since 3.3 571 */ 572 public void setDragSourceEffect(DragSourceEffect effect) { 573 dragEffect = effect; 574 } 575 576 /** 577 * Specifies the list of data types that can be transferred by this DragSource. 578 * The application must be able to provide data to match each of these types when 579 * a successful drop has occurred. 580 * 581 * @param transferAgents a list of Transfer objects which define the types of data that can be 582 * dragged from this source 583 */ 584 public void setTransfer(Transfer[] transferAgents){ 585 if (targetList !is null) { 586 OS.gtk_target_list_unref(targetList); 587 targetList = null; 588 } 589 this.transferAgents = transferAgents; 590 if (transferAgents is null || transferAgents.length is 0) return; 591 592 GtkTargetEntry*[] targets; 593 for (int i = 0; i < transferAgents.length; i++) { 594 Transfer transfer = transferAgents[i]; 595 if (transfer !is null) { 596 int[] typeIds = transfer.getTypeIds(); 597 String[] typeNames = transfer.getTypeNames(); 598 for (int j = 0; j < typeIds.length; j++) { 599 GtkTargetEntry* entry = new GtkTargetEntry(); 600 String type = typeNames[j]; 601 entry.target = cast(char*)OS.g_malloc(type.length+1); 602 entry.target[ 0 .. type.length ] = type[]; 603 entry.target[ type.length ] = '\0'; 604 entry.info = typeIds[j]; 605 GtkTargetEntry*[] newTargets = new GtkTargetEntry*[targets.length + 1]; 606 SimpleType!(GtkTargetEntry*).arraycopy(targets, 0, newTargets, 607 0, targets.length); 608 newTargets[targets.length] = entry; 609 targets = newTargets; 610 } 611 } 612 } 613 614 void* pTargets = OS.g_malloc(targets.length * GtkTargetEntry.sizeof); 615 for (int i = 0; i < targets.length; i++) { 616 OS.memmove(pTargets + i*GtkTargetEntry.sizeof, targets[i], GtkTargetEntry.sizeof); 617 } 618 targetList = OS.gtk_target_list_new(pTargets, cast(int)/*64bit*/targets.length); 619 620 for (int i = 0; i < targets.length; i++) { 621 OS.g_free(targets[i].target); 622 } 623 } 624 625 static GdkDrawable* createPixbuf(Image image) { 626 int w, h; 627 OS.gdk_drawable_get_size (image.pixmap, &w, &h); 628 auto colormap = OS.gdk_colormap_get_system (); 629 void* pixbuf; 630 bool hasMask = image.mask !is null && OS.gdk_drawable_get_depth (image.mask) is 1; 631 if (hasMask) { 632 pixbuf = OS.gdk_pixbuf_new (OS.GDK_COLORSPACE_RGB, true, 8, w, h); 633 if (pixbuf is null) SWT.error (SWT.ERROR_NO_HANDLES); 634 OS.gdk_pixbuf_get_from_drawable (pixbuf, image.pixmap, colormap, 0, 0, 0, 0, w , h); 635 auto maskPixbuf = OS.gdk_pixbuf_new(OS.GDK_COLORSPACE_RGB, false, 8, w , h ); 636 if (maskPixbuf is null) SWT.error (SWT.ERROR_NO_HANDLES); 637 OS.gdk_pixbuf_get_from_drawable(maskPixbuf, image.mask, null, 0, 0, 0, 0, w , h ); 638 ptrdiff_t stride = OS.gdk_pixbuf_get_rowstride(pixbuf); 639 auto pixels = OS.gdk_pixbuf_get_pixels(pixbuf); 640 byte[] line = new byte[stride]; 641 ptrdiff_t maskStride = OS.gdk_pixbuf_get_rowstride(maskPixbuf); 642 auto maskPixels = OS.gdk_pixbuf_get_pixels(maskPixbuf); 643 byte[] maskLine = new byte[maskStride]; 644 for (int y=0; y<h; y++) { 645 auto offset = pixels + (y * stride); 646 OS.memmove(line.ptr, offset, stride); 647 auto maskOffset = maskPixels + (y * maskStride); 648 OS.memmove(maskLine.ptr, maskOffset, maskStride); 649 for (int x=0; x<w; x++) { 650 if (maskLine[x * 3] is 0) { 651 line[x * 4 + 3] = 0; 652 } 653 } 654 OS.memmove(offset, line.ptr, stride); 655 } 656 OS.g_object_unref(maskPixbuf); 657 } else { 658 ImageData data = image.getImageData (); 659 bool hasAlpha = data.getTransparencyType () is SWT.TRANSPARENCY_ALPHA; 660 pixbuf = OS.gdk_pixbuf_new (OS.GDK_COLORSPACE_RGB, hasAlpha, 8, w , h ); 661 if (pixbuf is null) SWT.error (SWT.ERROR_NO_HANDLES); 662 OS.gdk_pixbuf_get_from_drawable (pixbuf, image.pixmap, colormap, 0, 0, 0, 0, w , h ); 663 if (hasAlpha) { 664 byte [] alpha = data.alphaData; 665 ptrdiff_t stride = OS.gdk_pixbuf_get_rowstride (pixbuf); 666 auto pixels = OS.gdk_pixbuf_get_pixels (pixbuf); 667 byte [] line = new byte [stride]; 668 for (int y = 0; y < h ; y++) { 669 auto offset = pixels + (y * stride); 670 OS.memmove (line.ptr, offset, stride); 671 for (int x = 0; x < w ; x++) { 672 line [x*4+3] = alpha [y*w +x]; 673 } 674 OS.memmove (offset, line.ptr, stride); 675 } 676 } 677 } 678 return cast(GdkDrawable*)pixbuf; 679 } 680 }