]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/dnd/DragSource.java
Merge branch 'bug-623' into release/1.43.0
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / dnd / DragSource.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2020 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Lars Vogel <Lars.Vogel@vogella.com> - Bug 497807
14  *     Paul Pazderski - Improved implementation of IDataObject for bug 549643, 549661 and 567422
15  *******************************************************************************/
16 package org.eclipse.swt.dnd;
17
18
19 import org.eclipse.swt.*;
20 import org.eclipse.swt.graphics.*;
21 import org.eclipse.swt.internal.*;
22 import org.eclipse.swt.internal.ole.win32.*;
23 import org.eclipse.swt.internal.win32.*;
24 import org.eclipse.swt.widgets.*;
25
26 /**
27  *
28  * <code>DragSource</code> defines the source object for a drag and drop transfer.
29  *
30  * <p>IMPORTANT: This class is <em>not</em> intended to be subclassed.</p>
31  *
32  * <p>A drag source is the object which originates a drag and drop operation. For the specified widget,
33  * it defines the type of data that is available for dragging and the set of operations that can
34  * be performed on that data.  The operations can be any bit-wise combination of DND.MOVE, DND.COPY or
35  * DND.LINK.  The type of data that can be transferred is specified by subclasses of Transfer such as
36  * TextTransfer or FileTransfer.  The type of data transferred can be a predefined system type or it
37  * can be a type defined by the application.  For instructions on how to define your own transfer type,
38  * refer to <code>ByteArrayTransfer</code>.</p>
39  *
40  * <p>You may have several DragSources in an application but you can only have one DragSource
41  * per Control.  Data dragged from this DragSource can be dropped on a site within this application
42  * or it can be dropped on another application such as an external Text editor.</p>
43  *
44  * <p>The application supplies the content of the data being transferred by implementing the
45  * <code>DragSourceListener</code> and associating it with the DragSource via DragSource#addDragListener.</p>
46  *
47  * <p>When a successful move operation occurs, the application is required to take the appropriate
48  * action to remove the data from its display and remove any associated operating system resources or
49  * internal references.  Typically in a move operation, the drop target makes a copy of the data
50  * and the drag source deletes the original.  However, sometimes copying the data can take a long
51  * time (such as copying a large file).  Therefore, on some platforms, the drop target may actually
52  * move the data in the operating system rather than make a copy.  This is usually only done in
53  * file transfers.  In this case, the drag source is informed in the DragEnd event that a
54  * DROP_TARGET_MOVE was performed.  It is the responsibility of the drag source at this point to clean
55  * up its displayed information.  No action needs to be taken on the operating system resources.</p>
56  *
57  * <p> The following example shows a Label widget that allows text to be dragged from it.</p>
58  *
59  * <pre><code>
60  *      // Enable a label as a Drag Source
61  *      Label label = new Label(shell, SWT.NONE);
62  *      // This example will allow text to be dragged
63  *      Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
64  *      // This example will allow the text to be copied or moved to the drop target
65  *      int operations = DND.DROP_MOVE | DND.DROP_COPY;
66  *
67  *      DragSource source = new DragSource(label, operations);
68  *      source.setTransfer(types);
69  *      source.addDragListener(new DragSourceListener() {
70  *              public void dragStart(DragSourceEvent e) {
71  *                      // Only start the drag if there is actually text in the
72  *                      // label - this text will be what is dropped on the target.
73  *                      if (label.getText().length() == 0) {
74  *                              event.doit = false;
75  *                      }
76  *              };
77  *              public void dragSetData(DragSourceEvent event) {
78  *                      // A drop has been performed, so provide the data of the
79  *                      // requested type.
80  *                      // (Checking the type of the requested data is only
81  *                      // necessary if the drag source supports more than
82  *                      // one data type but is shown here as an example).
83  *                      if (TextTransfer.getInstance().isSupportedType(event.dataType)){
84  *                              event.data = label.getText();
85  *                      }
86  *              }
87  *              public void dragFinished(DragSourceEvent event) {
88  *                      // A Move operation has been performed so remove the data
89  *                      // from the source
90  *                      if (event.detail == DND.DROP_MOVE)
91  *                              label.setText("");
92  *              }
93  *      });
94  * </code></pre>
95  *
96  *
97  * <dl>
98  *      <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd>
99  *      <dt><b>Events</b></dt> <dd>DND.DragStart, DND.DragSetData, DND.DragEnd</dd>
100  * </dl>
101  *
102  * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a>
103  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: DNDExample</a>
104  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
105  * @noextend This class is not intended to be subclassed by clients.
106  */
107 public class DragSource extends Widget {
108
109         // info for registering as a drag source
110         Control control;
111         Listener controlListener;
112         Transfer[] transferAgents = new Transfer[0];
113         DragSourceEffect dragEffect;
114         Composite topControl;
115         long hwndDrag;
116
117         // ole interfaces
118         COMIDropSource iDropSource;
119         COMIDataObject iDataObject;
120
121         //workaround - track the operation performed by the drop target for DragEnd event
122         int dataEffect = DND.DROP_NONE;
123
124         static final String DEFAULT_DRAG_SOURCE_EFFECT = "DEFAULT_DRAG_SOURCE_EFFECT"; //$NON-NLS-1$
125         static final int CFSTR_PERFORMEDDROPEFFECT  = Transfer.registerType("Performed DropEffect");     //$NON-NLS-1$
126         static final TCHAR WindowClass = new TCHAR (0, "#32770", true);
127
128         private class COMIDropSource extends COMObject {
129
130                 private long refCount = 0;
131
132                 /**
133                  * Create a new IDropSource COM object. Object is created with one active
134                  * reference. (see {@link #Release()})
135                  */
136                 public COMIDropSource() {
137                         super(new int[]{2, 0, 0, 2, 1});
138                         AddRef();
139                 }
140
141                 @Override
142                 public long method0(long[] args) {return QueryInterface(this, args[0], args[1]);}
143                 @Override
144                 public long method1(long[] args) {return AddRef();}
145                 @Override
146                 public long method2(long[] args) {return Release();}
147                 @Override
148                 public long method3(long[] args) {return QueryContinueDrag((int)args[0], (int)args[1]);}
149                 @Override
150                 public long method4(long[] args) {return GiveFeedback((int)args[0]);}
151
152                 public long AddRef() {
153                         refCount++;
154                         return refCount;
155                 }
156
157                 public long Release() {
158                         refCount--;
159                         if (refCount == 0) {
160                                 if (DragSource.this.iDropSource == this) {
161                                         DragSource.this.iDropSource = null;
162                                 }
163                                 this.dispose();
164                                 if (COM.FreeUnusedLibraries) {
165                                         COM.CoFreeUnusedLibraries();
166                                 }
167                         }
168                         return refCount;
169                 }
170         }
171
172         private class COMIDataObject extends COMObject {
173                 /*
174                  * A SWT application is used to provide the data to drag in an event callback
175                  * which is called while the DND operation is performed. However Windows expects
176                  * the data to passed around in an object whose lifetime is managed through
177                  * reference counting. The drop target can keep a reference on the IDataObject
178                  * and even try to query the data long after the DND operation is finished. One
179                  * such case is Windows Explorer when showing a Portal Device (see bug 549661).
180                  * SWT does two things to support this case:
181                  * 1. Implement reference counting as intended. I.e. do not force release the
182                  * object after DND is finished but trust that all involved applications are
183                  * able to count correctly and will release the object at some point.
184                  * 2. Cache the data which was last transfered/generated from the DragSource
185                  * to be able to send it again after the DND operation is finished.
186                  */
187
188                 private long refCount = 0;
189
190                 private final Transfer[] transferAgents;
191
192                 /**
193                  * The most recent data send in a GetData request (or GetDataHere if
194                  * implemented). Or from another perspective the data the application returned
195                  * in the most recent {@link DND#DragSetData} event.
196                  */
197                 private Object lastData = null;
198
199                 /**
200                  * Create a new IDataObject COM object. Objects are created with one active
201                  * reference. (see {@link #Release()})
202                  *
203                  * @param transferAgents should be the transfer agents which are set on
204                  *                       DragSource at the time this object is created.
205                  */
206                 public COMIDataObject(Transfer[] transferAgents) {
207                         super(new int[]{2, 0, 0, 2, 2, 1, 2, 3, 2, 4, 1, 1});
208                         AddRef();
209                         this.transferAgents = transferAgents;
210                 }
211
212                 @Override
213                 public long method0(long[] args) {return QueryInterface(this, args[0], args[1]);}
214                 @Override
215                 public long method1(long[] args) {return AddRef();}
216                 @Override
217                 public long method2(long[] args) {return Release();}
218                 @Override
219                 public long method3(long[] args) {return GetData(args[0], args[1]);}
220                 // method4 GetDataHere - not implemented
221                 @Override
222                 public long method5(long[] args) {return QueryGetData(transferAgents, args[0]);}
223                 // method6 GetCanonicalFormatEtc - not implemented
224                 @Override
225                 public long method7(long[] args) {return SetData(args[0], args[1], (int)args[2]);}
226                 @Override
227                 public long method8(long[] args) {return EnumFormatEtc(transferAgents, (int)args[0], args[1]);}
228                 // method9 DAdvise - not implemented
229                 // method10 DUnadvise - not implemented
230                 // method11 EnumDAdvise - not implemented
231
232                 public long AddRef() {
233                         refCount++;
234                         return refCount;
235                 }
236
237                 public long Release() {
238                         refCount--;
239                         if (refCount == 0) {
240                                 if (DragSource.this.iDataObject == this) {
241                                         DragSource.this.iDataObject = null;
242                                 }
243                                 this.dispose();
244                                 if (COM.FreeUnusedLibraries) {
245                                         COM.CoFreeUnusedLibraries();
246                                 }
247                         }
248                         return refCount;
249                 }
250
251                 /**
252                  * Check if this IDataObject is currently used in a DND operation.
253                  *
254                  * @return <code>true</true> if this object currently used for DND
255                  */
256                 private boolean isActive() {
257                         return DragSource.this.iDataObject == this;
258                 }
259
260                 private int GetData(long pFormatetc, long pmedium) {
261                         /* Called by a data consumer to obtain data from a source data object.
262                            The GetData method renders the data described in the specified FORMATETC
263                            structure and transfers it through the specified STGMEDIUM structure.
264                            The caller then assumes responsibility for releasing the STGMEDIUM structure.
265                         */
266                         if (pFormatetc == 0 || pmedium == 0) return COM.E_INVALIDARG;
267
268                         if (QueryGetData(transferAgents, pFormatetc) != COM.S_OK) return COM.DV_E_FORMATETC;
269
270                         TransferData transferData = new TransferData();
271                         transferData.formatetc = new FORMATETC();
272                         COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof);
273                         transferData.type = transferData.formatetc.cfFormat;
274                         transferData.stgmedium = new STGMEDIUM();
275                         transferData.result = COM.E_FAIL;
276
277                         final Object data;
278                         if (isActive()) {
279                                 DNDEvent event = new DNDEvent();
280                                 event.widget = DragSource.this;
281                                 event.time = OS.GetMessageTime();
282                                 event.dataType = transferData;
283                                 notifyListeners(DND.DragSetData,event);
284
285                                 if (!event.doit) return COM.E_FAIL;
286
287                                 lastData = event.data;
288                                 data = event.data;
289                         } else {
290                                 if (lastData == null) {
291                                         return COM.E_FAIL;
292                                 }
293                                 data = lastData;
294                         }
295
296                         // get matching transfer agent to perform conversion
297                         Transfer transfer = null;
298                         for (Transfer transferAgent : transferAgents) {
299                                 if (transferAgent != null && transferAgent.isSupportedType(transferData)){
300                                         transfer = transferAgent;
301                                         break;
302                                 }
303                         }
304
305                         if (transfer == null) return COM.DV_E_FORMATETC;
306                         transfer.javaToNative(data, transferData);
307                         if (transferData.result != COM.S_OK) return transferData.result;
308                         COM.MoveMemory(pmedium, transferData.stgmedium, STGMEDIUM.sizeof);
309                         return transferData.result;
310                 }
311
312                 private int SetData(long pFormatetc, long pmedium, int fRelease) {
313                         if (pFormatetc == 0 || pmedium == 0) return COM.E_INVALIDARG;
314                         FORMATETC formatetc = new FORMATETC();
315                         COM.MoveMemory(formatetc, pFormatetc, FORMATETC.sizeof);
316                         if (formatetc.cfFormat == CFSTR_PERFORMEDDROPEFFECT && formatetc.tymed == COM.TYMED_HGLOBAL) {
317                                 STGMEDIUM stgmedium = new STGMEDIUM();
318                                 COM.MoveMemory(stgmedium, pmedium,STGMEDIUM.sizeof);
319                                 //TODO - this should be GlobalLock()
320                                 long[] ptrEffect = new long[1];
321                                 OS.MoveMemory(ptrEffect, stgmedium.unionField, C.PTR_SIZEOF);
322                                 int[] effect = new int[1];
323                                 OS.MoveMemory(effect, ptrEffect[0], 4);
324                                 if (isActive()) {
325                                         dataEffect = osToOp(effect[0]);
326                                 }
327                         }
328                         if (fRelease == 1) {
329                                 COM.ReleaseStgMedium(pmedium);
330                         }
331                         return COM.S_OK;
332                 }
333         }
334
335 /**
336  * Creates a new <code>DragSource</code> to handle dragging from the specified <code>Control</code>.
337  * Creating an instance of a DragSource may cause system resources to be allocated depending on the platform.
338  * It is therefore mandatory that the DragSource instance be disposed when no longer required.
339  *
340  * @param control the <code>Control</code> that the user clicks on to initiate the drag
341  * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of
342  *                                      DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK
343  *
344  * @exception SWTException <ul>
345  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
346  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
347  * </ul>
348  * @exception SWTError <ul>
349  *    <li>ERROR_CANNOT_INIT_DRAG - unable to initiate drag source; this will occur if more than one
350  *        drag source is created for a control or if the operating system will not allow the creation
351  *        of the drag source</li>
352  * </ul>
353  *
354  * <p>NOTE: ERROR_CANNOT_INIT_DRAG should be an SWTException, since it is a
355  * recoverable error, but can not be changed due to backward compatibility.</p>
356  *
357  * @see Widget#dispose
358  * @see DragSource#checkSubclass
359  * @see DND#DROP_NONE
360  * @see DND#DROP_COPY
361  * @see DND#DROP_MOVE
362  * @see DND#DROP_LINK
363  */
364 public DragSource(Control control, int style) {
365         super(control, checkStyle(style));
366         this.control = control;
367         if (control.getData(DND.DRAG_SOURCE_KEY) != null) {
368                 DND.error(DND.ERROR_CANNOT_INIT_DRAG);
369         }
370         control.setData(DND.DRAG_SOURCE_KEY, this);
371
372         controlListener = event -> {
373                 if (event.type == SWT.Dispose) {
374                         if (!DragSource.this.isDisposed()) {
375                                 DragSource.this.dispose();
376                         }
377                 }
378                 if (event.type == SWT.DragDetect) {
379                         if (!DragSource.this.isDisposed()) {
380                                 DragSource.this.drag(event);
381                         }
382                 }
383         };
384         control.addListener(SWT.Dispose, controlListener);
385         control.addListener(SWT.DragDetect, controlListener);
386
387         this.addListener(SWT.Dispose, e -> DragSource.this.onDispose());
388
389         Object effect = control.getData(DEFAULT_DRAG_SOURCE_EFFECT);
390         if (effect instanceof DragSourceEffect) {
391                 dragEffect = (DragSourceEffect) effect;
392         } else if (control instanceof Tree) {
393                 dragEffect = new TreeDragSourceEffect((Tree) control);
394         } else if (control instanceof Table) {
395                 dragEffect = new TableDragSourceEffect((Table) control);
396         }
397 }
398
399 static int checkStyle(int style) {
400         if (style == SWT.NONE) return DND.DROP_MOVE;
401         return style;
402 }
403
404 /**
405  * Adds the listener to the collection of listeners who will
406  * be notified when a drag and drop operation is in progress, by sending
407  * it one of the messages defined in the <code>DragSourceListener</code>
408  * interface.
409  *
410  * <ul>
411  * <li><code>dragStart</code> is called when the user has begun the actions required to drag the widget.
412  * This event gives the application the chance to decide if a drag should be started.
413  * <li><code>dragSetData</code> is called when the data is required from the drag source.
414  * <li><code>dragFinished</code> is called when the drop has successfully completed (mouse up
415  * over a valid target) or has been terminated (such as hitting the ESC key). Perform cleanup
416  * such as removing data from the source side on a successful move operation.
417  * </ul>
418  *
419  * @param listener the listener which should be notified
420  *
421  * @exception IllegalArgumentException <ul>
422  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
423  * </ul>
424  * @exception SWTException <ul>
425  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
426  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
427  * </ul>
428  *
429  * @see DragSourceListener
430  * @see #getDragListeners
431  * @see #removeDragListener
432  * @see DragSourceEvent
433  */
434 public void addDragListener(DragSourceListener listener) {
435         if (listener == null) DND.error(SWT.ERROR_NULL_ARGUMENT);
436         DNDListener typedListener = new DNDListener(listener);
437         typedListener.dndWidget = this;
438         addListener(DND.DragStart, typedListener);
439         addListener(DND.DragSetData, typedListener);
440         addListener(DND.DragEnd, typedListener);
441 }
442
443 private void createCOMInterfaces() {
444         releaseCOMInterfaces();
445         iDropSource = new COMIDropSource();
446         iDataObject = new COMIDataObject(transferAgents);
447 }
448
449 @Override
450 protected void checkSubclass() {
451         String name = getClass().getName();
452         String validName = DragSource.class.getName();
453         if (!validName.equals(name)) {
454                 DND.error(SWT.ERROR_INVALID_SUBCLASS);
455         }
456 }
457
458 private void releaseCOMInterfaces() {
459         if (iDropSource != null)
460                 iDropSource.Release();
461         iDropSource = null;
462
463         if (iDataObject != null)
464                 iDataObject.Release();
465         iDataObject = null;
466 }
467
468 private void drag(Event dragEvent) {
469         DNDEvent event = new DNDEvent();
470         event.widget = this;
471         event.x = dragEvent.x;
472         event.y = dragEvent.y;
473         event.time = OS.GetMessageTime();
474         event.doit = true;
475         notifyListeners(DND.DragStart,event);
476         if (!event.doit || transferAgents == null || transferAgents.length == 0 ) return;
477
478         int[] pdwEffect = new int[1];
479         int operations = opToOs(getStyle());
480         Display display = control.getDisplay();
481         String key = "org.eclipse.swt.internal.win32.runMessagesInIdle"; //$NON-NLS-1$
482         Object oldValue = display.getData(key);
483         display.setData(key, Boolean.TRUE);
484         ImageList imagelist = null;
485         Image image = event.image;
486         hwndDrag = 0;
487         topControl = null;
488         if (image != null) {
489                 imagelist = new ImageList(SWT.NONE);
490                 imagelist.add(image);
491                 topControl = control.getShell();
492                 /*
493                  * Bug in Windows. The image is inverted if the shell is RIGHT_TO_LEFT.
494                  * The fix is to create a transparent window that covers the shell client
495                  * area and use it during the drag to prevent the image from being inverted.
496                  * On XP if the shell is RTL, the image is not displayed.
497                  */
498                 int offsetX = event.offsetX;
499                 hwndDrag = topControl.handle;
500                 if ((topControl.getStyle() & SWT.RIGHT_TO_LEFT) != 0) {
501                         offsetX = image.getBoundsInPixels().width - offsetX;
502                         RECT rect = new RECT ();
503                         OS.GetClientRect (topControl.handle, rect);
504                         hwndDrag = OS.CreateWindowEx (
505                                 OS.WS_EX_TRANSPARENT | OS.WS_EX_NOINHERITLAYOUT,
506                                 WindowClass,
507                                 null,
508                                 OS.WS_CHILD | OS.WS_CLIPSIBLINGS,
509                                 0, 0,
510                                 rect.right - rect.left, rect.bottom - rect.top,
511                                 topControl.handle,
512                                 0,
513                                 OS.GetModuleHandle (null),
514                                 null);
515                         OS.ShowWindow (hwndDrag, OS.SW_SHOW);
516                 }
517                 OS.ImageList_BeginDrag(imagelist.getHandle(), 0, offsetX, event.offsetY);
518                 /*
519                 * Feature in Windows. When ImageList_DragEnter() is called,
520                 * it takes a snapshot of the screen  If a drag is started
521                 * when another window is in front, then the snapshot will
522                 * contain part of the other window, causing pixel corruption.
523                 * The fix is to force all paints to be delivered before
524                 * calling ImageList_DragEnter().
525                 */
526                 int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN;
527                 OS.RedrawWindow (topControl.handle, null, 0, flags);
528                 POINT pt = new POINT ();
529                 pt.x = DPIUtil.autoScaleUp(dragEvent.x);// To Pixels
530                 pt.y = DPIUtil.autoScaleUp(dragEvent.y);// To Pixels
531                 OS.MapWindowPoints (control.handle, 0, pt, 1);
532                 RECT rect = new RECT ();
533                 OS.GetWindowRect (hwndDrag, rect);
534                 OS.ImageList_DragEnter(hwndDrag, pt.x - rect.left, pt.y - rect.top);
535         }
536         String externalLoopKey = "org.eclipse.swt.internal.win32.externalEventLoop";
537         int result = COM.DRAGDROP_S_CANCEL;
538         try {
539                 createCOMInterfaces();
540                 display.setData(externalLoopKey, Boolean.TRUE);
541                 result = COM.DoDragDrop(iDataObject.getAddress(), iDropSource.getAddress(), operations, pdwEffect);
542         } finally {
543                 display.setData(externalLoopKey, Boolean.FALSE);
544                 // ensure that we don't leave transparent window around
545                 if (hwndDrag != 0) {
546                         OS.ImageList_DragLeave(hwndDrag);
547                         OS.ImageList_EndDrag();
548                         imagelist.dispose();
549                         if (hwndDrag != topControl.handle) OS.DestroyWindow(hwndDrag);
550                         hwndDrag = 0;
551                         topControl = null;
552                 }
553                 display.setData(key, oldValue);
554                 releaseCOMInterfaces();
555         }
556         int operation = osToOp(pdwEffect[0]);
557         if (dataEffect == DND.DROP_MOVE) {
558                 operation = (operation == DND.DROP_NONE || operation == DND.DROP_COPY) ? DND.DROP_TARGET_MOVE : DND.DROP_MOVE;
559         } else {
560                 if (dataEffect != DND.DROP_NONE) {
561                         operation = dataEffect;
562                 }
563         }
564
565         event = new DNDEvent();
566         event.widget = this;
567         event.time = OS.GetMessageTime();
568         event.doit = (result == COM.DRAGDROP_S_DROP);
569         event.detail = operation;
570         notifyListeners(DND.DragEnd,event);
571         dataEffect = DND.DROP_NONE;
572 }
573 /*
574  * EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc)
575  * Ownership of ppenumFormatetc transfers from callee to caller so reference count on ppenumFormatetc
576  * must be incremented before returning.  Caller is responsible for releasing ppenumFormatetc.
577  */
578 private static int EnumFormatEtc(Transfer[] transferAgents, int dwDirection, long ppenumFormatetc) {
579         // only allow getting of data - SetData is not currently supported
580         if (dwDirection == COM.DATADIR_SET) return COM.E_NOTIMPL;
581
582         // what types have been registered?
583         TransferData[] allowedDataTypes = new TransferData[0];
584         for (Transfer transferAgent : transferAgents) {
585                 if (transferAgent != null) {
586                         TransferData[] formats = transferAgent.getSupportedTypes();
587                         TransferData[] newAllowedDataTypes = new TransferData[allowedDataTypes.length + formats.length];
588                         System.arraycopy(allowedDataTypes, 0, newAllowedDataTypes, 0, allowedDataTypes.length);
589                         System.arraycopy(formats, 0, newAllowedDataTypes, allowedDataTypes.length, formats.length);
590                         allowedDataTypes = newAllowedDataTypes;
591                 }
592         }
593
594         OleEnumFORMATETC enumFORMATETC = new OleEnumFORMATETC();
595         enumFORMATETC.AddRef();
596
597         FORMATETC[] formats = new FORMATETC[allowedDataTypes.length];
598         for (int i = 0; i < formats.length; i++){
599                 formats[i] = allowedDataTypes[i].formatetc;
600         }
601         enumFORMATETC.setFormats(formats);
602
603         OS.MoveMemory(ppenumFormatetc, new long[] {enumFORMATETC.getAddress()}, C.PTR_SIZEOF);
604         return COM.S_OK;
605 }
606 /**
607  * Returns the Control which is registered for this DragSource.  This is the control that the
608  * user clicks in to initiate dragging.
609  *
610  * @return the Control which is registered for this DragSource
611  */
612 public Control getControl() {
613         return control;
614 }
615
616 /**
617  * Returns an array of listeners who will be notified when a drag and drop
618  * operation is in progress, by sending it one of the messages defined in
619  * the <code>DragSourceListener</code> interface.
620  *
621  * @return the listeners who will be notified when a drag and drop
622  * operation is in progress
623  *
624  * @exception SWTException <ul>
625  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
626  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
627  * </ul>
628  *
629  * @see DragSourceListener
630  * @see #addDragListener
631  * @see #removeDragListener
632  * @see DragSourceEvent
633  *
634  * @since 3.4
635  */
636 public DragSourceListener[] getDragListeners() {
637         Listener[] listeners = getListeners(DND.DragStart);
638         int length = listeners.length;
639         DragSourceListener[] dragListeners = new DragSourceListener[length];
640         int count = 0;
641         for (int i = 0; i < length; i++) {
642                 Listener listener = listeners[i];
643                 if (listener instanceof DNDListener) {
644                         dragListeners[count] = (DragSourceListener) ((DNDListener) listener).getEventListener();
645                         count++;
646                 }
647         }
648         if (count == length) return dragListeners;
649         DragSourceListener[] result = new DragSourceListener[count];
650         System.arraycopy(dragListeners, 0, result, 0, count);
651         return result;
652 }
653
654 /**
655  * Returns the drag effect that is registered for this DragSource.  This drag
656  * effect will be used during a drag and drop operation.
657  *
658  * @return the drag effect that is registered for this DragSource
659  *
660  * @since 3.3
661  */
662 public DragSourceEffect getDragSourceEffect() {
663         return dragEffect;
664 }
665
666 /**
667  * Returns the list of data types that can be transferred by this DragSource.
668  *
669  * @return the list of data types that can be transferred by this DragSource
670  */
671 public Transfer[] getTransfer(){
672         return transferAgents;
673 }
674
675 private int GiveFeedback(int dwEffect) {
676         return COM.DRAGDROP_S_USEDEFAULTCURSORS;
677 }
678
679 private int QueryContinueDrag(int fEscapePressed, int grfKeyState) {
680         if (topControl != null && topControl.isDisposed()) return COM.DRAGDROP_S_CANCEL;
681         if (fEscapePressed != 0){
682                 if (hwndDrag != 0) OS.ImageList_DragLeave(hwndDrag);
683                 return COM.DRAGDROP_S_CANCEL;
684         }
685         /*
686         * Bug in Windows.  On some machines that do not have XBUTTONs,
687         * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set,
688         * causing mouse capture to become stuck.  The fix is to test
689         * for the extra buttons only when they exist.
690         */
691         int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON;
692 //      if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2;
693         if ((grfKeyState & mask) == 0) {
694                 if (hwndDrag != 0) OS.ImageList_DragLeave(hwndDrag);
695                 return COM.DRAGDROP_S_DROP;
696         }
697
698         if (hwndDrag != 0) {
699                 POINT pt = new POINT ();
700                 OS.GetCursorPos (pt);
701                 RECT rect = new RECT ();
702                 OS.GetWindowRect (hwndDrag, rect);
703                 OS.ImageList_DragMove (pt.x - rect.left, pt.y - rect.top);
704         }
705         return COM.S_OK;
706 }
707
708 private void onDispose() {
709         if (control == null) return;
710         releaseCOMInterfaces();
711         if (controlListener != null){
712                 control.removeListener(SWT.Dispose, controlListener);
713                 control.removeListener(SWT.DragDetect, controlListener);
714         }
715         controlListener = null;
716         control.setData(DND.DRAG_SOURCE_KEY, null);
717         control = null;
718         transferAgents = null;
719 }
720
721 private int opToOs(int operation) {
722         int osOperation = 0;
723         if ((operation & DND.DROP_COPY) != 0){
724                 osOperation |= COM.DROPEFFECT_COPY;
725         }
726         if ((operation & DND.DROP_LINK) != 0) {
727                 osOperation |= COM.DROPEFFECT_LINK;
728         }
729         if ((operation & DND.DROP_MOVE) != 0) {
730                 osOperation |= COM.DROPEFFECT_MOVE;
731         }
732         return osOperation;
733 }
734
735 private int osToOp(int osOperation){
736         int operation = 0;
737         if ((osOperation & COM.DROPEFFECT_COPY) != 0){
738                 operation |= DND.DROP_COPY;
739         }
740         if ((osOperation & COM.DROPEFFECT_LINK) != 0) {
741                 operation |= DND.DROP_LINK;
742         }
743         if ((osOperation & COM.DROPEFFECT_MOVE) != 0) {
744                 operation |= DND.DROP_MOVE;
745         }
746         return operation;
747 }
748
749 private static int QueryGetData(Transfer[] transferAgents, long pFormatetc) {
750         if (transferAgents == null) return COM.E_FAIL;
751         TransferData transferData = new TransferData();
752         transferData.formatetc = new FORMATETC();
753         COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof);
754         transferData.type = transferData.formatetc.cfFormat;
755
756         // is this type supported by the transfer agent?
757         for (Transfer transferAgent : transferAgents) {
758                 if (transferAgent != null && transferAgent.isSupportedType(transferData))
759                         return COM.S_OK;
760         }
761
762         return COM.DV_E_FORMATETC;
763 }
764
765 /* QueryInterface([in] riid, [out] ppvObject)
766  * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject
767  * must be incremented before returning.  Caller is responsible for releasing ppvObject.
768  */
769 private static int QueryInterface(COMObject comObject, long riid, long ppvObject) {
770         if (riid == 0 || ppvObject == 0)
771                 return COM.E_INVALIDARG;
772         GUID guid = new GUID();
773         COM.MoveMemory(guid, riid, GUID.sizeof);
774
775         if (comObject != null && COM.IsEqualGUID(guid, COM.IIDIUnknown)
776                         || (COM.IsEqualGUID(guid, COM.IIDIDropSource) && (comObject instanceof COMIDropSource))
777                         || (COM.IsEqualGUID(guid, COM.IIDIDataObject) && (comObject instanceof COMIDataObject))) {
778                 OS.MoveMemory(ppvObject, new long[] {comObject.getAddress()}, C.PTR_SIZEOF);
779                 comObject.method1(null); // AddRef
780                 return COM.S_OK;
781         }
782
783         OS.MoveMemory(ppvObject, new long[] {0}, C.PTR_SIZEOF);
784         return COM.E_NOINTERFACE;
785 }
786
787 /**
788  * Removes the listener from the collection of listeners who will
789  * be notified when a drag and drop operation is in progress.
790  *
791  * @param listener the listener which should no longer be notified
792  *
793  * @exception IllegalArgumentException <ul>
794  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
795  * </ul>
796  * @exception SWTException <ul>
797  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
798  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
799  * </ul>
800  *
801  * @see DragSourceListener
802  * @see #addDragListener
803  * @see #getDragListeners
804  */
805 public void removeDragListener(DragSourceListener listener) {
806         if (listener == null) DND.error(SWT.ERROR_NULL_ARGUMENT);
807         removeListener(DND.DragStart, listener);
808         removeListener(DND.DragSetData, listener);
809         removeListener(DND.DragEnd, listener);
810 }
811
812 /**
813  * Specifies the drag effect for this DragSource.  This drag effect will be
814  * used during a drag and drop operation.
815  *
816  * @param effect the drag effect that is registered for this DragSource
817  *
818  * @since 3.3
819  */
820 public void setDragSourceEffect(DragSourceEffect effect) {
821         dragEffect = effect;
822 }
823
824 /**
825  * Specifies the list of data types that can be transferred by this DragSource.
826  * The application must be able to provide data to match each of these types when
827  * a successful drop has occurred.
828  *
829  * @param transferAgents a list of Transfer objects which define the types of data that can be
830  * dragged from this source
831  */
832 public void setTransfer(Transfer... transferAgents){
833         this.transferAgents = transferAgents;
834 }
835
836 }