]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/dnd/DropTarget.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 / DropTarget.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 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  *******************************************************************************/
14 package org.eclipse.swt.dnd;
15
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.internal.*;
18 import org.eclipse.swt.internal.ole.win32.*;
19 import org.eclipse.swt.internal.win32.*;
20 import org.eclipse.swt.widgets.*;
21
22 /**
23  *
24  * Class <code>DropTarget</code> defines the target object for a drag and drop transfer.
25  *
26  * <p>IMPORTANT: This class is <em>not</em> intended to be subclassed.</p>
27  *
28  * <p>This class identifies the <code>Control</code> over which the user must position the cursor
29  * in order to drop the data being transferred.  It also specifies what data types can be dropped on
30  * this control and what operations can be performed.  You may have several DropTragets in an
31  * application but there can only be a one to one mapping between a <code>Control</code> and a <code>DropTarget</code>.
32  * The DropTarget can receive data from within the same application or from other applications
33  * (such as text dragged from a text editor like Word).</p>
34  *
35  * <pre><code>
36  *      int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK;
37  *      Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
38  *      DropTarget target = new DropTarget(label, operations);
39  *      target.setTransfer(types);
40  * </code></pre>
41  *
42  * <p>The application is notified of data being dragged over this control and of when a drop occurs by
43  * implementing the interface <code>DropTargetListener</code> which uses the class
44  * <code>DropTargetEvent</code>.  The application can modify the type of drag being performed
45  * on this Control at any stage of the drag by modifying the <code>event.detail</code> field or the
46  * <code>event.currentDataType</code> field.  When the data is dropped, it is the responsibility of
47  * the application to copy this data for its own purposes.
48  *
49  * <pre><code>
50  *      target.addDropListener (new DropTargetListener() {
51  *              public void dragEnter(DropTargetEvent event) {};
52  *              public void dragOver(DropTargetEvent event) {};
53  *              public void dragLeave(DropTargetEvent event) {};
54  *              public void dragOperationChanged(DropTargetEvent event) {};
55  *              public void dropAccept(DropTargetEvent event) {}
56  *              public void drop(DropTargetEvent event) {
57  *                      // A drop has occurred, copy over the data
58  *                      if (event.data == null) { // no data to copy, indicate failure in event.detail
59  *                              event.detail = DND.DROP_NONE;
60  *                              return;
61  *                      }
62  *                      label.setText ((String) event.data); // data copied to label text
63  *              }
64  *      });
65  * </code></pre>
66  *
67  * <dl>
68  *      <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd>
69  *      <dt><b>Events</b></dt> <dd>DND.DragEnter, DND.DragLeave, DND.DragOver, DND.DragOperationChanged,
70  *                             DND.DropAccept, DND.Drop </dd>
71  * </dl>
72  *
73  * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a>
74  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: DNDExample</a>
75  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
76  * @noextend This class is not intended to be subclassed by clients.
77  */
78 public class DropTarget extends Widget {
79
80         Control control;
81         Listener controlListener;
82         Transfer[] transferAgents = new Transfer[0];
83         DropTargetEffect dropEffect;
84
85         // Track application selections
86         TransferData selectedDataType;
87         int selectedOperation;
88
89         // workaround - There is no event for "operation changed" so track operation based on key state
90         int keyOperation = -1;
91
92         // workaround - The dataobject address is only passed as an argument in drag enter and drop.
93         // To allow applications to query the data values during the drag over operations,
94         // maintain a reference to it.
95         IDataObject iDataObject;
96
97         // interfaces
98         COMObject iDropTarget;
99         int refCount;
100
101         static final String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$
102
103 /**
104  * Creates a new <code>DropTarget</code> to allow data to be dropped on the specified
105  * <code>Control</code>.
106  * Creating an instance of a DropTarget may cause system resources to be allocated
107  * depending on the platform.  It is therefore mandatory that the DropTarget instance
108  * be disposed when no longer required.
109  *
110  * @param control the <code>Control</code> over which the user positions the cursor to drop the data
111  * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of
112  *                 DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK
113  *
114  * @exception SWTException <ul>
115  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
116  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
117  * </ul>
118  * @exception SWTError <ul>
119  *    <li>ERROR_CANNOT_INIT_DROP - unable to initiate drop target; this will occur if more than one
120  *        drop target is created for a control or if the operating system will not allow the creation
121  *        of the drop target</li>
122  * </ul>
123  *
124  * <p>NOTE: ERROR_CANNOT_INIT_DROP should be an SWTException, since it is a
125  * recoverable error, but can not be changed due to backward compatibility.</p>
126  *
127  * @see Widget#dispose
128  * @see DropTarget#checkSubclass
129  * @see DND#DROP_NONE
130  * @see DND#DROP_COPY
131  * @see DND#DROP_MOVE
132  * @see DND#DROP_LINK
133  */
134 public DropTarget(Control control, int style) {
135         super (control, checkStyle(style));
136         this.control = control;
137         if (control.getData(DND.DROP_TARGET_KEY) != null) {
138                 DND.error(DND.ERROR_CANNOT_INIT_DROP);
139         }
140         control.setData(DND.DROP_TARGET_KEY, this);
141         createCOMInterfaces();
142         this.AddRef();
143
144         if (COM.CoLockObjectExternal(iDropTarget.getAddress(), true, true) != COM.S_OK)
145                 DND.error(DND.ERROR_CANNOT_INIT_DROP);
146         if (COM.RegisterDragDrop( control.handle, iDropTarget.getAddress()) != COM.S_OK)
147                 DND.error(DND.ERROR_CANNOT_INIT_DROP);
148
149         controlListener = event -> {
150                 if (!DropTarget.this.isDisposed()){
151                         DropTarget.this.dispose();
152                 }
153         };
154         control.addListener (SWT.Dispose, controlListener);
155
156         this.addListener(SWT.Dispose, event -> onDispose());
157
158         Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT);
159         if (effect instanceof DropTargetEffect) {
160                 dropEffect = (DropTargetEffect) effect;
161         } else if (control instanceof Table) {
162                 dropEffect = new TableDropTargetEffect((Table) control);
163         } else if (control instanceof Tree) {
164                 dropEffect = new TreeDropTargetEffect((Tree) control);
165         }
166 }
167
168 static int checkStyle (int style) {
169         if (style == SWT.NONE) return DND.DROP_MOVE;
170         return style;
171 }
172
173 /**
174  * Adds the listener to the collection of listeners who will
175  * be notified when a drag and drop operation is in progress, by sending
176  * it one of the messages defined in the <code>DropTargetListener</code>
177  * interface.
178  *
179  * <ul>
180  * <li><code>dragEnter</code> is called when the cursor has entered the drop target boundaries
181  * <li><code>dragLeave</code> is called when the cursor has left the drop target boundaries and just before
182  * the drop occurs or is cancelled.
183  * <li><code>dragOperationChanged</code> is called when the operation being performed has changed
184  * (usually due to the user changing the selected modifier key(s) while dragging)
185  * <li><code>dragOver</code> is called when the cursor is moving over the drop target
186  * <li><code>dropAccept</code> is called just before the drop is performed.  The drop target is given
187  * the chance to change the nature of the drop or veto the drop by setting the <code>event.detail</code> field
188  * <li><code>drop</code> is called when the data is being dropped
189  * </ul>
190  *
191  * @param listener the listener which should be notified
192  *
193  * @exception IllegalArgumentException <ul>
194  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
195  * </ul>
196  * @exception SWTException <ul>
197  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
198  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
199  * </ul>
200  *
201  * @see DropTargetListener
202  * @see #getDropListeners
203  * @see #removeDropListener
204  * @see DropTargetEvent
205  */
206 public void addDropListener(DropTargetListener listener) {
207         if (listener == null) DND.error (SWT.ERROR_NULL_ARGUMENT);
208         DNDListener typedListener = new DNDListener (listener);
209         typedListener.dndWidget = this;
210         addListener (DND.DragEnter, typedListener);
211         addListener (DND.DragLeave, typedListener);
212         addListener (DND.DragOver, typedListener);
213         addListener (DND.DragOperationChanged, typedListener);
214         addListener (DND.Drop, typedListener);
215         addListener (DND.DropAccept, typedListener);
216 }
217
218 int AddRef() {
219         refCount++;
220         return refCount;
221 }
222
223 @Override
224 protected void checkSubclass () {
225         String name = getClass().getName ();
226         String validName = DropTarget.class.getName();
227         if (!validName.equals(name)) {
228                 DND.error (SWT.ERROR_INVALID_SUBCLASS);
229         }
230 }
231
232 void createCOMInterfaces() {
233         // register each of the interfaces that this object implements
234         boolean is32 = C.PTR_SIZEOF == 4;
235         iDropTarget = new COMObject(new int[]{2, 0, 0, is32 ? 5 : 4, is32 ? 4 : 3, 0, is32 ? 5 : 4}){
236                 @Override
237                 public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
238                 @Override
239                 public long method1(long[] args) {return AddRef();}
240                 @Override
241                 public long method2(long[] args) {return Release();}
242                 @Override
243                 public long method3(long[] args) {
244                         if (args.length == 5) {
245                                 return DragEnter(args[0], (int)args[1], (int)args[2], (int)args[3], args[4]);
246                         } else {
247                                 return DragEnter_64(args[0], (int)args[1], args[2], args[3]);
248                         }
249                 }
250                 @Override
251                 public long method4(long[] args) {
252                         if (args.length == 4) {
253                                 return DragOver((int)args[0], (int)args[1], (int)args[2], args[3]);
254                         } else {
255                                 return DragOver_64((int)args[0], args[1], args[2]);
256                         }
257                 }
258                 @Override
259                 public long method5(long[] args) {return DragLeave();}
260                 @Override
261                 public long method6(long[] args) {
262                         if (args.length == 5) {
263                                 return Drop(args[0], (int)args[1], (int)args[2], (int)args[3], args[4]);
264                         } else {
265                                 return Drop_64(args[0], (int)args[1], args[2], args[3]);
266                         }
267                 }
268         };
269 }
270
271 void disposeCOMInterfaces() {
272         if (iDropTarget != null)
273                 iDropTarget.dispose();
274         iDropTarget = null;
275 }
276
277 int DragEnter_64(long pDataObject, int grfKeyState, long pt, long pdwEffect) {
278         POINT point = new POINT();
279         OS.MoveMemory(point, new long[]{pt}, 8);
280         return DragEnter(pDataObject, grfKeyState, point.x, point.y, pdwEffect);
281 }
282
283 int DragEnter(long pDataObject, int grfKeyState, int pt_x, int pt_y, long pdwEffect) {
284         pt_x = DPIUtil.autoScaleDown(pt_x);// To Points
285         pt_y = DPIUtil.autoScaleDown(pt_y);// To Points
286         selectedDataType = null;
287         selectedOperation = DND.DROP_NONE;
288         if (iDataObject != null) iDataObject.Release();
289         iDataObject = null;
290
291         DNDEvent event = new DNDEvent();
292         if (!setEventData(event, pDataObject, grfKeyState, pt_x, pt_y, pdwEffect)) {
293                 OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4);
294                 return COM.S_FALSE;
295         }
296
297         // Remember the iDataObject because it is not passed into the DragOver callback
298         iDataObject = new IDataObject(pDataObject);
299         iDataObject.AddRef();
300
301         int allowedOperations = event.operations;
302         TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length];
303         System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length);
304         notifyListeners(DND.DragEnter, event);
305         refresh();
306         if (event.detail == DND.DROP_DEFAULT) {
307                 event.detail = (allowedOperations & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE;
308         }
309
310         selectedDataType = null;
311         for (int i = 0; i < allowedDataTypes.length; i++) {
312                 if (TransferData.sameType(allowedDataTypes[i], event.dataType)){
313                         selectedDataType = allowedDataTypes[i];
314                         break;
315                 }
316         }
317
318         selectedOperation = DND.DROP_NONE;
319         if (selectedDataType != null && ((allowedOperations & event.detail) != 0)) {
320                 selectedOperation = event.detail;
321         }
322
323         OS.MoveMemory(pdwEffect, new int[] {opToOs(selectedOperation)}, 4);
324         return COM.S_OK;
325 }
326
327 int DragLeave() {
328         keyOperation = -1;
329
330         if (iDataObject == null) return COM.S_FALSE;
331
332         DNDEvent event = new DNDEvent();
333         event.widget = this;
334         event.time = OS.GetMessageTime();
335         event.detail = DND.DROP_NONE;
336         notifyListeners(DND.DragLeave, event);
337         refresh();
338
339         iDataObject.Release();
340         iDataObject = null;
341         return COM.S_OK;
342 }
343
344 int DragOver_64(int grfKeyState, long pt, long pdwEffect) {
345         POINT point = new POINT();
346         OS.MoveMemory(point, new long[]{pt}, 8);
347         return DragOver(grfKeyState, point.x, point.y, pdwEffect);
348 }
349
350 int DragOver(int grfKeyState, int pt_x, int pt_y, long pdwEffect) {
351         pt_x = DPIUtil.autoScaleDown(pt_x);// To Points
352         pt_y = DPIUtil.autoScaleDown(pt_y);// To Points
353         if (iDataObject == null) return COM.S_FALSE;
354         int oldKeyOperation = keyOperation;
355
356         DNDEvent event = new DNDEvent();
357         if (!setEventData(event, iDataObject.getAddress(), grfKeyState, pt_x, pt_y, pdwEffect)) {
358                 keyOperation = -1;
359                 OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4);
360                 return COM.S_FALSE;
361         }
362
363         int allowedOperations = event.operations;
364         TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length];
365         System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length);
366
367         if (keyOperation == oldKeyOperation) {
368                 event.type = DND.DragOver;
369                 event.dataType = selectedDataType;
370                 event.detail = selectedOperation;
371         } else {
372                 event.type = DND.DragOperationChanged;
373                 event.dataType = selectedDataType;
374         }
375         notifyListeners(event.type, event);
376         refresh();
377         if (event.detail == DND.DROP_DEFAULT) {
378                 event.detail = (allowedOperations & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE;
379         }
380
381         selectedDataType = null;
382         for (int i = 0; i < allowedDataTypes.length; i++) {
383                 if (TransferData.sameType(allowedDataTypes[i], event.dataType)){
384                         selectedDataType = allowedDataTypes[i];
385                         break;
386                 }
387         }
388
389         selectedOperation = DND.DROP_NONE;
390         if (selectedDataType != null && ((allowedOperations & event.detail) == event.detail)) {
391                 selectedOperation = event.detail;
392         }
393
394         OS.MoveMemory(pdwEffect, new int[] {opToOs(selectedOperation)}, 4);
395         return COM.S_OK;
396 }
397
398 int Drop_64(long pDataObject, int grfKeyState, long pt, long pdwEffect) {
399         POINT point = new POINT();
400         OS.MoveMemory(point, new long[]{pt}, 8);
401         return Drop(pDataObject, grfKeyState, point.x, point.y, pdwEffect);
402 }
403
404 int Drop(long pDataObject, int grfKeyState, int pt_x, int pt_y, long pdwEffect) {
405         try {
406                 pt_x = DPIUtil.autoScaleDown(pt_x);// To Points
407                 pt_y = DPIUtil.autoScaleDown(pt_y);// To Points
408                 DNDEvent event = new DNDEvent();
409                 event.widget = this;
410                 event.time = OS.GetMessageTime();
411                 if (dropEffect != null) {
412                         event.item = dropEffect.getItem(pt_x, pt_y);
413                 }
414                 event.detail = DND.DROP_NONE;
415                 notifyListeners(DND.DragLeave, event);
416                 refresh();
417
418                 event = new DNDEvent();
419                 if (!setEventData(event, pDataObject, grfKeyState, pt_x, pt_y, pdwEffect)) {
420                         keyOperation = -1;
421                         OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4);
422                         return COM.S_FALSE;
423                 }
424                 keyOperation = -1;
425                 int allowedOperations = event.operations;
426                 TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length];
427                 System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length);
428                 event.dataType = selectedDataType;
429                 event.detail = selectedOperation;
430                 notifyListeners(DND.DropAccept,event);
431                 refresh();
432
433                 selectedDataType = null;
434                 for (int i = 0; i < allowedDataTypes.length; i++) {
435                         if (TransferData.sameType(allowedDataTypes[i], event.dataType)){
436                                 selectedDataType = allowedDataTypes[i];
437                                 break;
438                         }
439                 }
440                 selectedOperation = DND.DROP_NONE;
441                 if (selectedDataType != null && (allowedOperations & event.detail) == event.detail) {
442                         selectedOperation = event.detail;
443                 }
444
445                 if (selectedOperation == DND.DROP_NONE){
446                         OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4);
447                         return COM.S_OK;
448                 }
449
450                 // Get Data in a Java format
451                 Object object = null;
452                 for (int i = 0; i < transferAgents.length; i++){
453                         Transfer transfer = transferAgents[i];
454                         if (transfer != null && transfer.isSupportedType(selectedDataType)){
455                                 object = transfer.nativeToJava(selectedDataType);
456                                 break;
457                         }
458                 }
459                 if (object == null){
460                         selectedOperation = DND.DROP_NONE;
461                 }
462
463                 event.detail = selectedOperation;
464                 event.dataType = selectedDataType;
465                 event.data = object;
466                 OS.ImageList_DragShowNolock(false);
467                 try {
468                         notifyListeners(DND.Drop,event);
469                 } finally {
470                         OS.ImageList_DragShowNolock(true);
471                 }
472                 refresh();
473                 selectedOperation = DND.DROP_NONE;
474                 if ((allowedOperations & event.detail) == event.detail) {
475                         selectedOperation = event.detail;
476                 }
477                 //notify source of action taken
478                 OS.MoveMemory(pdwEffect, new int[] {opToOs(selectedOperation)}, 4);
479                 return COM.S_OK;
480         } finally {
481                 if (iDataObject != null) {
482                         iDataObject.Release();
483                         iDataObject = null;
484                 }
485         }
486 }
487
488 /**
489  * Returns the Control which is registered for this DropTarget.  This is the control over which the
490  * user positions the cursor to drop the data.
491  *
492  * @return the Control which is registered for this DropTarget
493  */
494 public Control getControl () {
495         return control;
496 }
497
498 /**
499  * Returns an array of listeners who will be notified when a drag and drop
500  * operation is in progress, by sending it one of the messages defined in
501  * the <code>DropTargetListener</code> interface.
502  *
503  * @return the listeners who will be notified when a drag and drop
504  * operation is in progress
505  *
506  * @exception SWTException <ul>
507  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
508  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
509  * </ul>
510  *
511  * @see DropTargetListener
512  * @see #addDropListener
513  * @see #removeDropListener
514  * @see DropTargetEvent
515  *
516  * @since 3.4
517  */
518 public DropTargetListener[] getDropListeners() {
519         Listener[] listeners = getListeners(DND.DragEnter);
520         int length = listeners.length;
521         DropTargetListener[] dropListeners = new DropTargetListener[length];
522         int count = 0;
523         for (int i = 0; i < length; i++) {
524                 Listener listener = listeners[i];
525                 if (listener instanceof DNDListener) {
526                         dropListeners[count] = (DropTargetListener) ((DNDListener) listener).getEventListener();
527                         count++;
528                 }
529         }
530         if (count == length) return dropListeners;
531         DropTargetListener[] result = new DropTargetListener[count];
532         System.arraycopy(dropListeners, 0, result, 0, count);
533         return result;
534 }
535
536 /**
537  * Returns the drop effect for this DropTarget.  This drop effect will be
538  * used during a drag and drop to display the drag under effect on the
539  * target widget.
540  *
541  * @return the drop effect that is registered for this DropTarget
542  *
543  * @since 3.3
544  */
545 public DropTargetEffect getDropTargetEffect() {
546         return dropEffect;
547 }
548
549 int getOperationFromKeyState(int grfKeyState) {
550         boolean ctrl = (grfKeyState & OS.MK_CONTROL) != 0;
551         boolean shift = (grfKeyState & OS.MK_SHIFT) != 0;
552         boolean alt = (grfKeyState & OS.MK_ALT) != 0;
553         if (alt) {
554                 if (ctrl || shift) return DND.DROP_DEFAULT;
555                 return DND.DROP_LINK;
556         }
557         if (ctrl && shift) return DND.DROP_LINK;
558         if (ctrl)return DND.DROP_COPY;
559         if (shift)return DND.DROP_MOVE;
560         return DND.DROP_DEFAULT;
561 }
562
563 /**
564  * Returns a list of the data types that can be transferred to this DropTarget.
565  *
566  * @return a list of the data types that can be transferred to this DropTarget
567  */
568 public Transfer[] getTransfer() {
569         return transferAgents;
570 }
571
572 void onDispose () {
573         if (control == null) return;
574
575         COM.RevokeDragDrop(control.handle);
576
577         if (controlListener != null)
578                 control.removeListener(SWT.Dispose, controlListener);
579         controlListener = null;
580         control.setData(DND.DROP_TARGET_KEY, null);
581         transferAgents = null;
582         control = null;
583
584         COM.CoLockObjectExternal(iDropTarget.getAddress(), false, true);
585
586         this.Release();
587         if (iDataObject != null) {
588                 iDataObject.Release();
589         }
590         iDataObject = null;
591
592         if (COM.FreeUnusedLibraries) {
593                 COM.CoFreeUnusedLibraries();
594         }
595 }
596
597 int opToOs(int operation) {
598         int osOperation = 0;
599         if ((operation & DND.DROP_COPY) != 0){
600                 osOperation |= COM.DROPEFFECT_COPY;
601         }
602         if ((operation & DND.DROP_LINK) != 0) {
603                 osOperation |= COM.DROPEFFECT_LINK;
604         }
605         if ((operation & DND.DROP_MOVE) != 0) {
606                 osOperation |= COM.DROPEFFECT_MOVE;
607         }
608         return osOperation;
609 }
610
611 int osToOp(int osOperation){
612         int operation = 0;
613         if ((osOperation & COM.DROPEFFECT_COPY) != 0){
614                 operation |= DND.DROP_COPY;
615         }
616         if ((osOperation & COM.DROPEFFECT_LINK) != 0) {
617                 operation |= DND.DROP_LINK;
618         }
619         if ((osOperation & COM.DROPEFFECT_MOVE) != 0) {
620                 operation |= DND.DROP_MOVE;
621         }
622         return operation;
623 }
624
625 /* QueryInterface([in] iid, [out] ppvObject)
626  * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject
627  * must be incremented before returning.  Caller is responsible for releasing ppvObject.
628  */
629 int QueryInterface(long riid, long ppvObject) {
630
631         if (riid == 0 || ppvObject == 0)
632                 return COM.E_INVALIDARG;
633         GUID guid = new GUID();
634         COM.MoveMemory(guid, riid, GUID.sizeof);
635         if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDropTarget)) {
636                 OS.MoveMemory(ppvObject, new long[] {iDropTarget.getAddress()}, C.PTR_SIZEOF);
637                 AddRef();
638                 return COM.S_OK;
639         }
640
641         OS.MoveMemory(ppvObject, new long[] {0}, C.PTR_SIZEOF);
642         return COM.E_NOINTERFACE;
643 }
644
645 int Release() {
646         refCount--;
647
648         if (refCount == 0) {
649                 disposeCOMInterfaces();
650                 if (COM.FreeUnusedLibraries) {
651                         COM.CoFreeUnusedLibraries();
652                 }
653         }
654
655         return refCount;
656 }
657
658 void refresh() {
659         if (control == null || control.isDisposed()) return;
660         long handle = control.handle;
661         RECT lpRect = new RECT();
662         if (OS.GetUpdateRect(handle, lpRect, false)) {
663                 OS.ImageList_DragShowNolock(false);
664                 OS.RedrawWindow(handle, lpRect, 0, OS.RDW_UPDATENOW | OS.RDW_INVALIDATE);
665                 OS.ImageList_DragShowNolock(true);
666         }
667 }
668
669 /**
670  * Removes the listener from the collection of listeners who will
671  * be notified when a drag and drop operation is in progress.
672  *
673  * @param listener the listener which should no longer be notified
674  *
675  * @exception IllegalArgumentException <ul>
676  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
677  * </ul>
678  * @exception SWTException <ul>
679  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
680  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
681  * </ul>
682  *
683  * @see DropTargetListener
684  * @see #addDropListener
685  * @see #getDropListeners
686  */
687 public void removeDropListener(DropTargetListener listener) {
688         if (listener == null) DND.error (SWT.ERROR_NULL_ARGUMENT);
689         removeListener (DND.DragEnter, listener);
690         removeListener (DND.DragLeave, listener);
691         removeListener (DND.DragOver, listener);
692         removeListener (DND.DragOperationChanged, listener);
693         removeListener (DND.Drop, listener);
694         removeListener (DND.DropAccept, listener);
695 }
696
697 /**
698  * Specifies the drop effect for this DropTarget.  This drop effect will be
699  * used during a drag and drop to display the drag under effect on the
700  * target widget.
701  *
702  * @param effect the drop effect that is registered for this DropTarget
703  *
704  * @since 3.3
705  */
706 public void setDropTargetEffect(DropTargetEffect effect) {
707         dropEffect = effect;
708 }
709
710 boolean setEventData(DNDEvent event, long pDataObject, int grfKeyState, int pt_x, int pt_y, long pdwEffect) {
711         if (pDataObject == 0 || pdwEffect == 0) return false;
712
713         // get allowed operations
714         int style = getStyle();
715         int[] operations = new int[1];
716         OS.MoveMemory(operations, pdwEffect, 4);
717         operations[0] = osToOp(operations[0]) & style;
718         if (operations[0] == DND.DROP_NONE) return false;
719
720         // get current operation
721         int operation = getOperationFromKeyState(grfKeyState);
722         keyOperation = operation;
723         if (operation == DND.DROP_DEFAULT) {
724                 if ((style & DND.DROP_DEFAULT) == 0) {
725                         operation = (operations[0] & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE;
726                 }
727         } else {
728                 if ((operation & operations[0]) == 0) operation = DND.DROP_NONE;
729         }
730
731         // Get allowed transfer types
732         TransferData[] dataTypes = new TransferData[0];
733         IDataObject dataObject = new IDataObject(pDataObject);
734         dataObject.AddRef();
735         try {
736                 long[] address = new long[1];
737                 if (dataObject.EnumFormatEtc(COM.DATADIR_GET, address) != COM.S_OK) {
738                         return false;
739                 }
740                 IEnumFORMATETC enumFormatetc = new IEnumFORMATETC(address[0]);
741                 try {
742                         // Loop over enumerator and save any types that match what we are looking for
743                         long rgelt = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, FORMATETC.sizeof);
744                         try {
745                                 int[] pceltFetched = new int[1];
746                                 enumFormatetc.Reset();
747                                 while (enumFormatetc.Next(1, rgelt, pceltFetched) == COM.S_OK && pceltFetched[0] == 1) {
748                                         TransferData transferData = new TransferData();
749                                         transferData.formatetc = new FORMATETC();
750                                         COM.MoveMemory(transferData.formatetc, rgelt, FORMATETC.sizeof);
751                                         transferData.type = transferData.formatetc.cfFormat;
752                                         transferData.pIDataObject = pDataObject;
753                                         for (int i = 0; i < transferAgents.length; i++){
754                                                 Transfer transfer = transferAgents[i];
755                                                 if (transfer != null && transfer.isSupportedType(transferData)){
756                                                         TransferData[] newDataTypes = new TransferData[dataTypes.length + 1];
757                                                         System.arraycopy(dataTypes, 0, newDataTypes, 0, dataTypes.length);
758                                                         newDataTypes[dataTypes.length] = transferData;
759                                                         dataTypes = newDataTypes;
760                                                         break;
761                                                 }
762                                         }
763                                 }
764                         } finally {
765                                 OS.GlobalFree(rgelt);
766                         }
767                 } finally {
768                         enumFormatetc.Release();
769                 }
770         } finally {
771                 dataObject.Release();
772         }
773         if (dataTypes.length == 0) return false;
774
775         event.widget = this;
776         event.x = pt_x;
777         event.y = pt_y;
778         event.time = OS.GetMessageTime();
779         event.feedback = DND.FEEDBACK_SELECT;
780         event.dataTypes = dataTypes;
781         event.dataType = dataTypes[0];
782         if (dropEffect != null) {
783                 event.item = dropEffect.getItem(pt_x, pt_y);
784         }
785         event.operations = operations[0];
786         event.detail = operation;
787         return true;
788 }
789
790 /**
791  * Specifies the data types that can be transferred to this DropTarget.  If data is
792  * being dragged that does not match one of these types, the drop target will be notified of
793  * the drag and drop operation but the currentDataType will be null and the operation
794  * will be DND.NONE.
795  *
796  * @param transferAgents a list of Transfer objects which define the types of data that can be
797  *                                               dropped on this target
798  *
799  * @exception IllegalArgumentException <ul>
800  *    <li>ERROR_NULL_ARGUMENT - if transferAgents is null</li>
801  * </ul>
802  */
803 public void setTransfer(Transfer... transferAgents){
804         if (transferAgents == null) DND.error(SWT.ERROR_NULL_ARGUMENT);
805         this.transferAgents = transferAgents;
806 }
807 }