| 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 |
|---|