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