root/dwt/dnd/Clipboard.d

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

Updater SWT 3.4M7 to 3.4

Line 
1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.dnd.Clipboard;
14
15
16 import dwt.DWT;
17 import dwt.DWTError;
18 import dwt.DWTException;
19 import dwt.internal.ole.win32.COM;
20 import dwt.internal.ole.win32.OBJIDL;
21 import dwt.internal.ole.win32.extras;
22 import dwt.internal.win32.OS;
23 import dwt.widgets.Display;
24
25 import dwt.dnd.Transfer;
26 import dwt.dnd.TransferData;
27 import dwt.dnd.OleEnumFORMATETC;
28 import dwt.dnd.DND;
29
30 import dwt.dwthelper.utils;
31 import tango.core.Thread;
32
33 /**
34  * The <code>Clipboard</code> provides a mechanism for transferring data from one
35  * application to another or within an application.
36  *
37  * <p>IMPORTANT: This class is <em>not</em> intended to be subclassed.</p>
38  *
39  * @see <a href="http://www.eclipse.org/swt/snippets/#clipboard">Clipboard snippets</a>
40  * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: ClipboardExample</a>
41  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
42  */
43 public class Clipboard {
44
45     private Display display;
46
47     // ole interfaces
48     private _IDataObjectImpl iDataObject;
49     private int refCount;
50     private Transfer[] transferAgents;
51     private Object[] data;
52     private int CFSTR_PREFERREDDROPEFFECT;
53
54 /**
55  * Constructs a new instance of this class.  Creating an instance of a Clipboard
56  * may cause system resources to be allocated depending on the platform.  It is therefore
57  * mandatory that the Clipboard instance be disposed when no longer required.
58  *
59  * @param display the display on which to allocate the clipboard
60  *
61  * @exception DWTException <ul>
62  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
63  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
64  * </ul>
65  *
66  * @see Clipboard#dispose
67  * @see Clipboard#checkSubclass
68  */
69 public this(Display display) {
70     checkSubclass ();
71     if (display is null) {
72         display = Display.getCurrent();
73         if (display is null) {
74             display = Display.getDefault();
75         }
76     }
77     if (display.getThread() !is Thread.getThis()) {
78         DND.error(DWT.ERROR_THREAD_INVALID_ACCESS);
79     }
80     this.display = display;
81     TCHAR* chFormatName = StrToTCHARz(0, "Preferred DropEffect"); //$NON-NLS-1$
82     CFSTR_PREFERREDDROPEFFECT = OS.RegisterClipboardFormat(chFormatName);
83     createCOMInterfaces();
84     this.AddRef();
85 }
86
87 /**
88  * Checks that this class can be subclassed.
89  * <p>
90  * The DWT class library is intended to be subclassed
91  * only at specific, controlled points. This method enforces this
92  * rule unless it is overridden.
93  * </p><p>
94  * <em>IMPORTANT:</em> By providing an implementation of this
95  * method that allows a subclass of a class which does not
96  * normally allow subclassing to be created, the implementer
97  * agrees to be fully responsible for the fact that any such
98  * subclass will likely fail between DWT releases and will be
99  * strongly platform specific. No support is provided for
100  * user-written classes which are implemented in this fashion.
101  * </p><p>
102  * The ability to subclass outside of the allowed DWT classes
103  * is intended purely to enable those not on the DWT development
104  * team to implement patches in order to get around specific
105  * limitations in advance of when those limitations can be
106  * addressed by the team. Subclassing should not be attempted
107  * without an intimate and detailed understanding of the hierarchy.
108  * </p>
109  *
110  * @exception DWTException <ul>
111  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
112  * </ul>
113  */
114 protected void checkSubclass () {
115     String name = this.classinfo.name;
116     String validName = Clipboard.classinfo.name;
117     if (validName!=/*eq*/name) {
118         DND.error (DWT.ERROR_INVALID_SUBCLASS);
119     }
120 }
121
122 /**
123  * Throws an <code>DWTException</code> if the receiver can not
124  * be accessed by the caller. This may include both checks on
125  * the state of the receiver and more generally on the entire
126  * execution context. This method <em>should</em> be called by
127  * widget implementors to enforce the standard DWT invariants.
128  * <p>
129  * Currently, it is an error to invoke any method (other than
130  * <code>isDisposed()</code>) on a widget that has had its
131  * <code>dispose()</code> method called. It is also an error
132  * to call widget methods from any thread that is different
133  * from the thread that created the widget.
134  * </p><p>
135  * In future releases of DWT, there may be more or fewer error
136  * checks and exceptions may be thrown for different reasons.
137  * </p>
138  *
139  * @exception DWTException <ul>
140  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
141  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
142  * </ul>
143  */
144 protected void checkWidget () {
145     Display display = this.display;
146     if (display is null) DND.error (DWT.ERROR_WIDGET_DISPOSED);
147     if (display.getThread() !is Thread.getThis ()) DND.error (DWT.ERROR_THREAD_INVALID_ACCESS);
148     if (display.isDisposed()) DND.error(DWT.ERROR_WIDGET_DISPOSED);
149 }
150
151 /**
152  * If this clipboard is currently the owner of the data on the system clipboard,
153  * clear the contents.  If this clipboard is not the owner, then nothing is done.
154  * Note that there are clipboard assistant applications that take ownership of
155  * data or make copies of data when it is placed on the clipboard.  In these
156  * cases, it may not be possible to clear the clipboard.
157  *
158  * @exception DWTException <ul>
159  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
160  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
161  * </ul>
162  *
163  * @since 3.1
164  */
165 public void clearContents() {
166     clearContents(DND.CLIPBOARD);
167 }
168
169 /**
170  * If this clipboard is currently the owner of the data on the specified
171  * clipboard, clear the contents.  If this clipboard is not the owner, then
172  * nothing is done.
173  *
174  * <p>Note that there are clipboard assistant applications that take ownership
175  * of data or make copies of data when it is placed on the clipboard.  In these
176  * cases, it may not be possible to clear the clipboard.</p>
177  *
178  * <p>The clipboards value is either one of the clipboard constants defined in
179  * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together
180  * (that is, using the <code>int</code> "|" operator) two or more
181  * of those <code>DND</code> clipboard constants.</p>
182  *
183  * @param clipboards to be cleared
184  *
185  * @exception DWTException <ul>
186  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
187  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
188  * </ul>
189  *
190  * @see DND#CLIPBOARD
191  * @see DND#SELECTION_CLIPBOARD
192  *
193  * @since 3.1
194  */
195 public void clearContents(int clipboards) {
196     checkWidget();
197     if ((clipboards & DND.CLIPBOARD) !is 0) {
198         /* OleIsCurrentClipboard([in] pDataObject)
199          * The argument pDataObject is owned by the caller so reference count does not
200          * need to be incremented.
201          */
202         if (COM.OleIsCurrentClipboard(this.iDataObject) is COM.S_OK) {
203             /* OleSetClipboard([in] pDataObject)
204              * The argument pDataObject is owned by the caller so reference count does not
205              * need to be incremented.
206              */
207             COM.OleSetClipboard(null);
208         }
209     }
210 }
211
212 /**
213  * Disposes of the operating system resources associated with the clipboard.
214  * The data will still be available on the system clipboard after the dispose
215  * method is called.
216  *
217  * <p>NOTE: On some platforms the data will not be available once the application
218  * has exited or the display has been disposed.</p>
219  *
220  * @exception DWTException <ul>
221  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
222  * </ul>
223  */
224 public void dispose () {
225     if (isDisposed()) return;
226     if (display.getThread() !is Thread.getThis ()) DND.error(DWT.ERROR_THREAD_INVALID_ACCESS);
227     /* OleIsCurrentClipboard([in] pDataObject)
228      * The argument pDataObject is owned by the caller so reference count does not
229      * need to be incremented.
230      */
231     if (COM.OleIsCurrentClipboard(this.iDataObject) is COM.S_OK) {
232         COM.OleFlushClipboard();
233     }
234     this.Release();
235     display = null;
236 }
237
238 /**
239  * Retrieve the data of the specified type currently available on the system
240  * clipboard.  Refer to the specific subclass of <code>Transfer</code> to
241  * determine the type of object returned.
242  *
243  * <p>The following snippet shows text and RTF text being retrieved from the
244  * clipboard:</p>
245  *
246  *    <code><pre>
247  *    Clipboard clipboard = new Clipboard(display);
248  *    TextTransfer textTransfer = TextTransfer.getInstance();
249  *    String textData = (String)clipboard.getContents(textTransfer);
250  *    if (textData !is null) System.out.println("Text is "+textData);
251  *    RTFTransfer rtfTransfer = RTFTransfer.getInstance();
252  *    String rtfData = (String)clipboard.getContents(rtfTransfer);
253  *    if (rtfData !is null) System.out.println("RTF Text is "+rtfData);
254  *    clipboard.dispose();
255  *    </code></pre>
256  *
257  * @param transfer the transfer agent for the type of data being requested
258  * @return the data obtained from the clipboard or null if no data of this type is available
259  *
260  * @exception DWTException <ul>
261  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
262  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
263  * </ul>
264  * @exception IllegalArgumentException <ul>
265  *    <li>ERROR_NULL_ARGUMENT - if transfer is null</li>
266  * </ul>
267  *
268  * @see Transfer
269  */
270 public Object getContents(Transfer transfer) {
271     return getContents(transfer, DND.CLIPBOARD);
272 }
273 /**
274  * Retrieve the data of the specified type currently available on the specified
275  * clipboard.  Refer to the specific subclass of <code>Transfer</code> to
276  * determine the type of object returned.
277  *
278  * <p>The following snippet shows text and RTF text being retrieved from the
279  * clipboard:</p>
280  *
281  *    <code><pre>
282  *    Clipboard clipboard = new Clipboard(display);
283  *    TextTransfer textTransfer = TextTransfer.getInstance();
284  *    String textData = (String)clipboard.getContents(textTransfer);
285  *    if (textData !is null) System.out.println("Text is "+textData);
286  *    RTFTransfer rtfTransfer = RTFTransfer.getInstance();
287  *    String rtfData = (String)clipboard.getContents(rtfTransfer, DND.CLIPBOARD);
288  *    if (rtfData !is null) System.out.println("RTF Text is "+rtfData);
289  *    clipboard.dispose();
290  *    </code></pre>
291  *
292  * <p>The clipboards value is either one of the clipboard constants defined in
293  * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together
294  * (that is, using the <code>int</code> "|" operator) two or more
295  * of those <code>DND</code> clipboard constants.</p>
296  *
297  * @param transfer the transfer agent for the type of data being requested
298  * @param clipboards on which to look for data
299  *
300  * @return the data obtained from the clipboard or null if no data of this type is available
301  *
302  * @exception DWTException <ul>
303  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
304  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
305  * </ul>
306  * @exception IllegalArgumentException <ul>
307  *    <li>ERROR_NULL_ARGUMENT - if transfer is null</li>
308  * </ul>
309  *
310  * @see Transfer
311  * @see DND#CLIPBOARD
312  * @see DND#SELECTION_CLIPBOARD
313  *
314  * @since 3.1
315  */
316 public Object getContents(Transfer transfer, int clipboards) {
317     checkWidget();
318     if (transfer is null) DND.error(DWT.ERROR_NULL_ARGUMENT);
319     if ((clipboards & DND.CLIPBOARD) is 0) return null;
320     /*
321     * Bug in Windows. When a new application takes control
322     * of the clipboard, other applications may open the
323     * clipboard to determine if they want to record the
324     * clipboard updates.  When this happens, the clipboard
325     * can not be accessed until the other application is
326     * finished.  To allow the other applications to release
327     * the clipboard, use PeekMessage() to enable cross thread
328     * message sends.
329     */
330     IDataObject dataObject;
331     int retryCount = 0;
332     /* OleGetClipboard([out] ppDataObject).
333      * AddRef has already been called on ppDataObject by the callee and must be released by the caller.
334      */
335     int result = COM.OleGetClipboard(&dataObject);
336     while (result !is COM.S_OK && retryCount++ < 10) {
337         try {Thread.sleep(0.050);} catch (Exception t) {}
338         MSG msg;
339         OS.PeekMessage(&msg, null, 0, 0, OS.PM_NOREMOVE | OS.PM_NOYIELD);
340         result = COM.OleGetClipboard(&dataObject);
341     }
342     if (result !is COM.S_OK) return null;
343     try {
344         TransferData[] allowed = transfer.getSupportedTypes();
345         for (int i = 0; i < allowed.length; i++) {
346             if (dataObject.QueryGetData(allowed[i].formatetc) is COM.S_OK) {
347                 TransferData data = allowed[i];
348                 data.pIDataObject = dataObject;
349                 return transfer.nativeToJava(data);
350             }
351         }
352     } finally {
353         dataObject.Release();
354     }
355     return null; // No data available for this transfer
356 }
357 /**
358  * Returns <code>true</code> if the clipboard has been disposed,
359  * and <code>false</code> otherwise.
360  * <p>
361  * This method gets the dispose state for the clipboard.
362  * When a clipboard has been disposed, it is an error to
363  * invoke any other method using the clipboard.
364  * </p>
365  *
366  * @return <code>true</code> when the widget is disposed and <code>false</code> otherwise
367  *
368  * @since 3.0
369  */
370 public bool isDisposed () {
371     return (display is null);
372 }
373
374 /**
375  * Place data of the specified type on the system clipboard.  More than one type
376  * of data can be placed on the system clipboard at the same time.  Setting the
377  * data clears any previous data from the system clipboard, regardless of type.
378  *
379  * <p>NOTE: On some platforms, the data is immediately copied to the system
380  * clipboard but on other platforms it is provided upon request.  As a result,
381  * if the application modifies the data object it has set on the clipboard, that
382  * modification may or may not be available when the data is subsequently
383  * requested.</p>
384  *
385  * <p>The following snippet shows text and RTF text being set on the copy/paste
386  * clipboard:
387  * </p>
388  *
389  * <code><pre>
390  *  Clipboard clipboard = new Clipboard(display);
391  *  String textData = "Hello World";
392  *  String rtfData = "{\\rtf1\\b\\i Hello World}";
393  *  TextTransfer textTransfer = TextTransfer.getInstance();
394  *  RTFTransfer rtfTransfer = RTFTransfer.getInstance();
395  *  Transfer[] transfers = new Transfer[]{textTransfer, rtfTransfer};
396  *  Object[] data = new Object[]{textData, rtfData};
397  *  clipboard.setContents(data, transfers);
398  *  clipboard.dispose();
399  * </code></pre>
400  *
401  * @param data the data to be set in the clipboard
402  * @param dataTypes the transfer agents that will convert the data to its
403  * platform specific format; each entry in the data array must have a
404  * corresponding dataType
405  *
406  * @exception IllegalArgumentException <ul>
407  *    <li>ERROR_INVALID_ARGUMENT - if data is null or datatypes is null
408  *          or the length of data is not the same as the length of dataTypes</li>
409  * </ul>
410  * @exception DWTException <ul>
411  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
412  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
413  * </ul>
414  *  @exception DWTError <ul>
415  *    <li>ERROR_CANNOT_SET_CLIPBOARD - if the clipboard is locked or otherwise unavailable</li>
416  * </ul>
417  *
418  * <p>NOTE: ERROR_CANNOT_SET_CLIPBOARD should be an DWTException, since it is a
419  * recoverable error, but can not be changed due to backward compatibility.</p>
420  */
421 public void setContents(Object[] data, Transfer[] dataTypes) {
422     setContents(data, dataTypes, DND.CLIPBOARD);
423 }
424
425 /**
426  * Place data of the specified type on the specified clipboard.  More than one
427  * type of data can be placed on the specified clipboard at the same time.
428  * Setting the data clears any previous data from the specified
429  * clipboard, regardless of type.
430  *
431  * <p>NOTE: On some platforms, the data is immediately copied to the specified
432  * clipboard but on other platforms it is provided upon request.  As a result,
433  * if the application modifies the data object it has set on the clipboard, that
434  * modification may or may not be available when the data is subsequently
435  * requested.</p>
436  *
437  * <p>The clipboards value is either one of the clipboard constants defined in
438  * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together
439  * (that is, using the <code>int</code> "|" operator) two or more
440  * of those <code>DND</code> clipboard constants.</p>
441  *
442  * <p>The following snippet shows text and RTF text being set on the copy/paste
443  * clipboard:
444  * </p>
445  *
446  * <code><pre>
447  *  Clipboard clipboard = new Clipboard(display);
448  *  String textData = "Hello World";
449  *  String rtfData = "{\\rtf1\\b\\i Hello World}";
450  *  TextTransfer textTransfer = TextTransfer.getInstance();
451  *  RTFTransfer rtfTransfer = RTFTransfer.getInstance();
452  *  Transfer[] transfers = new Transfer[]{textTransfer, rtfTransfer};
453  *  Object[] data = new Object[]{textData, rtfData};
454  *  clipboard.setContents(data, transfers, DND.CLIPBOARD);
455  *  clipboard.dispose();
456  * </code></pre>
457  *
458  * @param data the data to be set in the clipboard
459  * @param dataTypes the transfer agents that will convert the data to its
460  * platform specific format; each entry in the data array must have a
461  * corresponding dataType
462  * @param clipboards on which to set the data
463  *
464  * @exception IllegalArgumentException <ul>
465  *    <li>ERROR_INVALID_ARGUMENT - if data is null or datatypes is null
466  *          or the length of data is not the same as the length of dataTypes</li>
467  * </ul>
468  * @exception DWTException <ul>
469  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
470  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
471  * </ul>
472  *  @exception DWTError <ul>
473  *    <li>ERROR_CANNOT_SET_CLIPBOARD - if the clipboard is locked or otherwise unavailable</li>
474  * </ul>
475  *
476  * <p>NOTE: ERROR_CANNOT_SET_CLIPBOARD should be an DWTException, since it is a
477  * recoverable error, but can not be changed due to backward compatibility.</p>
478  *
479  * @see DND#CLIPBOARD
480  * @see DND#SELECTION_CLIPBOARD
481  *
482  * @since 3.1
483  */
484 public void setContents(Object[] data, Transfer[] dataTypes, int clipboards) {
485     checkWidget();
486     if (data is null || dataTypes is null || data.length !is dataTypes.length || data.length is 0) {
487         DND.error(DWT.ERROR_INVALID_ARGUMENT);
488     }
489     for (int i = 0; i < data.length; i++) {
490         if (data[i] is null || dataTypes[i] is null || !dataTypes[i].validate(data[i])) {
491             DND.error(DWT.ERROR_INVALID_ARGUMENT);
492         }
493     }
494     if ((clipboards & DND.CLIPBOARD) is 0) return;
495     this.data = data;
496     this.transferAgents = dataTypes;
497     /* OleSetClipboard([in] pDataObject)
498      * The argument pDataObject is owned by the caller so the reference count does not
499      * need to be incremented.
500      */
501     int result = COM.OleSetClipboard(iDataObject);
502
503     /*
504     * Bug in Windows. When a new application takes control
505     * of the clipboard, other applications may open the
506     * clipboard to determine if they want to record the
507     * clipboard updates.  When this happens, the clipboard
508     * can not be flushed until the other application is
509     * finished.  To allow other applications to get the
510     * data, use PeekMessage() to enable cross thread
511     * message sends.
512     */
513     int retryCount = 0;
514     while (result !is COM.S_OK && retryCount++ < 10) {
515         try {Thread.sleep(0.050);} catch (Exception t) {}
516         MSG msg;
517         OS.PeekMessage(&msg, null, 0, 0, OS.PM_NOREMOVE | OS.PM_NOYIELD);
518         result = COM.OleSetClipboard(iDataObject);
519     }
520     if (result !is COM.S_OK) {
521         DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD);
522     }
523 }
524 private int AddRef() {
525     refCount++;
526     return refCount;
527 }
528 private void createCOMInterfaces() {
529     // register each of the interfaces that this object implements
530     iDataObject = new _IDataObjectImpl( this );
531 }
532 private void disposeCOMInterfaces() {
533     iDataObject = null;
534 }
535 /*
536  * EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc)
537  * Ownership of ppenumFormatetc transfers from callee to caller so reference count on ppenumFormatetc
538  * must be incremented before returning.  Caller is responsible for releasing ppenumFormatetc.
539  */
540 LRESULT EnumFormatEtc(int dwDirection, IEnumFORMATETC* ppenumFormatetc) {
541     // only allow getting of data - SetData is not currently supported
542     if (dwDirection is COM.DATADIR_SET) return COM.E_NOTIMPL;
543     // what types have been registered?
544     TransferData[] allowedDataTypes = new TransferData[0];
545     for (int i = 0; i < transferAgents.length; i++){
546         TransferData[] formats = transferAgents[i].getSupportedTypes();
547         TransferData[] newAllowedDataTypes = new TransferData[allowedDataTypes.length + formats.length];
548         System.arraycopy(allowedDataTypes, 0, newAllowedDataTypes, 0, allowedDataTypes.length);
549         System.arraycopy(formats, 0, newAllowedDataTypes, allowedDataTypes.length, formats.length);
550         allowedDataTypes = newAllowedDataTypes;
551     }
552     OleEnumFORMATETC enumFORMATETC = new OleEnumFORMATETC();
553     enumFORMATETC.AddRef();
554     FORMATETC*[] formats = new FORMATETC*[allowedDataTypes.length + 1];
555     for (int i = 0; i < allowedDataTypes.length; i++){
556         formats[i] = allowedDataTypes[i].formatetc;
557     }
558     // include the drop effect format to specify a copy operation
559     FORMATETC* dropeffect = new FORMATETC();
560     dropeffect.cfFormat = CFSTR_PREFERREDDROPEFFECT;
561     dropeffect.dwAspect = COM.DVASPECT_CONTENT;
562     dropeffect.lindex = -1;
563     dropeffect.tymed = COM.TYMED_HGLOBAL;
564     formats[formats.length -1] = dropeffect;
565     enumFORMATETC.setFormats(formats);
566
567     // TODO: <shawn liu> do we need AddRef() here
568     *ppenumFormatetc = enumFORMATETC.getAddress();
569     return COM.S_OK;
570 }
571
572 private IDataObject getAddress(){
573     return iDataObject;
574 }
575
576 LRESULT GetData(FORMATETC *pFormatetc, STGMEDIUM *pmedium) {
577     /* Called by a data consumer to obtain data from a source data object.
578        The GetData method renders the data described in the specified FORMATETC
579        structure and transfers it through the specified STGMEDIUM structure.
580        The caller then assumes responsibility for releasing the STGMEDIUM structure.
581     */
582     if (pFormatetc is null || pmedium is null) return COM.E_INVALIDARG;
583     if (QueryGetData(pFormatetc) !is COM.S_OK) return COM.DV_E_FORMATETC;
584
585     TransferData transferData = new TransferData();
586     transferData.formatetc = new FORMATETC();
587     COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof);
588     transferData.type = transferData.formatetc.cfFormat;
589     transferData.stgmedium = new STGMEDIUM();
590     transferData.result = COM.E_FAIL;
591
592     if (transferData.type is CFSTR_PREFERREDDROPEFFECT) {
593         // specify that a copy operation is to be performed
594         STGMEDIUM* stgmedium = new STGMEDIUM();
595         stgmedium.tymed = COM.TYMED_HGLOBAL;
596         stgmedium.unionField = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, 4);
597         //TODO - should call GlobalLock
598         stgmedium.unionField = cast(void*)COM.DROPEFFECT_COPY;
599         stgmedium.pUnkForRelease = null;
600         COM.MoveMemory(pmedium, stgmedium, STGMEDIUM.sizeof);
601         return COM.S_OK;
602     }
603
604     // get matching transfer agent to perform conversion
605     int transferIndex = -1;
606     for (int i = 0; i < transferAgents.length; i++){
607         if (transferAgents[i].isSupportedType(transferData)){
608             transferIndex = i;
609             break;
610         }
611     }
612     if (transferIndex is -1) return COM.DV_E_FORMATETC;
613     transferAgents[transferIndex].javaToNative(data[transferIndex], transferData);
614     COM.MoveMemory(pmedium, transferData.stgmedium, STGMEDIUM.sizeof);
615     return transferData.result;
616 }
617
618 LRESULT QueryGetData(FORMATETC * pFormatetc) {
619     if (transferAgents is null) return COM.E_FAIL;
620     TransferData transferData = new TransferData();
621     transferData.formatetc = new FORMATETC();
622     COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof);
623     transferData.type = transferData.formatetc.cfFormat;
624     if (transferData.type is CFSTR_PREFERREDDROPEFFECT) return COM.S_OK;
625     // is this type supported by the transfer agent?
626     for (int i = 0; i < transferAgents.length; i++){
627         if (transferAgents[i].isSupportedType(transferData))
628             return COM.S_OK;
629     }
630
631     return COM.DV_E_FORMATETC;
632 }
633 /* QueryInterface([in] iid, [out] ppvObject)
634  * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject
635  * must be incremented before returning.  Caller is responsible for releasing ppvObject.
636  */
637 HRESULT QueryInterface(GUID* riid, void ** ppvObject) {
638     if (riid is null || ppvObject is null) return COM.E_INVALIDARG;
639     if (COM.IsEqualGUID(riid, &COM.IIDIUnknown) || COM.IsEqualGUID(riid, &COM.IIDIDataObject) ) {
640         *ppvObject = cast(void*)cast(IUnknown)iDataObject;
641         AddRef();
642         return COM.S_OK;
643     }
644     *ppvObject = null;
645     return COM.E_NOINTERFACE;
646 }
647 private ULONG Release() {
648     refCount--;
649     if (refCount is 0) {
650         this.data = null;
651         this.transferAgents = null;
652         disposeCOMInterfaces();
653         COM.CoFreeUnusedLibraries();
654     }
655     return refCount;
656 }
657
658 /**
659  * Returns an array of the data types currently available on the system
660  * clipboard. Use with Transfer.isSupportedType.
661  *
662  * @return array of data types currently available on the system clipboard
663  *
664  * @exception DWTException <ul>
665  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
666  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
667  * </ul>
668  *
669  * @see Transfer#isSupportedType
670  *
671  * @since 3.0
672  */
673 public TransferData[] getAvailableTypes() {
674     return getAvailableTypes(DND.CLIPBOARD);
675 }
676
677 /**
678  * Returns an array of the data types currently available on the specified
679  * clipboard. Use with Transfer.isSupportedType.
680  *
681  * <p>The clipboards value is either one of the clipboard constants defined in
682  * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together
683  * (that is, using the <code>int</code> "|" operator) two or more
684  * of those <code>DND</code> clipboard constants.</p>
685