root/dwt/dnd/DragSource.d

Revision 246:fd9c62a2998e, 28.8 kB (checked in by Frank Benoit <benoit@tionex.de>, 6 months ago)

Updater SWT 3.4M7 to 3.4

Line 
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 dwt.dnd.DragSource;
14
15
16 import dwt.DWT;
17 import dwt.DWTError;
18 import dwt.DWTException;
19 import dwt.graphics.Image;
20 import dwt.graphics.Point;
21 import dwt.internal.ImageList;
22 import dwt.internal.ole.win32.COM;
23 import dwt.internal.ole.win32.OLEIDL;
24 import dwt.internal.ole.win32.OBJIDL;
25 import dwt.internal.ole.win32.ifs;
26 import dwt.internal.ole.win32.extras;
27 import dwt.internal.win32.OS;
28 import dwt.widgets.Composite;
29 import dwt.widgets.Control;
30 import dwt.widgets.Display;
31 import dwt.widgets.Event;
32 import dwt.widgets.Listener;
33 import dwt.widgets.Table;
34 import dwt.widgets.Tree;
35 import dwt.widgets.Widget;
36
37 import dwt.dnd.DragSourceEffect;
38 import dwt.dnd.DragSourceListener;
39 import dwt.dnd.Transfer;
40 import dwt.dnd.TransferData;
41 import dwt.dnd.DND;
42 import dwt.dnd.DNDListener;
43 import dwt.dnd.DNDEvent;
44 import dwt.dnd.TreeDragSourceEffect;
45 import dwt.dnd.TableDragSourceEffect;
46 import dwt.dnd.OleEnumFORMATETC;
47
48 import dwt.dwthelper.utils;
49
50 /**
51  *
52  * <code>DragSource</code> defines the source object for a drag and drop transfer.
53  *
54  * <p>IMPORTANT: This class is <em>not</em> intended to be subclassed.</p>
55  *
56  * <p>A drag source is the object which originates a drag and drop operation. For the specified widget,
57  * it defines the type of data that is available for dragging and the set of operations that can
58  * be performed on that data.  The operations can be any bit-wise combination of DND.MOVE, DND.COPY or
59  * DND.LINK.  The type of data that can be transferred is specified by subclasses of Transfer such as
60  * TextTransfer or FileTransfer.  The type of data transferred can be a predefined system type or it
61  * can be a type defined by the application.  For instructions on how to define your own transfer type,
62  * refer to <code>ByteArrayTransfer</code>.</p>
63  *
64  * <p>You may have several DragSources in an application but you can only have one DragSource
65  * per Control.  Data dragged from this DragSource can be dropped on a site within this application
66  * or it can be dropped on another application such as an external Text editor.</p>
67  *
68  * <p>The application supplies the content of the data being transferred by implementing the
69  * <code>DragSourceListener</code> and associating it with the DragSource via DragSource#addDragListener.</p>
70  *
71  * <p>When a successful move operation occurs, the application is required to take the appropriate
72  * action to remove the data from its display and remove any associated operating system resources or
73  * internal references.  Typically in a move operation, the drop target makes a copy of the data
74  * and the drag source deletes the original.  However, sometimes copying the data can take a long
75  * time (such as copying a large file).  Therefore, on some platforms, the drop target may actually
76  * move the data in the operating system rather than make a copy.  This is usually only done in
77  * file transfers.  In this case, the drag source is informed in the DragEnd event that a
78  * DROP_TARGET_MOVE was performed.  It is the responsibility of the drag source at this point to clean
79  * up its displayed information.  No action needs to be taken on the operating system resources.</p>
80  *
81  * <p> The following example shows a Label widget that allows text to be dragged from it.</p>
82  *
83  * <code><pre>
84  *  // Enable a label as a Drag Source
85  *  Label label = new Label(shell, DWT.NONE);
86  *  // This example will allow text to be dragged
87  *  Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
88  *  // This example will allow the text to be copied or moved to the drop target
89  *  int operations = DND.DROP_MOVE | DND.DROP_COPY;
90  *
91  *  DragSource source = new DragSource(label, operations);
92  *  source.setTransfer(types);
93  *  source.addDragListener(new DragSourceListener() {
94  *      public void dragStart(DragSourceEvent e) {
95  *          // Only start the drag if there is actually text in the
96  *          // label - this text will be what is dropped on the target.
97  *          if (label.getText().length() is 0) {
98  *              event.doit = false;
99  *          }
100  *      };
101  *      public void dragSetData(DragSourceEvent event) {
102  *          // A drop has been performed, so provide the data of the
103  *          // requested type.
104  *          // (Checking the type of the requested data is only
105  *          // necessary if the drag source supports more than
106  *          // one data type but is shown here as an example).
107  *          if (TextTransfer.getInstance().isSupportedType(event.dataType)){
108  *              event.data = label.getText();
109  *          }
110  *      }
111  *      public void dragFinished(DragSourceEvent event) {
112  *          // A Move operation has been performed so remove the data
113  *          // from the source
114  *          if (event.detail is DND.DROP_MOVE)
115  *              label.setText("");
116  *      }
117  *  });
118  * </pre></code>
119  *
120  *
121  * <dl>
122  *  <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd>
123  *  <dt><b>Events</b></dt> <dd>DND.DragStart, DND.DragSetData, DND.DragEnd</dd>
124  * </dl>
125  *
126  * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a>
127  * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: DNDExample</a>
128  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
129  */
130 public class DragSource : Widget {
131
132     // info for registering as a drag source
133     Control control;
134     Listener controlListener;
135     Transfer[] transferAgents;
136     DragSourceEffect dragEffect;
137     Composite topControl;
138     HWND hwndDrag;
139
140     // ole interfaces
141     _IDropSourceImpl iDropSource;
142     _IDataObjectImpl iDataObject;
143     int refCount;
144
145     //workaround - track the operation performed by the drop target for DragEnd event
146     int dataEffect = DND.DROP_NONE;
147
148     static const String DEFAULT_DRAG_SOURCE_EFFECT = "DEFAULT_DRAG_SOURCE_EFFECT"; //$NON-NLS-1$
149     static const int CFSTR_PERFORMEDDROPEFFECT;
150     static final TCHAR[] WindowClass = "#32770\0";
151     static this(){
152         CFSTR_PERFORMEDDROPEFFECT  = Transfer.registerType("Performed DropEffect");     //$NON-NLS-1$
153     }
154 /**
155  * Creates a new <code>DragSource</code> to handle dragging from the specified <code>Control</code>.
156  * Creating an instance of a DragSource may cause system resources to be allocated depending on the platform.
157  * It is therefore mandatory that the DragSource instance be disposed when no longer required.
158  *
159  * @param control the <code>Control</code> that the user clicks on to initiate the drag
160  * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of
161  *                  DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK
162  *
163  * @exception DWTException <ul>
164  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
165  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
166  * </ul>
167  * @exception DWTError <ul>
168  *    <li>ERROR_CANNOT_INIT_DRAG - unable to initiate drag source; this will occur if more than one
169  *        drag source is created for a control or if the operating system will not allow the creation
170  *        of the drag source</li>
171  * </ul>
172  *
173  * <p>NOTE: ERROR_CANNOT_INIT_DRAG should be an DWTException, since it is a
174  * recoverable error, but can not be changed due to backward compatibility.</p>
175  *
176  * @see Widget#dispose
177  * @see DragSource#checkSubclass
178  * @see DND#DROP_NONE
179  * @see DND#DROP_COPY
180  * @see DND#DROP_MOVE
181  * @see DND#DROP_LINK
182  */
183 public this(Control control, int style) {
184     super(control, checkStyle(style));
185     this.control = control;
186     if (control.getData(DND.DRAG_SOURCE_KEY) !is null) {
187         DND.error(DND.ERROR_CANNOT_INIT_DRAG);
188     }
189     control.setData(DND.DRAG_SOURCE_KEY, this);
190     createCOMInterfaces();
191     this.AddRef();
192
193     controlListener = new class() Listener {
194         public void handleEvent(Event event) {
195             if (event.type is DWT.Dispose) {
196                 if (!this.outer.isDisposed()) {
197                     this.outer.dispose();
198                 }
199             }
200             if (event.type is DWT.DragDetect) {
201                 if (!this.outer.isDisposed()) {
202                     this.outer.drag(event);
203                 }
204             }
205         }
206     };
207     control.addListener(DWT.Dispose, controlListener);
208     control.addListener(DWT.DragDetect, controlListener);
209
210     this.addListener(DWT.Dispose, new class() Listener {
211         public void handleEvent(Event e) {
212             this.outer.onDispose();
213         }
214     });
215
216     Object effect = control.getData(DEFAULT_DRAG_SOURCE_EFFECT);
217     if ( auto dse = cast(DragSourceEffect)effect ) {
218         dragEffect = dse;
219     } else if ( auto tree = cast(Tree)control ) {
220         dragEffect = new TreeDragSourceEffect(tree);
221     } else if ( auto table = cast(Table)control ) {
222         dragEffect = new TableDragSourceEffect(table);
223     }
224 }
225
226 static int checkStyle(int style) {
227     if (style is DWT.NONE) return DND.DROP_MOVE;
228     return style;
229 }
230
231 /**
232  * Adds the listener to the collection of listeners who will
233  * be notified when a drag and drop operation is in progress, by sending
234  * it one of the messages defined in the <code>DragSourceListener</code>
235  * interface.
236  *
237  * <p><ul>
238  * <li><code>dragStart</code> is called when the user has begun the actions required to drag the widget.
239  * This event gives the application the chance to decide if a drag should be started.
240  * <li><code>dragSetData</code> is called when the data is required from the drag source.
241  * <li><code>dragFinished</code> is called when the drop has successfully completed (mouse up
242  * over a valid target) or has been terminated (such as hitting the ESC key). Perform cleanup
243  * such as removing data from the source side on a successful move operation.
244  * </ul></p>
245  *
246  * @param listener the listener which should be notified
247  *
248  * @exception IllegalArgumentException <ul>
249  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
250  * </ul>
251  * @exception DWTException <ul>
252  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
253  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
254  * </ul>
255  *
256  * @see DragSourceListener
257  * @see #getDragListeners
258  * @see #removeDragListener
259  * @see DragSourceEvent
260  */
261 public void addDragListener(DragSourceListener listener) {
262     if (listener is null) DND.error(DWT.ERROR_NULL_ARGUMENT);
263     DNDListener typedListener = new DNDListener(listener);
264     typedListener.dndWidget = this;
265     addListener(DND.DragStart, typedListener);
266     addListener(DND.DragSetData, typedListener);
267     addListener(DND.DragEnd, typedListener);
268 }
269
270 private int AddRef() {
271     refCount++;
272     return refCount;
273 }
274
275 private void createCOMInterfaces() {
276     // register each of the interfaces that this object implements
277     iDropSource = new _IDropSourceImpl(this);
278     iDataObject = new _IDataObjectImpl(this);
279 }
280
281 protected void checkSubclass() {
282     String name = this.classinfo.name;
283     String validName = DragSource.classinfo.name;
284     if (validName!=/*eq*/name) {
285         DND.error(DWT.ERROR_INVALID_SUBCLASS);
286     }
287 }
288
289 private void disposeCOMInterfaces() {
290     iDropSource = null;
291     iDataObject = null;
292 }
293
294 private void drag(Event dragEvent) {
295     DNDEvent event = new DNDEvent();
296     event.widget = this;
297     event.x = dragEvent.x;
298     event.y = dragEvent.y;
299     event.time = OS.GetMessageTime();
300     event.doit = true;
301     notifyListeners(DND.DragStart,event);
302     if (!event.doit || transferAgents is null || transferAgents.length is 0 ) return;
303
304     uint[1] pdwEffect;
305     int operations = opToOs(getStyle());
306     Display display = control.getDisplay();
307     String key = "dwt.internal.win32.runMessagesInIdle"; //$NON-NLS-1$
308     Object oldValue = display.getData(key);
309     display.setData(key, new Boolean(true));
310     ImageList imagelist = null;
311     Image image = event.image;
312     hwndDrag = null;
313     topControl = null;
314     if (image !is null) {
315         imagelist = new ImageList(DWT.NONE);
316         imagelist.add(image);
317         topControl = control.getShell();
318         /*
319          * Bug in Windows. The image is inverted if the shell is RIGHT_TO_LEFT.
320          * The fix is to create a transparent window that covers the shell client
321          * area and use it during the drag to prevent the image from being inverted.
322          * On XP if the shell is RTL, the image is not displayed.
323          */
324         int offset = event.x - dragEvent.x;
325         hwndDrag = topControl.handle;
326         if ((topControl.getStyle() & DWT.RIGHT_TO_LEFT) !is 0) {
327             offset = image.getBounds().width - offset;
328             RECT rect;
329             OS.GetClientRect (topControl.handle, &rect);
330             hwndDrag = OS.CreateWindowEx (
331                 OS.WS_EX_TRANSPARENT | OS.WS_EX_NOINHERITLAYOUT,
332                 WindowClass.ptr,
333                 null,
334                 OS.WS_CHILD | OS.WS_CLIPSIBLINGS,
335                 0, 0,
336                 rect.right - rect.left, rect.bottom - rect.top,
337                 topControl.handle,
338                 null,
339                 OS.GetModuleHandle (null),
340                 null);
341             OS.ShowWindow (hwndDrag, OS.SW_SHOW);
342         }
343         OS.ImageList_BeginDrag(imagelist.getHandle(), 0, offset, event.y - dragEvent.y);
344         /*
345         * Feature in Windows. When ImageList_DragEnter() is called,
346         * it takes a snapshot of the screen  If a drag is started
347         * when another window is in front, then the snapshot will
348         * contain part of the other window, causing pixel corruption.
349         * The fix is to force all paints to be delivered before
350         * calling ImageList_DragEnter().
351         */
352         if (OS.IsWinCE) {
353             OS.UpdateWindow (topControl.handle);
354         } else {
355             int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN;
356             OS.RedrawWindow (topControl.handle, null, null, flags);
357         }
358         POINT pt;
359         pt.x = dragEvent.x;
360         pt.y = dragEvent.y;
361         OS.MapWindowPoints (control.handle, null, &pt, 1);
362         RECT rect;
363         OS.GetWindowRect (hwndDrag, &rect);
364         OS.ImageList_DragEnter(hwndDrag, pt.x - rect.left, pt.y - rect.top);
365     }
366     int result = COM.DRAGDROP_S_CANCEL;
367     try {
368         result = COM.DoDragDrop(iDataObject, iDropSource, operations, pdwEffect.ptr);
369     } finally {
370         // ensure that we don't leave transparent window around
371         if (hwndDrag !is null) {
372             OS.ImageList_DragLeave(hwndDrag);
373             OS.ImageList_EndDrag();
374             imagelist.dispose();
375             if (hwndDrag !is topControl.handle) OS.DestroyWindow(hwndDrag);
376             hwndDrag = null;
377             topControl = null;
378         }
379         display.setData(key, oldValue);
380     }
381     int operation = osToOp(pdwEffect[0]);
382     if (dataEffect is DND.DROP_MOVE) {
383         operation = (operation is DND.DROP_NONE || operation is DND.DROP_COPY) ? DND.DROP_TARGET_MOVE : DND.DROP_MOVE;
384     } else {
385         if (dataEffect !is DND.DROP_NONE) {
386             operation = dataEffect;
387         }
388     }
389     event = new DNDEvent();
390     event.widget = this;
391     event.time = OS.GetMessageTime();
392     event.doit = (result is COM.DRAGDROP_S_DROP);
393     event.detail = operation;
394     notifyListeners(DND.DragEnd,event);
395     dataEffect = DND.DROP_NONE;
396 }
397 /*
398  * EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc)
399  * Ownership of ppenumFormatetc transfers from callee to caller so reference count on ppenumFormatetc
400  * must be incremented before returning.  Caller is responsible for releasing ppenumFormatetc.
401  */
402 private int EnumFormatEtc(int dwDirection, IEnumFORMATETC* ppenumFormatetc) {
403     // only allow getting of data - SetData is not currently supported
404     if (dwDirection is COM.DATADIR_SET) return COM.E_NOTIMPL;
405
406     // what types have been registered?
407     TransferData[] allowedDataTypes = new TransferData[0];
408     for (int i = 0; i < transferAgents.length; i++){
409         Transfer transferAgent = transferAgents[i];
410         if (transferAgent !is null) {
411             TransferData[] formats = transferAgent.getSupportedTypes();
412             TransferData[] newAllowedDataTypes = new TransferData[allowedDataTypes.length + formats.length];
413             System.arraycopy(allowedDataTypes, 0, newAllowedDataTypes, 0, allowedDataTypes.length);
414             System.arraycopy(formats, 0, newAllowedDataTypes, allowedDataTypes.length, formats.length);
415             allowedDataTypes = newAllowedDataTypes;
416         }
417     }
418
419     OleEnumFORMATETC enumFORMATETC = new OleEnumFORMATETC();
420     enumFORMATETC.AddRef();
421
422     FORMATETC*[] formats = new FORMATETC*[allowedDataTypes.length];
423     for (int i = 0; i < formats.length; i++){
424         formats[i] = allowedDataTypes[i].formatetc;
425     }
426     enumFORMATETC.setFormats(formats);
427
428     *ppenumFormatetc = enumFORMATETC.getAddress();
429     return COM.S_OK;
430 }
431 /**
432  * Returns the Control which is registered for this DragSource.  This is the control that the
433  * user clicks in to initiate dragging.
434  *
435  * @return the Control which is registered for this DragSource
436  */
437 public Control getControl() {
438     return control;
439 }
440
441 .LRESULT GetData(FORMATETC *pFormatetc, STGMEDIUM *pmedium) {
442     /* Called by a data consumer to obtain data from a source data object.
443        The GetData method renders the data described in the specified FORMATETC
444        structure and transfers it through the specified STGMEDIUM structure.
445        The caller then assumes responsibility for releasing the STGMEDIUM structure.
446     */
447     if (pFormatetc is null || pmedium is null) return COM.E_INVALIDARG;
448
449     if (QueryGetData(pFormatetc) !is COM.S_OK) return COM.DV_E_FORMATETC;
450
451     TransferData transferData = new TransferData();
452     transferData.formatetc = new FORMATETC();
453     COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof);
454     transferData.type = transferData.formatetc.cfFormat;
455     transferData.stgmedium = new STGMEDIUM();
456     transferData.result = COM.E_FAIL;
457
458     DNDEvent event = new DNDEvent();
459     event.widget = this;
460     event.time = OS.GetMessageTime();
461     event.dataType = transferData;
462     notifyListeners(DND.DragSetData,event);
463
464     // get matching transfer agent to perform conversion
465     Transfer transfer = null;
466     for (int i = 0; i < transferAgents.length; i++){
467         Transfer transferAgent = transferAgents[i];
468         if (transferAgent !is null && transferAgent.isSupportedType(transferData)){
469             transfer = transferAgent;
470             break;
471         }
472     }
473
474     if (transfer is null) return COM.DV_E_FORMATETC;
475     transfer.javaToNative(event.data, transferData);
476     if (transferData.result !is COM.S_OK) return transferData.result;
477     COM.MoveMemory(pmedium, transferData.stgmedium, STGMEDIUM.sizeof);
478     return transferData.result;
479 }
480
481 /**
482  * Returns an array of listeners who will be notified when a drag and drop
483  * operation is in progress, by sending it one of the messages defined in
484  * the <code>DragSourceListener</code> interface.
485  *
486  * @return the listeners who will be notified when a drag and drop
487  * operation is in progress
488  *
489  * @exception DWTException <ul>
490  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
491  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
492  * </ul>
493  *
494  * @see DragSourceListener
495  * @see #addDragListener
496  * @see #removeDragListener
497  * @see DragSourceEvent
498  *
499  * @since 3.4
500  */
501 public DragSourceListener[] getDragListeners() {
502     Listener[] listeners = getListeners(DND.DragStart);
503     int length = listeners.length;
504     DragSourceListener[] dragListeners = new DragSourceListener[length];
505     int count = 0;
506     for (int i = 0; i < length; i++) {
507         Listener listener = listeners[i];
508         if ( auto l = cast(DNDListener)listener ) {
509             dragListeners[count] = cast(DragSourceListener) (l.getEventListener());
510             count++;
511         }
512     }
513     if (count is length) return dragListeners;
514     DragSourceListener[] result = new DragSourceListener[count];
515     SimpleType!(DragSourceListener).arraycopy(dragListeners, 0, result, 0, count);
516     return result;
517 }
518
519 /**
520  * Returns the drag effect that is registered for this DragSource.  This drag
521  * effect will be used during a drag and drop operation.
522  *
523  * @return the drag effect that is registered for this DragSource
524  *
525  * @since 3.3
526  */
527 public DragSourceEffect getDragSourceEffect() {
528     return dragEffect;
529 }
530
531 /**
532  * Returns the list of data types that can be transferred by this DragSource.
533  *
534  * @return the list of data types that can be transferred by this DragSource
535  */
536 public Transfer[] getTransfer(){
537     return transferAgents;
538 }
539
540 package .LRESULT GiveFeedback(DWORD dwEffect) {
541     return COM.DRAGDROP_S_USEDEFAULTCURSORS;
542 }
543
544 package .LRESULT QueryContinueDrag(int fEscapePressed, DWORD grfKeyState) {
545     if (topControl !is null && topControl.isDisposed()) return COM.DRAGDROP_S_CANCEL;
546     if (fEscapePressed !is 0){
547         if (hwndDrag !is null) OS.ImageList_DragLeave(hwndDrag);
548         return COM.DRAGDROP_S_CANCEL;
549     }
550     /*
551     * Bug in Windows.  On some machines that do not have XBUTTONs,
552     * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set,
553     * causing mouse capture to become stuck.  The fix is to test
554     * for the extra buttons only when they exist.
555     */
556     int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON;
557 //  if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2;
558     if ((grfKeyState & mask) is 0) {
559         if (hwndDrag !is null) OS.ImageList_DragLeave(hwndDrag);
560         return COM.DRAGDROP_S_DROP;
561     }
562
563     if (hwndDrag !is null) {
564         POINT pt;
565         OS.GetCursorPos (&pt);
566         RECT rect;
567         OS.GetWindowRect (hwndDrag, &rect);
568         OS.ImageList_DragMove (pt.x - rect.left, pt.y - rect.top);
569     }
570     return COM.S_OK;
571 }
572
573 private void onDispose() {
574     if (control is null) return;
575     this.Release();
576     if (controlListener !is null){
577         control.removeListener(DWT.Dispose, controlListener);
578         control.removeListener(DWT.DragDetect, controlListener);
579     }
580     controlListener = null;
581     control.setData(DND.DRAG_SOURCE_KEY, null);
582     control = null;
583     transferAgents = null;
584 }
585
586 private int opToOs(int operation) {
587     int osOperation = 0;
588     if ((operation & DND.DROP_COPY) !is 0){
589         osOperation |= COM.DROPEFFECT_COPY;
590     }
591     if ((operation & DND.DROP_LINK) !is 0) {
592         osOperation |= COM.DROPEFFECT_LINK;
593     }
594     if ((operation & DND.DROP_MOVE) !is 0) {
595         osOperation |= COM.DROPEFFECT_MOVE;
596     }
597     return osOperation;
598 }
599
600 private int osToOp(int osOperation){
601     int operation = 0;
602     if ((osOperation &