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 }