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