]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/dnd/Clipboard.java
Work around SWT 4.13 - 4.18 Win32 DnD bug 567422
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / dnd / Clipboard.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2017 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
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.internal.*;
19 import org.eclipse.swt.internal.ole.win32.*;
20 import org.eclipse.swt.internal.win32.*;
21 import org.eclipse.swt.widgets.*;
22
23 /**
24  * The <code>Clipboard</code> provides a mechanism for transferring data from one
25  * application to another or within an application.
26  *
27  * <p>IMPORTANT: This class is <em>not</em> intended to be subclassed.</p>
28  *
29  * @see <a href="http://www.eclipse.org/swt/snippets/#clipboard">Clipboard snippets</a>
30  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ClipboardExample</a>
31  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
32  * @noextend This class is not intended to be subclassed by clients.
33  */
34 public class Clipboard {
35
36         private static final int RETRY_LIMIT = 10;
37         private Display display;
38
39         // ole interfaces
40         private COMObject iDataObject;
41         private int refCount;
42         private Transfer[] transferAgents = new Transfer[0];
43         private Object[] data = new Object[0];
44         private int CFSTR_PREFERREDDROPEFFECT;
45
46 /**
47  * Constructs a new instance of this class.  Creating an instance of a Clipboard
48  * may cause system resources to be allocated depending on the platform.  It is therefore
49  * mandatory that the Clipboard instance be disposed when no longer required.
50  *
51  * @param display the display on which to allocate the clipboard
52  *
53  * @exception SWTException <ul>
54  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
55  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
56  * </ul>
57  *
58  * @see Clipboard#dispose
59  * @see Clipboard#checkSubclass
60  */
61 public Clipboard(Display display) {
62         checkSubclass ();
63         if (display == null) {
64                 display = Display.getCurrent();
65                 if (display == null) {
66                         display = Display.getDefault();
67                 }
68         }
69         if (display.getThread() != Thread.currentThread()) {
70                 DND.error(SWT.ERROR_THREAD_INVALID_ACCESS);
71         }
72         this.display = display;
73         TCHAR chFormatName = new TCHAR(0, "Preferred DropEffect", true); //$NON-NLS-1$
74         CFSTR_PREFERREDDROPEFFECT = OS.RegisterClipboardFormat(chFormatName);
75         createCOMInterfaces();
76         this.AddRef();
77 }
78
79 /**
80  * Checks that this class can be subclassed.
81  * <p>
82  * The SWT class library is intended to be subclassed
83  * only at specific, controlled points. This method enforces this
84  * rule unless it is overridden.
85  * </p><p>
86  * <em>IMPORTANT:</em> By providing an implementation of this
87  * method that allows a subclass of a class which does not
88  * normally allow subclassing to be created, the implementer
89  * agrees to be fully responsible for the fact that any such
90  * subclass will likely fail between SWT releases and will be
91  * strongly platform specific. No support is provided for
92  * user-written classes which are implemented in this fashion.
93  * </p><p>
94  * The ability to subclass outside of the allowed SWT classes
95  * is intended purely to enable those not on the SWT development
96  * team to implement patches in order to get around specific
97  * limitations in advance of when those limitations can be
98  * addressed by the team. Subclassing should not be attempted
99  * without an intimate and detailed understanding of the hierarchy.
100  * </p>
101  *
102  * @exception SWTException <ul>
103  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
104  * </ul>
105  */
106 protected void checkSubclass () {
107         String name = getClass().getName ();
108         String validName = Clipboard.class.getName();
109         if (!validName.equals(name)) {
110                 DND.error (SWT.ERROR_INVALID_SUBCLASS);
111         }
112 }
113
114 /**
115  * Throws an <code>SWTException</code> if the receiver can not
116  * be accessed by the caller. This may include both checks on
117  * the state of the receiver and more generally on the entire
118  * execution context. This method <em>should</em> be called by
119  * widget implementors to enforce the standard SWT invariants.
120  * <p>
121  * Currently, it is an error to invoke any method (other than
122  * <code>isDisposed()</code>) on a widget that has had its
123  * <code>dispose()</code> method called. It is also an error
124  * to call widget methods from any thread that is different
125  * from the thread that created the widget.
126  * </p><p>
127  * In future releases of SWT, there may be more or fewer error
128  * checks and exceptions may be thrown for different reasons.
129  * </p>
130  *
131  * @exception SWTException <ul>
132  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
133  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
134  * </ul>
135  */
136 protected void checkWidget () {
137         Display display = this.display;
138         if (display == null) DND.error (SWT.ERROR_WIDGET_DISPOSED);
139         if (display.getThread() != Thread.currentThread ()) DND.error (SWT.ERROR_THREAD_INVALID_ACCESS);
140         if (display.isDisposed()) DND.error(SWT.ERROR_WIDGET_DISPOSED);
141 }
142
143 /**
144  * If this clipboard is currently the owner of the data on the system clipboard,
145  * clear the contents.  If this clipboard is not the owner, then nothing is done.
146  * Note that there are clipboard assistant applications that take ownership of
147  * data or make copies of data when it is placed on the clipboard.  In these
148  * cases, it may not be possible to clear the clipboard.
149  *
150  * @exception SWTException <ul>
151  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
152  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
153  * </ul>
154  *
155  * @since 3.1
156  */
157 public void clearContents() {
158         clearContents(DND.CLIPBOARD);
159 }
160
161 /**
162  * If this clipboard is currently the owner of the data on the specified
163  * clipboard, clear the contents.  If this clipboard is not the owner, then
164  * nothing is done.
165  *
166  * <p>Note that there are clipboard assistant applications that take ownership
167  * of data or make copies of data when it is placed on the clipboard.  In these
168  * cases, it may not be possible to clear the clipboard.</p>
169  *
170  * <p>The clipboards value is either one of the clipboard constants defined in
171  * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together
172  * (that is, using the <code>int</code> "|" operator) two or more
173  * of those <code>DND</code> clipboard constants.</p>
174  *
175  * @param clipboards to be cleared
176  *
177  * @exception SWTException <ul>
178  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
179  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
180  * </ul>
181  *
182  * @see DND#CLIPBOARD
183  * @see DND#SELECTION_CLIPBOARD
184  *
185  * @since 3.1
186  */
187 public void clearContents(int clipboards) {
188         checkWidget();
189         if ((clipboards & DND.CLIPBOARD) != 0) {
190                 /* OleIsCurrentClipboard([in] pDataObject)
191                  * The argument pDataObject is owned by the caller so reference count does not
192                  * need to be incremented.
193                  */
194                 if (COM.OleIsCurrentClipboard(this.iDataObject.getAddress()) == COM.S_OK) {
195                         /* OleSetClipboard([in] pDataObject)
196                          * The argument pDataObject is owned by the caller so reference count does not
197                          * need to be incremented.
198                          */
199                         COM.OleSetClipboard(0);
200                 }
201         }
202 }
203
204 /**
205  * Disposes of the operating system resources associated with the clipboard.
206  * The data will still be available on the system clipboard after the dispose
207  * method is called.
208  *
209  * <p>NOTE: On some platforms the data will not be available once the application
210  * has exited or the display has been disposed.</p>
211  *
212  * @exception SWTException <ul>
213  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
214  * </ul>
215  */
216 public void dispose () {
217         if (isDisposed()) return;
218         if (display.getThread() != Thread.currentThread()) DND.error(SWT.ERROR_THREAD_INVALID_ACCESS);
219         /* OleIsCurrentClipboard([in] pDataObject)
220          * The argument pDataObject is owned by the caller so reference count does not
221          * need to be incremented.
222          */
223         if (COM.OleIsCurrentClipboard(this.iDataObject.getAddress()) == COM.S_OK) {
224                 COM.OleFlushClipboard();
225         }
226         this.Release();
227         display = null;
228 }
229
230 /**
231  * Retrieve the data of the specified type currently available on the system
232  * clipboard.  Refer to the specific subclass of <code>Transfer</code> to
233  * determine the type of object returned.
234  *
235  * <p>The following snippet shows text and RTF text being retrieved from the
236  * clipboard:</p>
237  *
238  *    <pre><code>
239  *    Clipboard clipboard = new Clipboard(display);
240  *    TextTransfer textTransfer = TextTransfer.getInstance();
241  *    String textData = (String)clipboard.getContents(textTransfer);
242  *    if (textData != null) System.out.println("Text is "+textData);
243  *    RTFTransfer rtfTransfer = RTFTransfer.getInstance();
244  *    String rtfData = (String)clipboard.getContents(rtfTransfer);
245  *    if (rtfData != null) System.out.println("RTF Text is "+rtfData);
246  *    clipboard.dispose();
247  *    </code></pre>
248  *
249  * @param transfer the transfer agent for the type of data being requested
250  * @return the data obtained from the clipboard or null if no data of this type is available
251  *
252  * @exception SWTException <ul>
253  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
254  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
255  * </ul>
256  * @exception IllegalArgumentException <ul>
257  *    <li>ERROR_NULL_ARGUMENT - if transfer is null</li>
258  * </ul>
259  *
260  * @see Transfer
261  */
262 public Object getContents(Transfer transfer) {
263         return getContents(transfer, DND.CLIPBOARD);
264 }
265 /**
266  * Retrieve the data of the specified type currently available on the specified
267  * clipboard.  Refer to the specific subclass of <code>Transfer</code> to
268  * determine the type of object returned.
269  *
270  * <p>The following snippet shows text and RTF text being retrieved from the
271  * clipboard:</p>
272  *
273  *    <pre><code>
274  *    Clipboard clipboard = new Clipboard(display);
275  *    TextTransfer textTransfer = TextTransfer.getInstance();
276  *    String textData = (String)clipboard.getContents(textTransfer);
277  *    if (textData != null) System.out.println("Text is "+textData);
278  *    RTFTransfer rtfTransfer = RTFTransfer.getInstance();
279  *    String rtfData = (String)clipboard.getContents(rtfTransfer, DND.CLIPBOARD);
280  *    if (rtfData != null) System.out.println("RTF Text is "+rtfData);
281  *    clipboard.dispose();
282  *    </code></pre>
283  *
284  * <p>The clipboards value is either one of the clipboard constants defined in
285  * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together
286  * (that is, using the <code>int</code> "|" operator) two or more
287  * of those <code>DND</code> clipboard constants.</p>
288  *
289  * @param transfer the transfer agent for the type of data being requested
290  * @param clipboards on which to look for data
291  *
292  * @return the data obtained from the clipboard or null if no data of this type is available
293  *
294  * @exception SWTException <ul>
295  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
296  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
297  * </ul>
298  * @exception IllegalArgumentException <ul>
299  *    <li>ERROR_NULL_ARGUMENT - if transfer is null</li>
300  * </ul>
301  *
302  * @see Transfer
303  * @see DND#CLIPBOARD
304  * @see DND#SELECTION_CLIPBOARD
305  *
306  * @since 3.1
307  */
308 public Object getContents(Transfer transfer, int clipboards) {
309         checkWidget();
310         if (transfer == null) DND.error(SWT.ERROR_NULL_ARGUMENT);
311         if ((clipboards & DND.CLIPBOARD) == 0) return null;
312         /*
313         * Bug in Windows. When a new application takes control
314         * of the clipboard, other applications may open the
315         * clipboard to determine if they want to record the
316         * clipboard updates.  When this happens, the clipboard
317         * can not be accessed until the other application is
318         * finished.  To allow the other applications to release
319         * the clipboard, use PeekMessage() to enable cross thread
320         * message sends.
321         */
322         long[] ppv = new long[1];
323         int retryCount = 0;
324         /* OleGetClipboard([out] ppDataObject).
325          * AddRef has already been called on ppDataObject by the callee and must be released by the caller.
326          */
327         int result = COM.OleGetClipboard(ppv);
328         while (result != COM.S_OK && retryCount++ < RETRY_LIMIT) {
329                 try {Thread.sleep(50);} catch (Throwable t) {}
330                 MSG msg = new MSG();
331                 OS.PeekMessage(msg, 0, 0, 0, OS.PM_NOREMOVE | OS.PM_NOYIELD);
332                 result = COM.OleGetClipboard(ppv);
333         }
334         if (result != COM.S_OK) return null;
335         IDataObject dataObject = new IDataObject(ppv[0]);
336         try {
337                 TransferData[] allowed = transfer.getSupportedTypes();
338                 for (int i = 0; i < allowed.length; i++) {
339                         if (dataObject.QueryGetData(allowed[i].formatetc) == COM.S_OK) {
340                                 TransferData data = allowed[i];
341                                 data.pIDataObject = ppv[0];
342                                 return transfer.nativeToJava(data);
343                         }
344                 }
345         } finally {
346                 dataObject.Release();
347         }
348         return null; // No data available for this transfer
349 }
350 /**
351  * Returns <code>true</code> if the clipboard has been disposed,
352  * and <code>false</code> otherwise.
353  * <p>
354  * This method gets the dispose state for the clipboard.
355  * When a clipboard has been disposed, it is an error to
356  * invoke any other method using the clipboard.
357  * </p>
358  *
359  * @return <code>true</code> when the widget is disposed and <code>false</code> otherwise
360  *
361  * @since 3.0
362  */
363 public boolean isDisposed () {
364         return (display == null);
365 }
366
367 /**
368  * Place data of the specified type on the system clipboard.  More than one type
369  * of data can be placed on the system clipboard at the same time.  Setting the
370  * data clears any previous data from the system clipboard, regardless of type.
371  *
372  * <p>NOTE: On some platforms, the data is immediately copied to the system
373  * clipboard but on other platforms it is provided upon request.  As a result,
374  * if the application modifies the data object it has set on the clipboard, that
375  * modification may or may not be available when the data is subsequently
376  * requested.</p>
377  *
378  * <p>The following snippet shows text and RTF text being set on the copy/paste
379  * clipboard:
380  * </p>
381  *
382  * <pre><code>
383  *      Clipboard clipboard = new Clipboard(display);
384  *      String textData = "Hello World";
385  *      String rtfData = "{\\rtf1\\b\\i Hello World}";
386  *      TextTransfer textTransfer = TextTransfer.getInstance();
387  *      RTFTransfer rtfTransfer = RTFTransfer.getInstance();
388  *      Transfer[] transfers = new Transfer[]{textTransfer, rtfTransfer};
389  *      Object[] data = new Object[]{textData, rtfData};
390  *      clipboard.setContents(data, transfers);
391  *      clipboard.dispose();
392  * </code></pre>
393  *
394  * @param data the data to be set in the clipboard
395  * @param dataTypes the transfer agents that will convert the data to its
396  * platform specific format; each entry in the data array must have a
397  * corresponding dataType
398  *
399  * @exception IllegalArgumentException <ul>
400  *    <li>ERROR_INVALID_ARGUMENT - if data is null or datatypes is null
401  *          or the length of data is not the same as the length of dataTypes</li>
402  * </ul>
403  * @exception SWTException <ul>
404  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
405  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
406  * </ul>
407  *  @exception SWTError <ul>
408  *    <li>ERROR_CANNOT_SET_CLIPBOARD - if the clipboard is locked or otherwise unavailable</li>
409  * </ul>
410  *
411  * <p>NOTE: ERROR_CANNOT_SET_CLIPBOARD should be an SWTException, since it is a
412  * recoverable error, but can not be changed due to backward compatibility.</p>
413  */
414 public void setContents(Object[] data, Transfer[] dataTypes) {
415         setContents(data, dataTypes, DND.CLIPBOARD);
416 }
417
418 /**
419  * Place data of the specified type on the specified clipboard.  More than one
420  * type of data can be placed on the specified clipboard at the same time.
421  * Setting the data clears any previous data from the specified
422  * clipboard, regardless of type.
423  *
424  * <p>NOTE: On some platforms, the data is immediately copied to the specified
425  * clipboard but on other platforms it is provided upon request.  As a result,
426  * if the application modifies the data object it has set on the clipboard, that
427  * modification may or may not be available when the data is subsequently
428  * requested.</p>
429  *
430  * <p>The clipboards value is either one of the clipboard constants defined in
431  * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together
432  * (that is, using the <code>int</code> "|" operator) two or more
433  * of those <code>DND</code> clipboard constants.</p>
434  *
435  * <p>The following snippet shows text and RTF text being set on the copy/paste
436  * clipboard:
437  * </p>
438  *
439  * <pre><code>
440  *      Clipboard clipboard = new Clipboard(display);
441  *      String textData = "Hello World";
442  *      String rtfData = "{\\rtf1\\b\\i Hello World}";
443  *      TextTransfer textTransfer = TextTransfer.getInstance();
444  *      RTFTransfer rtfTransfer = RTFTransfer.getInstance();
445  *      Transfer[] transfers = new Transfer[]{textTransfer, rtfTransfer};
446  *      Object[] data = new Object[]{textData, rtfData};
447  *      clipboard.setContents(data, transfers, DND.CLIPBOARD);
448  *      clipboard.dispose();
449  * </code></pre>
450  *
451  * @param data the data to be set in the clipboard
452  * @param dataTypes the transfer agents that will convert the data to its
453  * platform specific format; each entry in the data array must have a
454  * corresponding dataType
455  * @param clipboards on which to set the data
456  *
457  * @exception IllegalArgumentException <ul>
458  *    <li>ERROR_INVALID_ARGUMENT - if data is null or datatypes is null
459  *          or the length of data is not the same as the length of dataTypes</li>
460  * </ul>
461  * @exception SWTException <ul>
462  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
463  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
464  * </ul>
465  *  @exception SWTError <ul>
466  *    <li>ERROR_CANNOT_SET_CLIPBOARD - if the clipboard is locked or otherwise unavailable</li>
467  * </ul>
468  *
469  * <p>NOTE: ERROR_CANNOT_SET_CLIPBOARD should be an SWTException, since it is a
470  * recoverable error, but can not be changed due to backward compatibility.</p>
471  *
472  * @see DND#CLIPBOARD
473  * @see DND#SELECTION_CLIPBOARD
474  *
475  * @since 3.1
476  */
477 public void setContents(Object[] data, Transfer[] dataTypes, int clipboards) {
478         checkWidget();
479         if (data == null || dataTypes == null || data.length != dataTypes.length || data.length == 0) {
480                 DND.error(SWT.ERROR_INVALID_ARGUMENT);
481         }
482         for (int i = 0; i < data.length; i++) {
483                 if (data[i] == null || dataTypes[i] == null || !dataTypes[i].validate(data[i])) {
484                         DND.error(SWT.ERROR_INVALID_ARGUMENT);
485                 }
486         }
487         if ((clipboards & DND.CLIPBOARD) == 0) return;
488         this.data = data;
489         this.transferAgents = dataTypes;
490         /* OleSetClipboard([in] pDataObject)
491          * The argument pDataObject is owned by the caller so the reference count does not
492          * need to be incremented.
493          */
494         int result = COM.OleSetClipboard(iDataObject.getAddress());
495
496         /*
497         * Bug in Windows. When a new application takes control
498         * of the clipboard, other applications may open the
499         * clipboard to determine if they want to record the
500         * clipboard updates.  When this happens, the clipboard
501         * can not be flushed until the other application is
502         * finished.  To allow other applications to get the
503         * data, use PeekMessage() to enable cross thread
504         * message sends.
505         */
506         int retryCount = 0;
507         while (result != COM.S_OK && retryCount++ < RETRY_LIMIT) {
508                 try {Thread.sleep(50);} catch (Throwable t) {}
509                 MSG msg = new MSG();
510                 OS.PeekMessage(msg, 0, 0, 0, OS.PM_NOREMOVE | OS.PM_NOYIELD);
511                 result = COM.OleSetClipboard(iDataObject.getAddress());
512         }
513         if (result != COM.S_OK) {
514                 DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD);
515         }
516 }
517 private int AddRef() {
518         refCount++;
519         return refCount;
520 }
521 private void createCOMInterfaces() {
522         // register each of the interfaces that this object implements
523         iDataObject = new COMObject(new int[]{2, 0, 0, 2, 2, 1, 2, 3, 2, 4, 1, 1}){
524                 @Override
525                 public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
526                 @Override
527                 public long method1(long[] args) {return AddRef();}
528                 @Override
529                 public long method2(long[] args) {return Release();}
530                 @Override
531                 public long method3(long[] args) {return GetData(args[0], args[1]);}
532                 // method4 GetDataHere - not implemented
533                 @Override
534                 public long method5(long[] args) {return QueryGetData(args[0]);}
535                 // method6 GetCanonicalFormatEtc - not implemented
536                 // method7 SetData - not implemented
537                 @Override
538                 public long method8(long[] args) {return EnumFormatEtc((int)args[0], args[1]);}
539                 // method9 DAdvise - not implemented
540                 // method10 DUnadvise - not implemented
541                 // method11 EnumDAdvise - not implemented
542         };
543 }
544 private void disposeCOMInterfaces() {
545         if (iDataObject != null)
546                 iDataObject.dispose();
547         iDataObject = null;
548 }
549 /*
550  * EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc)
551  * Ownership of ppenumFormatetc transfers from callee to caller so reference count on ppenumFormatetc
552  * must be incremented before returning.  Caller is responsible for releasing ppenumFormatetc.
553  */
554 private int EnumFormatEtc(int dwDirection, long ppenumFormatetc) {
555         // only allow getting of data - SetData is not currently supported
556         if (dwDirection == COM.DATADIR_SET) return COM.E_NOTIMPL;
557         // what types have been registered?
558         TransferData[] allowedDataTypes = new TransferData[0];
559         for (int i = 0; i < transferAgents.length; i++){
560                 TransferData[] formats = transferAgents[i].getSupportedTypes();
561                 TransferData[] newAllowedDataTypes = new TransferData[allowedDataTypes.length + formats.length];
562                 System.arraycopy(allowedDataTypes, 0, newAllowedDataTypes, 0, allowedDataTypes.length);
563                 System.arraycopy(formats, 0, newAllowedDataTypes, allowedDataTypes.length, formats.length);
564                 allowedDataTypes = newAllowedDataTypes;
565         }
566         OleEnumFORMATETC enumFORMATETC = new OleEnumFORMATETC();
567         enumFORMATETC.AddRef();
568         FORMATETC[] formats = new FORMATETC[allowedDataTypes.length + 1];
569         for (int i = 0; i < allowedDataTypes.length; i++){
570                 formats[i] = allowedDataTypes[i].formatetc;
571         }
572         // include the drop effect format to specify a copy operation
573         FORMATETC dropeffect = new FORMATETC();
574         dropeffect.cfFormat = CFSTR_PREFERREDDROPEFFECT;
575         dropeffect.dwAspect = COM.DVASPECT_CONTENT;
576         dropeffect.lindex = -1;
577         dropeffect.tymed = COM.TYMED_HGLOBAL;
578         formats[formats.length -1] = dropeffect;
579         enumFORMATETC.setFormats(formats);
580         OS.MoveMemory(ppenumFormatetc, new long[] {enumFORMATETC.getAddress()}, C.PTR_SIZEOF);
581         return COM.S_OK;
582 }
583 private int GetData(long pFormatetc, long pmedium) {
584         /* Called by a data consumer to obtain data from a source data object.
585            The GetData method renders the data described in the specified FORMATETC
586            structure and transfers it through the specified STGMEDIUM structure.
587            The caller then assumes responsibility for releasing the STGMEDIUM structure.
588         */
589         if (pFormatetc == 0 || pmedium == 0) return COM.E_INVALIDARG;
590         if (QueryGetData(pFormatetc) != COM.S_OK) return COM.DV_E_FORMATETC;
591
592         TransferData transferData = new TransferData();
593         transferData.formatetc = new FORMATETC();
594         COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof);
595         transferData.type = transferData.formatetc.cfFormat;
596         transferData.stgmedium = new STGMEDIUM();
597         transferData.result = COM.E_FAIL;
598
599         if (transferData.type == CFSTR_PREFERREDDROPEFFECT) {
600                 // specify that a copy operation is to be performed
601                 STGMEDIUM stgmedium = new STGMEDIUM();
602                 stgmedium.tymed = COM.TYMED_HGLOBAL;
603                 stgmedium.unionField = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, 4);
604                 //TODO - should call GlobalLock
605                 OS.MoveMemory(stgmedium.unionField, new int[] {COM.DROPEFFECT_COPY}, 4);
606                 stgmedium.pUnkForRelease = 0;
607                 COM.MoveMemory(pmedium, stgmedium, STGMEDIUM.sizeof);
608                 return COM.S_OK;
609         }
610
611         // get matching transfer agent to perform conversion
612         int transferIndex = -1;
613         for (int i = 0; i < transferAgents.length; i++){
614                 if (transferAgents[i].isSupportedType(transferData)){
615                         transferIndex = i;
616                         break;
617                 }
618         }
619         if (transferIndex == -1) return COM.DV_E_FORMATETC;
620         transferAgents[transferIndex].javaToNative(data[transferIndex], transferData);
621         COM.MoveMemory(pmedium, transferData.stgmedium, STGMEDIUM.sizeof);
622         return transferData.result;
623 }
624
625 private int QueryGetData(long pFormatetc) {
626         if (transferAgents == null) return COM.E_FAIL;
627         TransferData transferData = new TransferData();
628         transferData.formatetc = new FORMATETC();
629         COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof);
630         transferData.type = transferData.formatetc.cfFormat;
631         if (transferData.type == CFSTR_PREFERREDDROPEFFECT) return COM.S_OK;
632         // is this type supported by the transfer agent?
633         for (int i = 0; i < transferAgents.length; i++){
634                 if (transferAgents[i].isSupportedType(transferData))
635                         return COM.S_OK;
636         }
637
638         return COM.DV_E_FORMATETC;
639 }
640 /* QueryInterface([in] iid, [out] ppvObject)
641  * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject
642  * must be incremented before returning.  Caller is responsible for releasing ppvObject.
643  */
644 private int QueryInterface(long riid, long ppvObject) {
645         if (riid == 0 || ppvObject == 0) return COM.E_INVALIDARG;
646         GUID guid = new GUID();
647         COM.MoveMemory(guid, riid, GUID.sizeof);
648         if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDataObject) ) {
649                 OS.MoveMemory(ppvObject, new long[] {iDataObject.getAddress()}, C.PTR_SIZEOF);
650                 AddRef();
651                 return COM.S_OK;
652         }
653         OS.MoveMemory(ppvObject, new long[] {0}, C.PTR_SIZEOF);
654         return COM.E_NOINTERFACE;
655 }
656 private int Release() {
657         refCount--;
658         if (refCount == 0) {
659                 this.data = new Object[0];
660                 this.transferAgents = new Transfer[0];
661                 disposeCOMInterfaces();
662                 if (COM.FreeUnusedLibraries) {
663                         COM.CoFreeUnusedLibraries();
664                 }
665         }
666         return refCount;
667 }
668
669 /**
670  * Returns an array of the data types currently available on the system
671  * clipboard. Use with Transfer.isSupportedType.
672  *
673  * @return array of data types currently available on the system clipboard
674  *
675  * @exception SWTException <ul>
676  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
677  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
678  * </ul>
679  *
680  * @see Transfer#isSupportedType
681  *
682  * @since 3.0
683  */
684 public TransferData[] getAvailableTypes() {
685         return getAvailableTypes(DND.CLIPBOARD);
686 }
687
688 /**
689  * Returns an array of the data types currently available on the specified
690  * clipboard. Use with Transfer.isSupportedType.
691  *
692  * <p>The clipboards value is either one of the clipboard constants defined in
693  * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together
694  * (that is, using the <code>int</code> "|" operator) two or more
695  * of those <code>DND</code> clipboard constants.</p>
696  *
697  * @param clipboards from which to get the data types
698  * @return array of data types currently available on the specified clipboard
699  *
700  * @exception SWTException <ul>
701  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
702  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
703  * </ul>
704  *
705  * @see Transfer#isSupportedType
706  * @see DND#CLIPBOARD
707  * @see DND#SELECTION_CLIPBOARD
708  *
709  * @since 3.1
710  */
711 public TransferData[] getAvailableTypes(int clipboards) {
712         checkWidget();
713         if ((clipboards & DND.CLIPBOARD) == 0) return new TransferData[0];
714         FORMATETC[] types = _getAvailableTypes();
715         TransferData[] data = new TransferData[types.length];
716         for (int i = 0; i < types.length; i++) {
717                 data[i] = new TransferData();
718                 data[i].type = types[i].cfFormat;
719                 data[i].formatetc = types[i];
720         }
721         return data;
722 }
723
724 /**
725  * Returns a platform specific list of the data types currently available on the
726  * system clipboard.
727  *
728  * <p>Note: <code>getAvailableTypeNames</code> is a utility for writing a Transfer
729  * sub-class.  It should NOT be used within an application because it provides
730  * platform specific information.</p>
731  *
732  * @return a platform specific list of the data types currently available on the
733  * system clipboard
734  *
735  * @exception SWTException <ul>
736  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
737  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
738  * </ul>
739  */
740 public String[] getAvailableTypeNames() {
741         checkWidget();
742         FORMATETC[] types = _getAvailableTypes();
743         String[] names = new String[types.length];
744         int maxSize = 128;
745         for (int i = 0; i < types.length; i++){
746                 char [] buffer = new char [maxSize];
747                 int size = OS.GetClipboardFormatName(types[i].cfFormat, buffer, maxSize);
748                 if (size != 0) {
749                         names[i] = new String (buffer, 0, size);
750                 } else {
751                         switch (types[i].cfFormat) {
752                                 case COM.CF_HDROP: names[i] = "CF_HDROP"; break; //$NON-NLS-1$
753                                 case COM.CF_TEXT: names[i] = "CF_TEXT"; break; //$NON-NLS-1$
754                                 case COM.CF_BITMAP: names[i] = "CF_BITMAP"; break; //$NON-NLS-1$
755                                 case COM.CF_METAFILEPICT: names[i] = "CF_METAFILEPICT"; break; //$NON-NLS-1$
756                                 case COM.CF_SYLK: names[i] = "CF_SYLK"; break; //$NON-NLS-1$
757                                 case COM.CF_DIF: names[i] = "CF_DIF"; break; //$NON-NLS-1$
758                                 case COM.CF_TIFF: names[i] = "CF_TIFF"; break; //$NON-NLS-1$
759                                 case COM.CF_OEMTEXT: names[i] = "CF_OEMTEXT"; break; //$NON-NLS-1$
760                                 case COM.CF_DIB: names[i] = "CF_DIB"; break; //$NON-NLS-1$
761                                 case COM.CF_PALETTE: names[i] = "CF_PALETTE"; break; //$NON-NLS-1$
762                                 case COM.CF_PENDATA: names[i] = "CF_PENDATA"; break; //$NON-NLS-1$
763                                 case COM.CF_RIFF: names[i] = "CF_RIFF"; break; //$NON-NLS-1$
764                                 case COM.CF_WAVE: names[i] = "CF_WAVE"; break; //$NON-NLS-1$
765                                 case COM.CF_UNICODETEXT: names[i] = "CF_UNICODETEXT"; break; //$NON-NLS-1$
766                                 case COM.CF_ENHMETAFILE: names[i] = "CF_ENHMETAFILE"; break; //$NON-NLS-1$
767                                 case COM.CF_LOCALE: names[i] = "CF_LOCALE"; break; //$NON-NLS-1$
768                                 case COM.CF_MAX: names[i] = "CF_MAX"; break; //$NON-NLS-1$
769                                 default: names[i] = "UNKNOWN"; //$NON-NLS-1$
770                         }
771                 }
772         }
773         return names;
774 }
775
776 private FORMATETC[] _getAvailableTypes() {
777         FORMATETC[] types = new FORMATETC[0];
778         long[] ppv = new long[1];
779         /* OleGetClipboard([out] ppDataObject).
780          * AddRef has already been called on ppDataObject by the callee and must be released by the caller.
781          */
782         if (COM.OleGetClipboard(ppv) != COM.S_OK) return types;
783         IDataObject dataObject = new IDataObject(ppv[0]);
784         long[] ppFormatetc = new long[1];
785         /* EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc)
786          * AddRef has already been called on ppenumFormatetc by the callee and must be released by the caller.
787          */
788         int rc = dataObject.EnumFormatEtc(COM.DATADIR_GET, ppFormatetc);
789         dataObject.Release();
790         if (rc != COM.S_OK)return types;
791         IEnumFORMATETC enumFormatetc = new IEnumFORMATETC(ppFormatetc[0]);
792         // Loop over enumerator and save any types that match what we are looking for
793         long rgelt = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, FORMATETC.sizeof);
794         int[] pceltFetched = new int[1];
795         enumFormatetc.Reset();
796         while (enumFormatetc.Next(1, rgelt, pceltFetched) == COM.S_OK && pceltFetched[0] == 1) {
797                 FORMATETC formatetc = new FORMATETC();
798                 COM.MoveMemory(formatetc, rgelt, FORMATETC.sizeof);
799                 FORMATETC[] newTypes = new FORMATETC[types.length + 1];
800                 System.arraycopy(types, 0, newTypes, 0, types.length);
801                 newTypes[types.length] = formatetc;
802                 types = newTypes;
803         }
804         OS.GlobalFree(rgelt);
805         enumFormatetc.Release();
806         return types;
807 }
808 }