diff options
author | Silenio Quarti <silenio> | 2009-07-01 14:50:54 +0000 |
---|---|---|
committer | Silenio Quarti <silenio> | 2009-07-01 14:50:54 +0000 |
commit | 093c579a4ffd9551acb901bba9617e7aa776989d (patch) | |
tree | 71cf23798b651ef92f188390841a8d130908fb11 /bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse | |
parent | f664d297f7bb009784868bf3fcf0b3e3bb9a646b (diff) | |
download | eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.tar.gz eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.tar.xz eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.zip |
restore HEAD after accidental deletion by error in automated build script
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse')
17 files changed, 4879 insertions, 0 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/ByteArrayTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/ByteArrayTransfer.java new file mode 100755 index 0000000000..d33f8815ae --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/ByteArrayTransfer.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; + +/** + * The class <code>ByteArrayTransfer</code> provides a platform specific + * mechanism for converting a java <code>byte[]</code> to a platform + * specific representation of the byte array and vice versa. + * + * <p><code>ByteArrayTransfer</code> is never used directly but is sub-classed + * by transfer agents that convert between data in a java format such as a + * <code>String</code> and a platform specific byte array. + * + * <p>If the data you are converting <b>does not</b> map to a + * <code>byte[]</code>, you should sub-class <code>Transfer</code> directly + * and do your own mapping to a platform data type.</p> + * + * <p>The following snippet shows a subclass of ByteArrayTransfer that transfers + * data defined by the class <code>MyType</code>.</p> + * + * <pre><code> + * public class MyType { + * public String fileName; + * public long fileLength; + * public long lastModified; + * } + * </code></pre> + * + * <pre><code> + * public class MyTypeTransfer extends ByteArrayTransfer { + * + * private static final String MYTYPENAME = "my_type_name"; + * private static final int MYTYPEID = registerType(MYTYPENAME); + * private static MyTypeTransfer _instance = new MyTypeTransfer(); + * + * private MyTypeTransfer() {} + * + * public static MyTypeTransfer getInstance () { + * return _instance; + * } + * public void javaToNative (Object object, TransferData transferData) { + * if (object == null || !(object instanceof MyType[])) return; + * + * if (isSupportedType(transferData)) { + * MyType[] myTypes = (MyType[]) object; + * try { + * // write data to a byte array and then ask super to convert to pMedium + * ByteArrayOutputStream out = new ByteArrayOutputStream(); + * DataOutputStream writeOut = new DataOutputStream(out); + * for (int i = 0, length = myTypes.length; i < length; i++){ + * byte[] buffer = myTypes[i].fileName.getBytes(); + * writeOut.writeInt(buffer.length); + * writeOut.write(buffer); + * writeOut.writeLong(myTypes[i].fileLength); + * writeOut.writeLong(myTypes[i].lastModified); + * } + * byte[] buffer = out.toByteArray(); + * writeOut.close(); + * + * super.javaToNative(buffer, transferData); + * + * } catch (IOException e) { + * } + * } + * } + * public Object nativeToJava(TransferData transferData){ + * + * if (isSupportedType(transferData)) { + * + * byte[] buffer = (byte[])super.nativeToJava(transferData); + * if (buffer == null) return null; + * + * MyType[] myData = new MyType[0]; + * try { + * ByteArrayInputStream in = new ByteArrayInputStream(buffer); + * DataInputStream readIn = new DataInputStream(in); + * while(readIn.available() > 20) { + * MyType datum = new MyType(); + * int size = readIn.readInt(); + * byte[] name = new byte[size]; + * readIn.read(name); + * datum.fileName = new String(name); + * datum.fileLength = readIn.readLong(); + * datum.lastModified = readIn.readLong(); + * MyType[] newMyData = new MyType[myData.length + 1]; + * System.arraycopy(myData, 0, newMyData, 0, myData.length); + * newMyData[myData.length] = datum; + * myData = newMyData; + * } + * readIn.close(); + * } catch (IOException ex) { + * return null; + * } + * return myData; + * } + * + * return null; + * } + * protected String[] getTypeNames(){ + * return new String[]{MYTYPENAME}; + * } + * protected int[] getTypeIds(){ + * return new int[] {MYTYPEID}; + * } + * } + * </code></pre> + * + * @see Transfer + */ +public abstract class ByteArrayTransfer extends Transfer { + +public TransferData[] getSupportedTypes() { + int[] types = getTypeIds(); + TransferData[] data = new TransferData[types.length]; + for (int i = 0; i < types.length; i++) { + data[i] = new TransferData(); + data[i].type = types[i]; + data[i].formatetc = new FORMATETC(); + data[i].formatetc.cfFormat = types[i]; + data[i].formatetc.dwAspect = COM.DVASPECT_CONTENT; + data[i].formatetc.lindex = -1; + data[i].formatetc.tymed = COM.TYMED_HGLOBAL; + } + return data; +} + +public boolean isSupportedType(TransferData transferData){ + if (transferData == null) return false; + int[] types = getTypeIds(); + for (int i = 0; i < types.length; i++) { + FORMATETC format = transferData.formatetc; + if (format.cfFormat == types[i] && + (format.dwAspect & COM.DVASPECT_CONTENT) == COM.DVASPECT_CONTENT && + (format.tymed & COM.TYMED_HGLOBAL) == COM.TYMED_HGLOBAL ) + return true; + } + return false; +} + +/** + * This implementation of <code>javaToNative</code> converts a java + * <code>byte[]</code> to a platform specific representation. + * + * @param object a java <code>byte[]</code> containing the data to be converted + * @param transferData an empty <code>TransferData</code> object that will + * be filled in on return with the platform specific format of the data + * + * @see Transfer#nativeToJava + */ +protected void javaToNative (Object object, TransferData transferData) { + if (!checkByteArray(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + // Allocate the memory because the caller (DropTarget) has not handed it in + // The caller of this method must release the data when it is done with it. + byte[] data = (byte[])object; + int size = data.length; + int /*long*/ newPtr = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, size); + OS.MoveMemory(newPtr, data, size); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = newPtr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform specific + * representation of a byte array to a java <code>byte[]</code>. + * + * @param transferData the platform specific representation of the data to be converted + * @return a java <code>byte[]</code> containing the converted data if the conversion was + * successful; otherwise null + * + * @see Transfer#javaToNative + */ +protected Object nativeToJava(TransferData transferData) { + if (!isSupportedType(transferData) || transferData.pIDataObject == 0) return null; + + IDataObject data = new IDataObject(transferData.pIDataObject); + data.AddRef(); + FORMATETC formatetc = transferData.formatetc; + STGMEDIUM stgmedium = new STGMEDIUM(); + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = getData(data, formatetc, stgmedium); + data.Release(); + if (transferData.result != COM.S_OK) return null; + int /*long*/ hMem = stgmedium.unionField; + int size = OS.GlobalSize(hMem); + byte[] buffer = new byte[size]; + int /*long*/ ptr = OS.GlobalLock(hMem); + OS.MoveMemory(buffer, ptr, size); + OS.GlobalUnlock(hMem); + OS.GlobalFree(hMem); + return buffer; +} + +boolean checkByteArray(Object object) { + return (object != null && object instanceof byte[] && ((byte[])object).length > 0); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/Clipboard.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/Clipboard.java new file mode 100755 index 0000000000..10a3126cef --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/Clipboard.java @@ -0,0 +1,796 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + + +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.internal.ole.win32.*; + +/** + * The <code>Clipboard</code> provides a mechanism for transferring data from one + * application to another or within an application. + * + * <p>IMPORTANT: This class is <em>not</em> intended to be subclassed.</p> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#clipboard">Clipboard snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ClipboardExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class Clipboard { + + private static final int RETRY_LIMIT = 10; + private Display display; + + // ole interfaces + private COMObject iDataObject; + private int refCount; + private Transfer[] transferAgents = new Transfer[0]; + private Object[] data = new Object[0]; + private int CFSTR_PREFERREDDROPEFFECT; + +/** + * Constructs a new instance of this class. Creating an instance of a Clipboard + * may cause system resources to be allocated depending on the platform. It is therefore + * mandatory that the Clipboard instance be disposed when no longer required. + * + * @param display the display on which to allocate the clipboard + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * + * @see Clipboard#dispose + * @see Clipboard#checkSubclass + */ +public Clipboard(Display display) { + checkSubclass (); + if (display == null) { + display = Display.getCurrent(); + if (display == null) { + display = Display.getDefault(); + } + } + if (display.getThread() != Thread.currentThread()) { + DND.error(SWT.ERROR_THREAD_INVALID_ACCESS); + } + this.display = display; + TCHAR chFormatName = new TCHAR(0, "Preferred DropEffect", true); //$NON-NLS-1$ + CFSTR_PREFERREDDROPEFFECT = OS.RegisterClipboardFormat(chFormatName); + createCOMInterfaces(); + this.AddRef(); +} + +/** + * Checks that this class can be subclassed. + * <p> + * The SWT class library is intended to be subclassed + * only at specific, controlled points. This method enforces this + * rule unless it is overridden. + * </p><p> + * <em>IMPORTANT:</em> By providing an implementation of this + * method that allows a subclass of a class which does not + * normally allow subclassing to be created, the implementer + * agrees to be fully responsible for the fact that any such + * subclass will likely fail between SWT releases and will be + * strongly platform specific. No support is provided for + * user-written classes which are implemented in this fashion. + * </p><p> + * The ability to subclass outside of the allowed SWT classes + * is intended purely to enable those not on the SWT development + * team to implement patches in order to get around specific + * limitations in advance of when those limitations can be + * addressed by the team. Subclassing should not be attempted + * without an intimate and detailed understanding of the hierarchy. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +protected void checkSubclass () { + String name = getClass().getName (); + String validName = Clipboard.class.getName(); + if (!validName.equals(name)) { + DND.error (SWT.ERROR_INVALID_SUBCLASS); + } +} + +/** + * Throws an <code>SWTException</code> if the receiver can not + * be accessed by the caller. This may include both checks on + * the state of the receiver and more generally on the entire + * execution context. This method <em>should</em> be called by + * widget implementors to enforce the standard SWT invariants. + * <p> + * Currently, it is an error to invoke any method (other than + * <code>isDisposed()</code>) on a widget that has had its + * <code>dispose()</code> method called. It is also an error + * to call widget methods from any thread that is different + * from the thread that created the widget. + * </p><p> + * In future releases of SWT, there may be more or fewer error + * checks and exceptions may be thrown for different reasons. + * </p> + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +protected void checkWidget () { + Display display = this.display; + if (display == null) DND.error (SWT.ERROR_WIDGET_DISPOSED); + if (display.getThread() != Thread.currentThread ()) DND.error (SWT.ERROR_THREAD_INVALID_ACCESS); + if (display.isDisposed()) DND.error(SWT.ERROR_WIDGET_DISPOSED); +} + +/** + * If this clipboard is currently the owner of the data on the system clipboard, + * clear the contents. If this clipboard is not the owner, then nothing is done. + * Note that there are clipboard assistant applications that take ownership of + * data or make copies of data when it is placed on the clipboard. In these + * cases, it may not be possible to clear the clipboard. + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @since 3.1 + */ +public void clearContents() { + clearContents(DND.CLIPBOARD); +} + +/** + * If this clipboard is currently the owner of the data on the specified + * clipboard, clear the contents. If this clipboard is not the owner, then + * nothing is done. + * + * <p>Note that there are clipboard assistant applications that take ownership + * of data or make copies of data when it is placed on the clipboard. In these + * cases, it may not be possible to clear the clipboard.</p> + * + * <p>The clipboards value is either one of the clipboard constants defined in + * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>DND</code> clipboard constants.</p> + * + * @param clipboards to be cleared + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DND#CLIPBOARD + * @see DND#SELECTION_CLIPBOARD + * + * @since 3.1 + */ +public void clearContents(int clipboards) { + checkWidget(); + if ((clipboards & DND.CLIPBOARD) != 0) { + /* OleIsCurrentClipboard([in] pDataObject) + * The argument pDataObject is owned by the caller so reference count does not + * need to be incremented. + */ + if (COM.OleIsCurrentClipboard(this.iDataObject.getAddress()) == COM.S_OK) { + /* OleSetClipboard([in] pDataObject) + * The argument pDataObject is owned by the caller so reference count does not + * need to be incremented. + */ + COM.OleSetClipboard(0); + } + } +} + +/** + * Disposes of the operating system resources associated with the clipboard. + * The data will still be available on the system clipboard after the dispose + * method is called. + * + * <p>NOTE: On some platforms the data will not be available once the application + * has exited or the display has been disposed.</p> + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * </ul> + */ +public void dispose () { + if (isDisposed()) return; + if (display.getThread() != Thread.currentThread()) DND.error(SWT.ERROR_THREAD_INVALID_ACCESS); + /* OleIsCurrentClipboard([in] pDataObject) + * The argument pDataObject is owned by the caller so reference count does not + * need to be incremented. + */ + if (COM.OleIsCurrentClipboard(this.iDataObject.getAddress()) == COM.S_OK) { + COM.OleFlushClipboard(); + } + this.Release(); + display = null; +} + +/** + * Retrieve the data of the specified type currently available on the system + * clipboard. Refer to the specific subclass of <code>Transfer</code> to + * determine the type of object returned. + * + * <p>The following snippet shows text and RTF text being retrieved from the + * clipboard:</p> + * + * <code><pre> + * Clipboard clipboard = new Clipboard(display); + * TextTransfer textTransfer = TextTransfer.getInstance(); + * String textData = (String)clipboard.getContents(textTransfer); + * if (textData != null) System.out.println("Text is "+textData); + * RTFTransfer rtfTransfer = RTFTransfer.getInstance(); + * String rtfData = (String)clipboard.getContents(rtfTransfer); + * if (rtfData != null) System.out.println("RTF Text is "+rtfData); + * clipboard.dispose(); + * </code></pre> + * + * @param transfer the transfer agent for the type of data being requested + * @return the data obtained from the clipboard or null if no data of this type is available + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if transfer is null</li> + * </ul> + * + * @see Transfer + */ +public Object getContents(Transfer transfer) { + return getContents(transfer, DND.CLIPBOARD); +} +/** + * Retrieve the data of the specified type currently available on the specified + * clipboard. Refer to the specific subclass of <code>Transfer</code> to + * determine the type of object returned. + * + * <p>The following snippet shows text and RTF text being retrieved from the + * clipboard:</p> + * + * <code><pre> + * Clipboard clipboard = new Clipboard(display); + * TextTransfer textTransfer = TextTransfer.getInstance(); + * String textData = (String)clipboard.getContents(textTransfer); + * if (textData != null) System.out.println("Text is "+textData); + * RTFTransfer rtfTransfer = RTFTransfer.getInstance(); + * String rtfData = (String)clipboard.getContents(rtfTransfer, DND.CLIPBOARD); + * if (rtfData != null) System.out.println("RTF Text is "+rtfData); + * clipboard.dispose(); + * </code></pre> + * + * <p>The clipboards value is either one of the clipboard constants defined in + * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>DND</code> clipboard constants.</p> + * + * @param transfer the transfer agent for the type of data being requested + * @param clipboards on which to look for data + * + * @return the data obtained from the clipboard or null if no data of this type is available + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if transfer is null</li> + * </ul> + * + * @see Transfer + * @see DND#CLIPBOARD + * @see DND#SELECTION_CLIPBOARD + * + * @since 3.1 + */ +public Object getContents(Transfer transfer, int clipboards) { + checkWidget(); + if (transfer == null) DND.error(SWT.ERROR_NULL_ARGUMENT); + if ((clipboards & DND.CLIPBOARD) == 0) return null; + /* + * Bug in Windows. When a new application takes control + * of the clipboard, other applications may open the + * clipboard to determine if they want to record the + * clipboard updates. When this happens, the clipboard + * can not be accessed until the other application is + * finished. To allow the other applications to release + * the clipboard, use PeekMessage() to enable cross thread + * message sends. + */ + int /*long*/[] ppv = new int /*long*/[1]; + int retryCount = 0; + /* OleGetClipboard([out] ppDataObject). + * AddRef has already been called on ppDataObject by the callee and must be released by the caller. + */ + int result = COM.OleGetClipboard(ppv); + while (result != COM.S_OK && retryCount++ < RETRY_LIMIT) { + try {Thread.sleep(50);} catch (Throwable t) {} + MSG msg = new MSG(); + OS.PeekMessage(msg, 0, 0, 0, OS.PM_NOREMOVE | OS.PM_NOYIELD); + result = COM.OleGetClipboard(ppv); + } + if (result != COM.S_OK) return null; + IDataObject dataObject = new IDataObject(ppv[0]); + try { + TransferData[] allowed = transfer.getSupportedTypes(); + for (int i = 0; i < allowed.length; i++) { + if (dataObject.QueryGetData(allowed[i].formatetc) == COM.S_OK) { + TransferData data = allowed[i]; + data.pIDataObject = ppv[0]; + return transfer.nativeToJava(data); + } + } + } finally { + dataObject.Release(); + } + return null; // No data available for this transfer +} +/** + * Returns <code>true</code> if the clipboard has been disposed, + * and <code>false</code> otherwise. + * <p> + * This method gets the dispose state for the clipboard. + * When a clipboard has been disposed, it is an error to + * invoke any other method using the clipboard. + * </p> + * + * @return <code>true</code> when the widget is disposed and <code>false</code> otherwise + * + * @since 3.0 + */ +public boolean isDisposed () { + return (display == null); +} + +/** + * Place data of the specified type on the system clipboard. More than one type + * of data can be placed on the system clipboard at the same time. Setting the + * data clears any previous data from the system clipboard, regardless of type. + * + * <p>NOTE: On some platforms, the data is immediately copied to the system + * clipboard but on other platforms it is provided upon request. As a result, + * if the application modifies the data object it has set on the clipboard, that + * modification may or may not be available when the data is subsequently + * requested.</p> + * + * <p>The following snippet shows text and RTF text being set on the copy/paste + * clipboard: + * </p> + * + * <code><pre> + * Clipboard clipboard = new Clipboard(display); + * String textData = "Hello World"; + * String rtfData = "{\\rtf1\\b\\i Hello World}"; + * TextTransfer textTransfer = TextTransfer.getInstance(); + * RTFTransfer rtfTransfer = RTFTransfer.getInstance(); + * Transfer[] transfers = new Transfer[]{textTransfer, rtfTransfer}; + * Object[] data = new Object[]{textData, rtfData}; + * clipboard.setContents(data, transfers); + * clipboard.dispose(); + * </code></pre> + * + * @param data the data to be set in the clipboard + * @param dataTypes the transfer agents that will convert the data to its + * platform specific format; each entry in the data array must have a + * corresponding dataType + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if data is null or datatypes is null + * or the length of data is not the same as the length of dataTypes</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_CANNOT_SET_CLIPBOARD - if the clipboard is locked or otherwise unavailable</li> + * </ul> + * + * <p>NOTE: ERROR_CANNOT_SET_CLIPBOARD should be an SWTException, since it is a + * recoverable error, but can not be changed due to backward compatibility.</p> + */ +public void setContents(Object[] data, Transfer[] dataTypes) { + setContents(data, dataTypes, DND.CLIPBOARD); +} + +/** + * Place data of the specified type on the specified clipboard. More than one + * type of data can be placed on the specified clipboard at the same time. + * Setting the data clears any previous data from the specified + * clipboard, regardless of type. + * + * <p>NOTE: On some platforms, the data is immediately copied to the specified + * clipboard but on other platforms it is provided upon request. As a result, + * if the application modifies the data object it has set on the clipboard, that + * modification may or may not be available when the data is subsequently + * requested.</p> + * + * <p>The clipboards value is either one of the clipboard constants defined in + * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>DND</code> clipboard constants.</p> + * + * <p>The following snippet shows text and RTF text being set on the copy/paste + * clipboard: + * </p> + * + * <code><pre> + * Clipboard clipboard = new Clipboard(display); + * String textData = "Hello World"; + * String rtfData = "{\\rtf1\\b\\i Hello World}"; + * TextTransfer textTransfer = TextTransfer.getInstance(); + * RTFTransfer rtfTransfer = RTFTransfer.getInstance(); + * Transfer[] transfers = new Transfer[]{textTransfer, rtfTransfer}; + * Object[] data = new Object[]{textData, rtfData}; + * clipboard.setContents(data, transfers, DND.CLIPBOARD); + * clipboard.dispose(); + * </code></pre> + * + * @param data the data to be set in the clipboard + * @param dataTypes the transfer agents that will convert the data to its + * platform specific format; each entry in the data array must have a + * corresponding dataType + * @param clipboards on which to set the data + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_INVALID_ARGUMENT - if data is null or datatypes is null + * or the length of data is not the same as the length of dataTypes</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_CANNOT_SET_CLIPBOARD - if the clipboard is locked or otherwise unavailable</li> + * </ul> + * + * <p>NOTE: ERROR_CANNOT_SET_CLIPBOARD should be an SWTException, since it is a + * recoverable error, but can not be changed due to backward compatibility.</p> + * + * @see DND#CLIPBOARD + * @see DND#SELECTION_CLIPBOARD + * + * @since 3.1 + */ +public void setContents(Object[] data, Transfer[] dataTypes, int clipboards) { + checkWidget(); + if (data == null || dataTypes == null || data.length != dataTypes.length || data.length == 0) { + DND.error(SWT.ERROR_INVALID_ARGUMENT); + } + for (int i = 0; i < data.length; i++) { + if (data[i] == null || dataTypes[i] == null || !dataTypes[i].validate(data[i])) { + DND.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + if ((clipboards & DND.CLIPBOARD) == 0) return; + this.data = data; + this.transferAgents = dataTypes; + /* OleSetClipboard([in] pDataObject) + * The argument pDataObject is owned by the caller so the reference count does not + * need to be incremented. + */ + int result = COM.OleSetClipboard(iDataObject.getAddress()); + + /* + * Bug in Windows. When a new application takes control + * of the clipboard, other applications may open the + * clipboard to determine if they want to record the + * clipboard updates. When this happens, the clipboard + * can not be flushed until the other application is + * finished. To allow other applications to get the + * data, use PeekMessage() to enable cross thread + * message sends. + */ + int retryCount = 0; + while (result != COM.S_OK && retryCount++ < RETRY_LIMIT) { + try {Thread.sleep(50);} catch (Throwable t) {} + MSG msg = new MSG(); + OS.PeekMessage(msg, 0, 0, 0, OS.PM_NOREMOVE | OS.PM_NOYIELD); + result = COM.OleSetClipboard(iDataObject.getAddress()); + } + if (result != COM.S_OK) { + DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD); + } +} +private int AddRef() { + refCount++; + return refCount; +} +private void createCOMInterfaces() { + // register each of the interfaces that this object implements + iDataObject = new COMObject(new int[]{2, 0, 0, 2, 2, 1, 2, 3, 2, 4, 1, 1}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return GetData(args[0], args[1]);} + // method4 GetDataHere - not implemented + public int /*long*/ method5(int /*long*/[] args) {return QueryGetData(args[0]);} + // method6 GetCanonicalFormatEtc - not implemented + // method7 SetData - not implemented + public int /*long*/ method8(int /*long*/[] args) {return EnumFormatEtc((int)/*64*/args[0], args[1]);} + // method9 DAdvise - not implemented + // method10 DUnadvise - not implemented + // method11 EnumDAdvise - not implemented + }; +} +private void disposeCOMInterfaces() { + if (iDataObject != null) + iDataObject.dispose(); + iDataObject = null; +} +/* + * EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc) + * Ownership of ppenumFormatetc transfers from callee to caller so reference count on ppenumFormatetc + * must be incremented before returning. Caller is responsible for releasing ppenumFormatetc. + */ +private int EnumFormatEtc(int dwDirection, int /*long*/ ppenumFormatetc) { + // only allow getting of data - SetData is not currently supported + if (dwDirection == COM.DATADIR_SET) return COM.E_NOTIMPL; + // what types have been registered? + TransferData[] allowedDataTypes = new TransferData[0]; + for (int i = 0; i < transferAgents.length; i++){ + TransferData[] formats = transferAgents[i].getSupportedTypes(); + TransferData[] newAllowedDataTypes = new TransferData[allowedDataTypes.length + formats.length]; + System.arraycopy(allowedDataTypes, 0, newAllowedDataTypes, 0, allowedDataTypes.length); + System.arraycopy(formats, 0, newAllowedDataTypes, allowedDataTypes.length, formats.length); + allowedDataTypes = newAllowedDataTypes; + } + OleEnumFORMATETC enumFORMATETC = new OleEnumFORMATETC(); + enumFORMATETC.AddRef(); + FORMATETC[] formats = new FORMATETC[allowedDataTypes.length + 1]; + for (int i = 0; i < allowedDataTypes.length; i++){ + formats[i] = allowedDataTypes[i].formatetc; + } + // include the drop effect format to specify a copy operation + FORMATETC dropeffect = new FORMATETC(); + dropeffect.cfFormat = CFSTR_PREFERREDDROPEFFECT; + dropeffect.dwAspect = COM.DVASPECT_CONTENT; + dropeffect.lindex = -1; + dropeffect.tymed = COM.TYMED_HGLOBAL; + formats[formats.length -1] = dropeffect; + enumFORMATETC.setFormats(formats); + OS.MoveMemory(ppenumFormatetc, new int /*long*/[] {enumFORMATETC.getAddress()}, OS.PTR_SIZEOF); + return COM.S_OK; +} +private int GetData(int /*long*/ pFormatetc, int /*long*/ pmedium) { + /* Called by a data consumer to obtain data from a source data object. + The GetData method renders the data described in the specified FORMATETC + structure and transfers it through the specified STGMEDIUM structure. + The caller then assumes responsibility for releasing the STGMEDIUM structure. + */ + if (pFormatetc == 0 || pmedium == 0) return COM.E_INVALIDARG; + if (QueryGetData(pFormatetc) != COM.S_OK) return COM.DV_E_FORMATETC; + + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.E_FAIL; + + if (transferData.type == CFSTR_PREFERREDDROPEFFECT) { + // specify that a copy operation is to be performed + STGMEDIUM stgmedium = new STGMEDIUM(); + stgmedium.tymed = COM.TYMED_HGLOBAL; + stgmedium.unionField = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, 4); + //TODO - should call GlobalLock + OS.MoveMemory(stgmedium.unionField, new int[] {COM.DROPEFFECT_COPY}, 4); + stgmedium.pUnkForRelease = 0; + COM.MoveMemory(pmedium, stgmedium, STGMEDIUM.sizeof); + return COM.S_OK; + } + + // get matching transfer agent to perform conversion + int transferIndex = -1; + for (int i = 0; i < transferAgents.length; i++){ + if (transferAgents[i].isSupportedType(transferData)){ + transferIndex = i; + break; + } + } + if (transferIndex == -1) return COM.DV_E_FORMATETC; + transferAgents[transferIndex].javaToNative(data[transferIndex], transferData); + COM.MoveMemory(pmedium, transferData.stgmedium, STGMEDIUM.sizeof); + return transferData.result; +} + +private int QueryGetData(int /*long*/ pFormatetc) { + if (transferAgents == null) return COM.E_FAIL; + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + if (transferData.type == CFSTR_PREFERREDDROPEFFECT) return COM.S_OK; + // is this type supported by the transfer agent? + for (int i = 0; i < transferAgents.length; i++){ + if (transferAgents[i].isSupportedType(transferData)) + return COM.S_OK; + } + + return COM.DV_E_FORMATETC; +} +/* QueryInterface([in] iid, [out] ppvObject) + * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject + * must be incremented before returning. Caller is responsible for releasing ppvObject. + */ +private int QueryInterface(int /*long*/ riid, int /*long*/ ppvObject) { + if (riid == 0 || ppvObject == 0) return COM.E_INVALIDARG; + GUID guid = new GUID(); + COM.MoveMemory(guid, riid, GUID.sizeof); + if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDataObject) ) { + OS.MoveMemory(ppvObject, new int /*long*/[] {iDataObject.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + OS.MoveMemory(ppvObject, new int /*long*/[] {0}, OS.PTR_SIZEOF); + return COM.E_NOINTERFACE; +} +private int Release() { + refCount--; + if (refCount == 0) { + this.data = new Object[0]; + this.transferAgents = new Transfer[0]; + disposeCOMInterfaces(); + COM.CoFreeUnusedLibraries(); + } + return refCount; +} + +/** + * Returns an array of the data types currently available on the system + * clipboard. Use with Transfer.isSupportedType. + * + * @return array of data types currently available on the system clipboard + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Transfer#isSupportedType + * + * @since 3.0 + */ +public TransferData[] getAvailableTypes() { + return getAvailableTypes(DND.CLIPBOARD); +} + +/** + * Returns an array of the data types currently available on the specified + * clipboard. Use with Transfer.isSupportedType. + * + * <p>The clipboards value is either one of the clipboard constants defined in + * class <code>DND</code>, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>DND</code> clipboard constants.</p> + * + * @param clipboards from which to get the data types + * @return array of data types currently available on the specified clipboard + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see Transfer#isSupportedType + * @see DND#CLIPBOARD + * @see DND#SELECTION_CLIPBOARD + * + * @since 3.1 + */ +public TransferData[] getAvailableTypes(int clipboards) { + checkWidget(); + if ((clipboards & DND.CLIPBOARD) == 0) return new TransferData[0]; + FORMATETC[] types = _getAvailableTypes(); + TransferData[] data = new TransferData[types.length]; + for (int i = 0; i < types.length; i++) { + data[i] = new TransferData(); + data[i].type = types[i].cfFormat; + data[i].formatetc = types[i]; + } + return data; +} + +/** + * Returns a platform specific list of the data types currently available on the + * system clipboard. + * + * <p>Note: <code>getAvailableTypeNames</code> is a utility for writing a Transfer + * sub-class. It should NOT be used within an application because it provides + * platform specific information.</p> + * + * @return a platform specific list of the data types currently available on the + * system clipboard + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + */ +public String[] getAvailableTypeNames() { + checkWidget(); + FORMATETC[] types = _getAvailableTypes(); + String[] names = new String[types.length]; + int maxSize = 128; + for (int i = 0; i < types.length; i++){ + TCHAR buffer = new TCHAR(0, maxSize); + int size = OS.GetClipboardFormatName(types[i].cfFormat, buffer, maxSize); + if (size != 0) { + names[i] = buffer.toString(0, size); + } else { + switch (types[i].cfFormat) { + case COM.CF_HDROP: names[i] = "CF_HDROP"; break; //$NON-NLS-1$ + case COM.CF_TEXT: names[i] = "CF_TEXT"; break; //$NON-NLS-1$ + case COM.CF_BITMAP: names[i] = "CF_BITMAP"; break; //$NON-NLS-1$ + case COM.CF_METAFILEPICT: names[i] = "CF_METAFILEPICT"; break; //$NON-NLS-1$ + case COM.CF_SYLK: names[i] = "CF_SYLK"; break; //$NON-NLS-1$ + case COM.CF_DIF: names[i] = "CF_DIF"; break; //$NON-NLS-1$ + case COM.CF_TIFF: names[i] = "CF_TIFF"; break; //$NON-NLS-1$ + case COM.CF_OEMTEXT: names[i] = "CF_OEMTEXT"; break; //$NON-NLS-1$ + case COM.CF_DIB: names[i] = "CF_DIB"; break; //$NON-NLS-1$ + case COM.CF_PALETTE: names[i] = "CF_PALETTE"; break; //$NON-NLS-1$ + case COM.CF_PENDATA: names[i] = "CF_PENDATA"; break; //$NON-NLS-1$ + case COM.CF_RIFF: names[i] = "CF_RIFF"; break; //$NON-NLS-1$ + case COM.CF_WAVE: names[i] = "CF_WAVE"; break; //$NON-NLS-1$ + case COM.CF_UNICODETEXT: names[i] = "CF_UNICODETEXT"; break; //$NON-NLS-1$ + case COM.CF_ENHMETAFILE: names[i] = "CF_ENHMETAFILE"; break; //$NON-NLS-1$ + case COM.CF_LOCALE: names[i] = "CF_LOCALE"; break; //$NON-NLS-1$ + case COM.CF_MAX: names[i] = "CF_MAX"; break; //$NON-NLS-1$ + default: names[i] = "UNKNOWN"; //$NON-NLS-1$ + } + } + } + return names; +} + +private FORMATETC[] _getAvailableTypes() { + FORMATETC[] types = new FORMATETC[0]; + int /*long*/[] ppv = new int /*long*/[1]; + /* OleGetClipboard([out] ppDataObject). + * AddRef has already been called on ppDataObject by the callee and must be released by the caller. + */ + if (COM.OleGetClipboard(ppv) != COM.S_OK) return types; + IDataObject dataObject = new IDataObject(ppv[0]); + int /*long*/[] ppFormatetc = new int /*long*/[1]; + /* EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc) + * AddRef has already been called on ppenumFormatetc by the callee and must be released by the caller. + */ + int rc = dataObject.EnumFormatEtc(COM.DATADIR_GET, ppFormatetc); + dataObject.Release(); + if (rc != COM.S_OK)return types; + IEnumFORMATETC enumFormatetc = new IEnumFORMATETC(ppFormatetc[0]); + // Loop over enumerator and save any types that match what we are looking for + int /*long*/ rgelt = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, FORMATETC.sizeof); + int[] pceltFetched = new int[1]; + enumFormatetc.Reset(); + while (enumFormatetc.Next(1, rgelt, pceltFetched) == COM.S_OK && pceltFetched[0] == 1) { + FORMATETC formatetc = new FORMATETC(); + COM.MoveMemory(formatetc, rgelt, FORMATETC.sizeof); + FORMATETC[] newTypes = new FORMATETC[types.length + 1]; + System.arraycopy(types, 0, newTypes, 0, types.length); + newTypes[types.length] = formatetc; + types = newTypes; + } + OS.GlobalFree(rgelt); + enumFormatetc.Release(); + return types; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/DragSource.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/DragSource.java new file mode 100755 index 0000000000..a7f220de1f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/DragSource.java @@ -0,0 +1,732 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; + +/** + * + * <code>DragSource</code> defines the source object for a drag and drop transfer. + * + * <p>IMPORTANT: This class is <em>not</em> intended to be subclassed.</p> + * + * <p>A drag source is the object which originates a drag and drop operation. For the specified widget, + * it defines the type of data that is available for dragging and the set of operations that can + * be performed on that data. The operations can be any bit-wise combination of DND.MOVE, DND.COPY or + * DND.LINK. The type of data that can be transferred is specified by subclasses of Transfer such as + * TextTransfer or FileTransfer. The type of data transferred can be a predefined system type or it + * can be a type defined by the application. For instructions on how to define your own transfer type, + * refer to <code>ByteArrayTransfer</code>.</p> + * + * <p>You may have several DragSources in an application but you can only have one DragSource + * per Control. Data dragged from this DragSource can be dropped on a site within this application + * or it can be dropped on another application such as an external Text editor.</p> + * + * <p>The application supplies the content of the data being transferred by implementing the + * <code>DragSourceListener</code> and associating it with the DragSource via DragSource#addDragListener.</p> + * + * <p>When a successful move operation occurs, the application is required to take the appropriate + * action to remove the data from its display and remove any associated operating system resources or + * internal references. Typically in a move operation, the drop target makes a copy of the data + * and the drag source deletes the original. However, sometimes copying the data can take a long + * time (such as copying a large file). Therefore, on some platforms, the drop target may actually + * move the data in the operating system rather than make a copy. This is usually only done in + * file transfers. In this case, the drag source is informed in the DragEnd event that a + * DROP_TARGET_MOVE was performed. It is the responsibility of the drag source at this point to clean + * up its displayed information. No action needs to be taken on the operating system resources.</p> + * + * <p> The following example shows a Label widget that allows text to be dragged from it.</p> + * + * <code><pre> + * // Enable a label as a Drag Source + * Label label = new Label(shell, SWT.NONE); + * // This example will allow text to be dragged + * Transfer[] types = new Transfer[] {TextTransfer.getInstance()}; + * // This example will allow the text to be copied or moved to the drop target + * int operations = DND.DROP_MOVE | DND.DROP_COPY; + * + * DragSource source = new DragSource(label, operations); + * source.setTransfer(types); + * source.addDragListener(new DragSourceListener() { + * public void dragStart(DragSourceEvent e) { + * // Only start the drag if there is actually text in the + * // label - this text will be what is dropped on the target. + * if (label.getText().length() == 0) { + * event.doit = false; + * } + * }; + * public void dragSetData(DragSourceEvent event) { + * // A drop has been performed, so provide the data of the + * // requested type. + * // (Checking the type of the requested data is only + * // necessary if the drag source supports more than + * // one data type but is shown here as an example). + * if (TextTransfer.getInstance().isSupportedType(event.dataType)){ + * event.data = label.getText(); + * } + * } + * public void dragFinished(DragSourceEvent event) { + * // A Move operation has been performed so remove the data + * // from the source + * if (event.detail == DND.DROP_MOVE) + * label.setText(""); + * } + * }); + * </pre></code> + * + * + * <dl> + * <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd> + * <dt><b>Events</b></dt> <dd>DND.DragStart, DND.DragSetData, DND.DragEnd</dd> + * </dl> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: DNDExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class DragSource extends Widget { + + // info for registering as a drag source + Control control; + Listener controlListener; + Transfer[] transferAgents = new Transfer[0]; + DragSourceEffect dragEffect; + Composite topControl; + int /*long*/ hwndDrag; + + // ole interfaces + COMObject iDropSource; + COMObject iDataObject; + int refCount; + + //workaround - track the operation performed by the drop target for DragEnd event + int dataEffect = DND.DROP_NONE; + + static final String DEFAULT_DRAG_SOURCE_EFFECT = "DEFAULT_DRAG_SOURCE_EFFECT"; //$NON-NLS-1$ + static final int CFSTR_PERFORMEDDROPEFFECT = Transfer.registerType("Performed DropEffect"); //$NON-NLS-1$ + static final TCHAR WindowClass = new TCHAR (0, "#32770", true); + +/** + * Creates a new <code>DragSource</code> to handle dragging from the specified <code>Control</code>. + * Creating an instance of a DragSource may cause system resources to be allocated depending on the platform. + * It is therefore mandatory that the DragSource instance be disposed when no longer required. + * + * @param control the <code>Control</code> that the user clicks on to initiate the drag + * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of + * DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_CANNOT_INIT_DRAG - unable to initiate drag source; this will occur if more than one + * drag source is created for a control or if the operating system will not allow the creation + * of the drag source</li> + * </ul> + * + * <p>NOTE: ERROR_CANNOT_INIT_DRAG should be an SWTException, since it is a + * recoverable error, but can not be changed due to backward compatibility.</p> + * + * @see Widget#dispose + * @see DragSource#checkSubclass + * @see DND#DROP_NONE + * @see DND#DROP_COPY + * @see DND#DROP_MOVE + * @see DND#DROP_LINK + */ +public DragSource(Control control, int style) { + super(control, checkStyle(style)); + this.control = control; + if (control.getData(DND.DRAG_SOURCE_KEY) != null) { + DND.error(DND.ERROR_CANNOT_INIT_DRAG); + } + control.setData(DND.DRAG_SOURCE_KEY, this); + createCOMInterfaces(); + this.AddRef(); + + controlListener = new Listener() { + public void handleEvent(Event event) { + if (event.type == SWT.Dispose) { + if (!DragSource.this.isDisposed()) { + DragSource.this.dispose(); + } + } + if (event.type == SWT.DragDetect) { + if (!DragSource.this.isDisposed()) { + DragSource.this.drag(event); + } + } + } + }; + control.addListener(SWT.Dispose, controlListener); + control.addListener(SWT.DragDetect, controlListener); + + this.addListener(SWT.Dispose, new Listener() { + public void handleEvent(Event e) { + DragSource.this.onDispose(); + } + }); + + Object effect = control.getData(DEFAULT_DRAG_SOURCE_EFFECT); + if (effect instanceof DragSourceEffect) { + dragEffect = (DragSourceEffect) effect; + } else if (control instanceof Tree) { + dragEffect = new TreeDragSourceEffect((Tree) control); + } else if (control instanceof Table) { + dragEffect = new TableDragSourceEffect((Table) control); + } +} + +static int checkStyle(int style) { + if (style == SWT.NONE) return DND.DROP_MOVE; + return style; +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when a drag and drop operation is in progress, by sending + * it one of the messages defined in the <code>DragSourceListener</code> + * interface. + * + * <p><ul> + * <li><code>dragStart</code> is called when the user has begun the actions required to drag the widget. + * This event gives the application the chance to decide if a drag should be started. + * <li><code>dragSetData</code> is called when the data is required from the drag source. + * <li><code>dragFinished</code> is called when the drop has successfully completed (mouse up + * over a valid target) or has been terminated (such as hitting the ESC key). Perform cleanup + * such as removing data from the source side on a successful move operation. + * </ul></p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DragSourceListener + * @see #getDragListeners + * @see #removeDragListener + * @see DragSourceEvent + */ +public void addDragListener(DragSourceListener listener) { + if (listener == null) DND.error(SWT.ERROR_NULL_ARGUMENT); + DNDListener typedListener = new DNDListener(listener); + typedListener.dndWidget = this; + addListener(DND.DragStart, typedListener); + addListener(DND.DragSetData, typedListener); + addListener(DND.DragEnd, typedListener); +} + +private int AddRef() { + refCount++; + return refCount; +} + +private void createCOMInterfaces() { + // register each of the interfaces that this object implements + iDropSource = new COMObject(new int[]{2, 0, 0, 2, 1}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return QueryContinueDrag((int)/*64*/args[0], (int)/*64*/args[1]);} + public int /*long*/ method4(int /*long*/[] args) {return GiveFeedback((int)/*64*/args[0]);} + }; + + iDataObject = new COMObject(new int[]{2, 0, 0, 2, 2, 1, 2, 3, 2, 4, 1, 1}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return GetData(args[0], args[1]);} + // method4 GetDataHere - not implemented + public int /*long*/ method5(int /*long*/[] args) {return QueryGetData(args[0]);} + // method6 GetCanonicalFormatEtc - not implemented + public int /*long*/ method7(int /*long*/[] args) {return SetData(args[0], args[1], (int)/*64*/args[2]);} + public int /*long*/ method8(int /*long*/[] args) {return EnumFormatEtc((int)/*64*/args[0], args[1]);} + // method9 DAdvise - not implemented + // method10 DUnadvise - not implemented + // method11 EnumDAdvise - not implemented + }; +} + +protected void checkSubclass() { + String name = getClass().getName(); + String validName = DragSource.class.getName(); + if (!validName.equals(name)) { + DND.error(SWT.ERROR_INVALID_SUBCLASS); + } +} + +private void disposeCOMInterfaces() { + if (iDropSource != null) + iDropSource.dispose(); + iDropSource = null; + + if (iDataObject != null) + iDataObject.dispose(); + iDataObject = null; +} + +private void drag(Event dragEvent) { + DNDEvent event = new DNDEvent(); + event.widget = this; + event.x = dragEvent.x; + event.y = dragEvent.y; + event.time = OS.GetMessageTime(); + event.doit = true; + notifyListeners(DND.DragStart,event); + if (!event.doit || transferAgents == null || transferAgents.length == 0 ) return; + + int[] pdwEffect = new int[1]; + int operations = opToOs(getStyle()); + Display display = control.getDisplay(); + String key = "org.eclipse.swt.internal.win32.runMessagesInIdle"; //$NON-NLS-1$ + Object oldValue = display.getData(key); + display.setData(key, new Boolean(true)); + ImageList imagelist = null; + Image image = event.image; + hwndDrag = 0; + topControl = null; + if (image != null) { + imagelist = new ImageList(SWT.NONE); + imagelist.add(image); + topControl = control.getShell(); + /* + * Bug in Windows. The image is inverted if the shell is RIGHT_TO_LEFT. + * The fix is to create a transparent window that covers the shell client + * area and use it during the drag to prevent the image from being inverted. + * On XP if the shell is RTL, the image is not displayed. + */ + int offsetX = event.offsetX; + hwndDrag = topControl.handle; + if ((topControl.getStyle() & SWT.RIGHT_TO_LEFT) != 0) { + offsetX = image.getBounds().width - offsetX; + RECT rect = new RECT (); + OS.GetClientRect (topControl.handle, rect); + hwndDrag = OS.CreateWindowEx ( + OS.WS_EX_TRANSPARENT | OS.WS_EX_NOINHERITLAYOUT, + WindowClass, + null, + OS.WS_CHILD | OS.WS_CLIPSIBLINGS, + 0, 0, + rect.right - rect.left, rect.bottom - rect.top, + topControl.handle, + 0, + OS.GetModuleHandle (null), + null); + OS.ShowWindow (hwndDrag, OS.SW_SHOW); + } + OS.ImageList_BeginDrag(imagelist.getHandle(), 0, offsetX, event.offsetY); + /* + * Feature in Windows. When ImageList_DragEnter() is called, + * it takes a snapshot of the screen If a drag is started + * when another window is in front, then the snapshot will + * contain part of the other window, causing pixel corruption. + * The fix is to force all paints to be delivered before + * calling ImageList_DragEnter(). + */ + if (OS.IsWinCE) { + OS.UpdateWindow (topControl.handle); + } else { + int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (topControl.handle, null, 0, flags); + } + POINT pt = new POINT (); + pt.x = dragEvent.x; + pt.y = dragEvent.y; + OS.MapWindowPoints (control.handle, 0, pt, 1); + RECT rect = new RECT (); + OS.GetWindowRect (hwndDrag, rect); + OS.ImageList_DragEnter(hwndDrag, pt.x - rect.left, pt.y - rect.top); + } + int result = COM.DRAGDROP_S_CANCEL; + try { + result = COM.DoDragDrop(iDataObject.getAddress(), iDropSource.getAddress(), operations, pdwEffect); + } finally { + // ensure that we don't leave transparent window around + if (hwndDrag != 0) { + OS.ImageList_DragLeave(hwndDrag); + OS.ImageList_EndDrag(); + imagelist.dispose(); + if (hwndDrag != topControl.handle) OS.DestroyWindow(hwndDrag); + hwndDrag = 0; + topControl = null; + } + display.setData(key, oldValue); + } + int operation = osToOp(pdwEffect[0]); + if (dataEffect == DND.DROP_MOVE) { + operation = (operation == DND.DROP_NONE || operation == DND.DROP_COPY) ? DND.DROP_TARGET_MOVE : DND.DROP_MOVE; + } else { + if (dataEffect != DND.DROP_NONE) { + operation = dataEffect; + } + } + event = new DNDEvent(); + event.widget = this; + event.time = OS.GetMessageTime(); + event.doit = (result == COM.DRAGDROP_S_DROP); + event.detail = operation; + notifyListeners(DND.DragEnd,event); + dataEffect = DND.DROP_NONE; +} +/* + * EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc) + * Ownership of ppenumFormatetc transfers from callee to caller so reference count on ppenumFormatetc + * must be incremented before returning. Caller is responsible for releasing ppenumFormatetc. + */ +private int EnumFormatEtc(int dwDirection, int /*long*/ ppenumFormatetc) { + // only allow getting of data - SetData is not currently supported + if (dwDirection == COM.DATADIR_SET) return COM.E_NOTIMPL; + + // what types have been registered? + TransferData[] allowedDataTypes = new TransferData[0]; + for (int i = 0; i < transferAgents.length; i++){ + Transfer transferAgent = transferAgents[i]; + if (transferAgent != null) { + TransferData[] formats = transferAgent.getSupportedTypes(); + TransferData[] newAllowedDataTypes = new TransferData[allowedDataTypes.length + formats.length]; + System.arraycopy(allowedDataTypes, 0, newAllowedDataTypes, 0, allowedDataTypes.length); + System.arraycopy(formats, 0, newAllowedDataTypes, allowedDataTypes.length, formats.length); + allowedDataTypes = newAllowedDataTypes; + } + } + + OleEnumFORMATETC enumFORMATETC = new OleEnumFORMATETC(); + enumFORMATETC.AddRef(); + + FORMATETC[] formats = new FORMATETC[allowedDataTypes.length]; + for (int i = 0; i < formats.length; i++){ + formats[i] = allowedDataTypes[i].formatetc; + } + enumFORMATETC.setFormats(formats); + + OS.MoveMemory(ppenumFormatetc, new int /*long*/[] {enumFORMATETC.getAddress()}, OS.PTR_SIZEOF); + return COM.S_OK; +} +/** + * Returns the Control which is registered for this DragSource. This is the control that the + * user clicks in to initiate dragging. + * + * @return the Control which is registered for this DragSource + */ +public Control getControl() { + return control; +} + +private int GetData(int /*long*/ pFormatetc, int /*long*/ pmedium) { + /* Called by a data consumer to obtain data from a source data object. + The GetData method renders the data described in the specified FORMATETC + structure and transfers it through the specified STGMEDIUM structure. + The caller then assumes responsibility for releasing the STGMEDIUM structure. + */ + if (pFormatetc == 0 || pmedium == 0) return COM.E_INVALIDARG; + + if (QueryGetData(pFormatetc) != COM.S_OK) return COM.DV_E_FORMATETC; + + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.E_FAIL; + + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = OS.GetMessageTime(); + event.dataType = transferData; + notifyListeners(DND.DragSetData,event); + + if (!event.doit) return COM.E_FAIL; + + // get matching transfer agent to perform conversion + Transfer transfer = null; + for (int i = 0; i < transferAgents.length; i++){ + Transfer transferAgent = transferAgents[i]; + if (transferAgent != null && transferAgent.isSupportedType(transferData)){ + transfer = transferAgent; + break; + } + } + + if (transfer == null) return COM.DV_E_FORMATETC; + transfer.javaToNative(event.data, transferData); + if (transferData.result != COM.S_OK) return transferData.result; + COM.MoveMemory(pmedium, transferData.stgmedium, STGMEDIUM.sizeof); + return transferData.result; +} + +/** + * Returns an array of listeners who will be notified when a drag and drop + * operation is in progress, by sending it one of the messages defined in + * the <code>DragSourceListener</code> interface. + * + * @return the listeners who will be notified when a drag and drop + * operation is in progress + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DragSourceListener + * @see #addDragListener + * @see #removeDragListener + * @see DragSourceEvent + * + * @since 3.4 + */ +public DragSourceListener[] getDragListeners() { + Listener[] listeners = getListeners(DND.DragStart); + int length = listeners.length; + DragSourceListener[] dragListeners = new DragSourceListener[length]; + int count = 0; + for (int i = 0; i < length; i++) { + Listener listener = listeners[i]; + if (listener instanceof DNDListener) { + dragListeners[count] = (DragSourceListener) ((DNDListener) listener).getEventListener(); + count++; + } + } + if (count == length) return dragListeners; + DragSourceListener[] result = new DragSourceListener[count]; + System.arraycopy(dragListeners, 0, result, 0, count); + return result; +} + +/** + * Returns the drag effect that is registered for this DragSource. This drag + * effect will be used during a drag and drop operation. + * + * @return the drag effect that is registered for this DragSource + * + * @since 3.3 + */ +public DragSourceEffect getDragSourceEffect() { + return dragEffect; +} + +/** + * Returns the list of data types that can be transferred by this DragSource. + * + * @return the list of data types that can be transferred by this DragSource + */ +public Transfer[] getTransfer(){ + return transferAgents; +} + +private int GiveFeedback(int dwEffect) { + return COM.DRAGDROP_S_USEDEFAULTCURSORS; +} + +private int QueryContinueDrag(int fEscapePressed, int grfKeyState) { + if (topControl != null && topControl.isDisposed()) return COM.DRAGDROP_S_CANCEL; + if (fEscapePressed != 0){ + if (hwndDrag != 0) OS.ImageList_DragLeave(hwndDrag); + return COM.DRAGDROP_S_CANCEL; + } + /* + * Bug in Windows. On some machines that do not have XBUTTONs, + * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set, + * causing mouse capture to become stuck. The fix is to test + * for the extra buttons only when they exist. + */ + int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON; +// if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2; + if ((grfKeyState & mask) == 0) { + if (hwndDrag != 0) OS.ImageList_DragLeave(hwndDrag); + return COM.DRAGDROP_S_DROP; + } + + if (hwndDrag != 0) { + POINT pt = new POINT (); + OS.GetCursorPos (pt); + RECT rect = new RECT (); + OS.GetWindowRect (hwndDrag, rect); + OS.ImageList_DragMove (pt.x - rect.left, pt.y - rect.top); + } + return COM.S_OK; +} + +private void onDispose() { + if (control == null) return; + this.Release(); + if (controlListener != null){ + control.removeListener(SWT.Dispose, controlListener); + control.removeListener(SWT.DragDetect, controlListener); + } + controlListener = null; + control.setData(DND.DRAG_SOURCE_KEY, null); + control = null; + transferAgents = null; +} + +private int opToOs(int operation) { + int osOperation = 0; + if ((operation & DND.DROP_COPY) != 0){ + osOperation |= COM.DROPEFFECT_COPY; + } + if ((operation & DND.DROP_LINK) != 0) { + osOperation |= COM.DROPEFFECT_LINK; + } + if ((operation & DND.DROP_MOVE) != 0) { + osOperation |= COM.DROPEFFECT_MOVE; + } + return osOperation; +} + +private int osToOp(int osOperation){ + int operation = 0; + if ((osOperation & COM.DROPEFFECT_COPY) != 0){ + operation |= DND.DROP_COPY; + } + if ((osOperation & COM.DROPEFFECT_LINK) != 0) { + operation |= DND.DROP_LINK; + } + if ((osOperation & COM.DROPEFFECT_MOVE) != 0) { + operation |= DND.DROP_MOVE; + } + return operation; +} + +private int QueryGetData(int /*long*/ pFormatetc) { + if (transferAgents == null) return COM.E_FAIL; + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + + // is this type supported by the transfer agent? + for (int i = 0; i < transferAgents.length; i++){ + Transfer transfer = transferAgents[i]; + if (transfer != null && transfer.isSupportedType(transferData)) + return COM.S_OK; + } + + return COM.DV_E_FORMATETC; +} + +/* QueryInterface([in] riid, [out] ppvObject) + * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject + * must be incremented before returning. Caller is responsible for releasing ppvObject. + */ +private int QueryInterface(int /*long*/ riid, int /*long*/ ppvObject) { + if (riid == 0 || ppvObject == 0) + return COM.E_INVALIDARG; + GUID guid = new GUID(); + COM.MoveMemory(guid, riid, GUID.sizeof); + + if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDropSource)) { + OS.MoveMemory(ppvObject, new int /*long*/[] {iDropSource.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + + if (COM.IsEqualGUID(guid, COM.IIDIDataObject) ) { + OS.MoveMemory(ppvObject, new int /*long*/[] {iDataObject.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + + OS.MoveMemory(ppvObject, new int /*long*/[] {0}, OS.PTR_SIZEOF); + return COM.E_NOINTERFACE; +} + +private int Release() { + refCount--; + if (refCount == 0) { + disposeCOMInterfaces(); + COM.CoFreeUnusedLibraries(); + } + return refCount; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when a drag and drop operation is in progress. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DragSourceListener + * @see #addDragListener + * @see #getDragListeners + */ +public void removeDragListener(DragSourceListener listener) { + if (listener == null) DND.error(SWT.ERROR_NULL_ARGUMENT); + removeListener(DND.DragStart, listener); + removeListener(DND.DragSetData, listener); + removeListener(DND.DragEnd, listener); +} + +private int SetData(int /*long*/ pFormatetc, int /*long*/ pmedium, int fRelease) { + if (pFormatetc == 0 || pmedium == 0) return COM.E_INVALIDARG; + FORMATETC formatetc = new FORMATETC(); + COM.MoveMemory(formatetc, pFormatetc, FORMATETC.sizeof); + if (formatetc.cfFormat == CFSTR_PERFORMEDDROPEFFECT && formatetc.tymed == COM.TYMED_HGLOBAL) { + STGMEDIUM stgmedium = new STGMEDIUM(); + COM.MoveMemory(stgmedium, pmedium,STGMEDIUM.sizeof); + //TODO - this should be GlobalLock() + int /*long*/[] ptrEffect = new int /*long*/[1]; + OS.MoveMemory(ptrEffect, stgmedium.unionField, OS.PTR_SIZEOF); + int[] effect = new int[1]; + OS.MoveMemory(effect, ptrEffect[0], 4); + dataEffect = osToOp(effect[0]); + } + if (fRelease == 1) { + COM.ReleaseStgMedium(pmedium); + } + return COM.S_OK; +} + +/** + * Specifies the drag effect for this DragSource. This drag effect will be + * used during a drag and drop operation. + * + * @param effect the drag effect that is registered for this DragSource + * + * @since 3.3 + */ +public void setDragSourceEffect(DragSourceEffect effect) { + dragEffect = effect; +} + +/** + * Specifies the list of data types that can be transferred by this DragSource. + * The application must be able to provide data to match each of these types when + * a successful drop has occurred. + * + * @param transferAgents a list of Transfer objects which define the types of data that can be + * dragged from this source + */ +public void setTransfer(Transfer[] transferAgents){ + this.transferAgents = transferAgents; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/DropTarget.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/DropTarget.java new file mode 100755 index 0000000000..f8720f83fa --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/DropTarget.java @@ -0,0 +1,781 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; + +/** + * + * Class <code>DropTarget</code> defines the target object for a drag and drop transfer. + * + * <p>IMPORTANT: This class is <em>not</em> intended to be subclassed.</p> + * + * <p>This class identifies the <code>Control</code> over which the user must position the cursor + * in order to drop the data being transferred. It also specifies what data types can be dropped on + * this control and what operations can be performed. You may have several DropTragets in an + * application but there can only be a one to one mapping between a <code>Control</code> and a <code>DropTarget</code>. + * The DropTarget can receive data from within the same application or from other applications + * (such as text dragged from a text editor like Word).</p> + * + * <code><pre> + * int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK; + * Transfer[] types = new Transfer[] {TextTransfer.getInstance()}; + * DropTarget target = new DropTarget(label, operations); + * target.setTransfer(types); + * </code></pre> + * + * <p>The application is notified of data being dragged over this control and of when a drop occurs by + * implementing the interface <code>DropTargetListener</code> which uses the class + * <code>DropTargetEvent</code>. The application can modify the type of drag being performed + * on this Control at any stage of the drag by modifying the <code>event.detail</code> field or the + * <code>event.currentDataType</code> field. When the data is dropped, it is the responsibility of + * the application to copy this data for its own purposes. + * + * <code><pre> + * target.addDropListener (new DropTargetListener() { + * public void dragEnter(DropTargetEvent event) {}; + * public void dragOver(DropTargetEvent event) {}; + * public void dragLeave(DropTargetEvent event) {}; + * public void dragOperationChanged(DropTargetEvent event) {}; + * public void dropAccept(DropTargetEvent event) {} + * public void drop(DropTargetEvent event) { + * // A drop has occurred, copy over the data + * if (event.data == null) { // no data to copy, indicate failure in event.detail + * event.detail = DND.DROP_NONE; + * return; + * } + * label.setText ((String) event.data); // data copied to label text + * } + * }); + * </pre></code> + * + * <dl> + * <dt><b>Styles</b></dt> <dd>DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK</dd> + * <dt><b>Events</b></dt> <dd>DND.DragEnter, DND.DragLeave, DND.DragOver, DND.DragOperationChanged, + * DND.DropAccept, DND.Drop </dd> + * </dl> + * + * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: DNDExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * @noextend This class is not intended to be subclassed by clients. + */ +public class DropTarget extends Widget { + + Control control; + Listener controlListener; + Transfer[] transferAgents = new Transfer[0]; + DropTargetEffect dropEffect; + + // Track application selections + TransferData selectedDataType; + int selectedOperation; + + // workaround - There is no event for "operation changed" so track operation based on key state + int keyOperation = -1; + + // workaround - The dataobject address is only passed as an argument in drag enter and drop. + // To allow applications to query the data values during the drag over operations, + // maintain a reference to it. + IDataObject iDataObject; + + // interfaces + COMObject iDropTarget; + int refCount; + + static final String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$ + +/** + * Creates a new <code>DropTarget</code> to allow data to be dropped on the specified + * <code>Control</code>. + * Creating an instance of a DropTarget may cause system resources to be allocated + * depending on the platform. It is therefore mandatory that the DropTarget instance + * be disposed when no longer required. + * + * @param control the <code>Control</code> over which the user positions the cursor to drop the data + * @param style the bitwise OR'ing of allowed operations; this may be a combination of any of + * DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK + * + * @exception SWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + * @exception SWTError <ul> + * <li>ERROR_CANNOT_INIT_DROP - unable to initiate drop target; this will occur if more than one + * drop target is created for a control or if the operating system will not allow the creation + * of the drop target</li> + * </ul> + * + * <p>NOTE: ERROR_CANNOT_INIT_DROP should be an SWTException, since it is a + * recoverable error, but can not be changed due to backward compatibility.</p> + * + * @see Widget#dispose + * @see DropTarget#checkSubclass + * @see DND#DROP_NONE + * @see DND#DROP_COPY + * @see DND#DROP_MOVE + * @see DND#DROP_LINK + */ +public DropTarget(Control control, int style) { + super (control, checkStyle(style)); + this.control = control; + if (control.getData(DND.DROP_TARGET_KEY) != null) { + DND.error(DND.ERROR_CANNOT_INIT_DROP); + } + control.setData(DND.DROP_TARGET_KEY, this); + createCOMInterfaces(); + this.AddRef(); + + if (COM.CoLockObjectExternal(iDropTarget.getAddress(), true, true) != COM.S_OK) + DND.error(DND.ERROR_CANNOT_INIT_DROP); + if (COM.RegisterDragDrop( control.handle, iDropTarget.getAddress()) != COM.S_OK) + DND.error(DND.ERROR_CANNOT_INIT_DROP); + + controlListener = new Listener () { + public void handleEvent (Event event) { + if (!DropTarget.this.isDisposed()){ + DropTarget.this.dispose(); + } + } + }; + control.addListener (SWT.Dispose, controlListener); + + this.addListener(SWT.Dispose, new Listener () { + public void handleEvent (Event event) { + onDispose(); + } + }); + + Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT); + if (effect instanceof DropTargetEffect) { + dropEffect = (DropTargetEffect) effect; + } else if (control instanceof Table) { + dropEffect = new TableDropTargetEffect((Table) control); + } else if (control instanceof Tree) { + dropEffect = new TreeDropTargetEffect((Tree) control); + } +} + +static int checkStyle (int style) { + if (style == SWT.NONE) return DND.DROP_MOVE; + return style; +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when a drag and drop operation is in progress, by sending + * it one of the messages defined in the <code>DropTargetListener</code> + * interface. + * + * <p><ul> + * <li><code>dragEnter</code> is called when the cursor has entered the drop target boundaries + * <li><code>dragLeave</code> is called when the cursor has left the drop target boundaries and just before + * the drop occurs or is cancelled. + * <li><code>dragOperationChanged</code> is called when the operation being performed has changed + * (usually due to the user changing the selected modifier key(s) while dragging) + * <li><code>dragOver</code> is called when the cursor is moving over the drop target + * <li><code>dropAccept</code> is called just before the drop is performed. The drop target is given + * the chance to change the nature of the drop or veto the drop by setting the <code>event.detail</code> field + * <li><code>drop</code> is called when the data is being dropped + * </ul></p> + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DropTargetListener + * @see #getDropListeners + * @see #removeDropListener + * @see DropTargetEvent + */ +public void addDropListener(DropTargetListener listener) { + if (listener == null) DND.error (SWT.ERROR_NULL_ARGUMENT); + DNDListener typedListener = new DNDListener (listener); + typedListener.dndWidget = this; + addListener (DND.DragEnter, typedListener); + addListener (DND.DragLeave, typedListener); + addListener (DND.DragOver, typedListener); + addListener (DND.DragOperationChanged, typedListener); + addListener (DND.Drop, typedListener); + addListener (DND.DropAccept, typedListener); +} + +int AddRef() { + refCount++; + return refCount; +} + +protected void checkSubclass () { + String name = getClass().getName (); + String validName = DropTarget.class.getName(); + if (!validName.equals(name)) { + DND.error (SWT.ERROR_INVALID_SUBCLASS); + } +} + +void createCOMInterfaces() { + // register each of the interfaces that this object implements + boolean is32 = C.PTR_SIZEOF == 4; + iDropTarget = new COMObject(new int[]{2, 0, 0, is32 ? 5 : 4, is32 ? 4 : 3, 0, is32 ? 5 : 4}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) { + if (args.length == 5) { + return DragEnter(args[0], (int)/*64*/args[1], (int)/*64*/args[2], (int)/*64*/args[3], args[4]); + } else { + return DragEnter_64(args[0], (int)/*64*/args[1], args[2], args[3]); + } + } + public int /*long*/ method4(int /*long*/[] args) { + if (args.length == 4) { + return DragOver((int)/*64*/args[0], (int)/*64*/args[1], (int)/*64*/args[2], args[3]); + } else { + return DragOver_64((int)/*64*/args[0], args[1], args[2]); + } + } + public int /*long*/ method5(int /*long*/[] args) {return DragLeave();} + public int /*long*/ method6(int /*long*/[] args) { + if (args.length == 5) { + return Drop(args[0], (int)/*64*/args[1], (int)/*64*/args[2], (int)/*64*/args[3], args[4]); + } else { + return Drop_64(args[0], (int)/*64*/args[1], args[2], args[3]); + } + } + }; +} + +void disposeCOMInterfaces() { + if (iDropTarget != null) + iDropTarget.dispose(); + iDropTarget = null; +} + +int DragEnter_64(int /*long*/ pDataObject, int grfKeyState, long pt, int /*long*/ pdwEffect) { + POINT point = new POINT(); + OS.MoveMemory(point, new long[]{pt}, 8); + return DragEnter(pDataObject, grfKeyState, point.x, point.y, pdwEffect); +} + +int DragEnter(int /*long*/ pDataObject, int grfKeyState, int pt_x, int pt_y, int /*long*/ pdwEffect) { + selectedDataType = null; + selectedOperation = DND.DROP_NONE; + if (iDataObject != null) iDataObject.Release(); + iDataObject = null; + + DNDEvent event = new DNDEvent(); + if (!setEventData(event, pDataObject, grfKeyState, pt_x, pt_y, pdwEffect)) { + OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4); + return COM.S_FALSE; + } + + // Remember the iDataObject because it is not passed into the DragOver callback + iDataObject = new IDataObject(pDataObject); + iDataObject.AddRef(); + + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + notifyListeners(DND.DragEnter, event); + refresh(); + if (event.detail == DND.DROP_DEFAULT) { + event.detail = (allowedOperations & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + + selectedDataType = null; + for (int i = 0; i < allowedDataTypes.length; i++) { + if (TransferData.sameType(allowedDataTypes[i], event.dataType)){ + selectedDataType = allowedDataTypes[i]; + break; + } + } + + selectedOperation = DND.DROP_NONE; + if (selectedDataType != null && ((allowedOperations & event.detail) != 0)) { + selectedOperation = event.detail; + } + + OS.MoveMemory(pdwEffect, new int[] {opToOs(selectedOperation)}, 4); + return COM.S_OK; +} + +int DragLeave() { + keyOperation = -1; + + if (iDataObject == null) return COM.S_FALSE; + + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = OS.GetMessageTime(); + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragLeave, event); + refresh(); + + iDataObject.Release(); + iDataObject = null; + return COM.S_OK; +} + +int DragOver_64(int grfKeyState, long pt, int /*long*/ pdwEffect) { + POINT point = new POINT(); + OS.MoveMemory(point, new long[]{pt}, 8); + return DragOver(grfKeyState, point.x, point.y, pdwEffect); +} + +int DragOver(int grfKeyState, int pt_x, int pt_y, int /*long*/ pdwEffect) { + if (iDataObject == null) return COM.S_FALSE; + int oldKeyOperation = keyOperation; + + DNDEvent event = new DNDEvent(); + if (!setEventData(event, iDataObject.getAddress(), grfKeyState, pt_x, pt_y, pdwEffect)) { + keyOperation = -1; + OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4); + return COM.S_FALSE; + } + + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + + if (keyOperation == oldKeyOperation) { + event.type = DND.DragOver; + event.dataType = selectedDataType; + event.detail = selectedOperation; + } else { + event.type = DND.DragOperationChanged; + event.dataType = selectedDataType; + } + notifyListeners(event.type, event); + refresh(); + if (event.detail == DND.DROP_DEFAULT) { + event.detail = (allowedOperations & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + + selectedDataType = null; + for (int i = 0; i < allowedDataTypes.length; i++) { + if (TransferData.sameType(allowedDataTypes[i], event.dataType)){ + selectedDataType = allowedDataTypes[i]; + break; + } + } + + selectedOperation = DND.DROP_NONE; + if (selectedDataType != null && ((allowedOperations & event.detail) == event.detail)) { + selectedOperation = event.detail; + } + + OS.MoveMemory(pdwEffect, new int[] {opToOs(selectedOperation)}, 4); + return COM.S_OK; +} + +int Drop_64(int /*long*/ pDataObject, int grfKeyState, long pt, int /*long*/ pdwEffect) { + POINT point = new POINT(); + OS.MoveMemory(point, new long[]{pt}, 8); + return Drop(pDataObject, grfKeyState, point.x, point.y, pdwEffect); +} + +int Drop(int /*long*/ pDataObject, int grfKeyState, int pt_x, int pt_y, int /*long*/ pdwEffect) { + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = OS.GetMessageTime(); + if (dropEffect != null) { + event.item = dropEffect.getItem(pt_x, pt_y); + } + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragLeave, event); + refresh(); + + event = new DNDEvent(); + if (!setEventData(event, pDataObject, grfKeyState, pt_x, pt_y, pdwEffect)) { + keyOperation = -1; + OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4); + return COM.S_FALSE; + } + keyOperation = -1; + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + event.dataType = selectedDataType; + event.detail = selectedOperation; + notifyListeners(DND.DropAccept,event); + refresh(); + + selectedDataType = null; + for (int i = 0; i < allowedDataTypes.length; i++) { + if (TransferData.sameType(allowedDataTypes[i], event.dataType)){ + selectedDataType = allowedDataTypes[i]; + break; + } + } + selectedOperation = DND.DROP_NONE; + if (selectedDataType != null && (allowedOperations & event.detail) == event.detail) { + selectedOperation = event.detail; + } + + if (selectedOperation == DND.DROP_NONE){ + OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4); + return COM.S_OK; + } + + // Get Data in a Java format + Object object = null; + for (int i = 0; i < transferAgents.length; i++){ + Transfer transfer = transferAgents[i]; + if (transfer != null && transfer.isSupportedType(selectedDataType)){ + object = transfer.nativeToJava(selectedDataType); + break; + } + } + if (object == null){ + selectedOperation = DND.DROP_NONE; + } + + event.detail = selectedOperation; + event.dataType = selectedDataType; + event.data = object; + OS.ImageList_DragShowNolock(false); + try { + notifyListeners(DND.Drop,event); + } finally { + OS.ImageList_DragShowNolock(true); + } + refresh(); + selectedOperation = DND.DROP_NONE; + if ((allowedOperations & event.detail) == event.detail) { + selectedOperation = event.detail; + } + //notify source of action taken + OS.MoveMemory(pdwEffect, new int[] {opToOs(selectedOperation)}, 4); + return COM.S_OK; +} + +/** + * Returns the Control which is registered for this DropTarget. This is the control over which the + * user positions the cursor to drop the data. + * + * @return the Control which is registered for this DropTarget + */ +public Control getControl () { + return control; +} + +/** + * Returns an array of listeners who will be notified when a drag and drop + * operation is in progress, by sending it one of the messages defined in + * the <code>DropTargetListener</code> interface. + * + * @return the listeners who will be notified when a drag and drop + * operation is in progress + * + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DropTargetListener + * @see #addDropListener + * @see #removeDropListener + * @see DropTargetEvent + * + * @since 3.4 + */ +public DropTargetListener[] getDropListeners() { + Listener[] listeners = getListeners(DND.DragEnter); + int length = listeners.length; + DropTargetListener[] dropListeners = new DropTargetListener[length]; + int count = 0; + for (int i = 0; i < length; i++) { + Listener listener = listeners[i]; + if (listener instanceof DNDListener) { + dropListeners[count] = (DropTargetListener) ((DNDListener) listener).getEventListener(); + count++; + } + } + if (count == length) return dropListeners; + DropTargetListener[] result = new DropTargetListener[count]; + System.arraycopy(dropListeners, 0, result, 0, count); + return result; +} + +/** + * Returns the drop effect for this DropTarget. This drop effect will be + * used during a drag and drop to display the drag under effect on the + * target widget. + * + * @return the drop effect that is registered for this DropTarget + * + * @since 3.3 + */ +public DropTargetEffect getDropTargetEffect() { + return dropEffect; +} + +int getOperationFromKeyState(int grfKeyState) { + boolean ctrl = (grfKeyState & OS.MK_CONTROL) != 0; + boolean shift = (grfKeyState & OS.MK_SHIFT) != 0; + boolean alt = (grfKeyState & OS.MK_ALT) != 0; + if (alt) { + if (ctrl || shift) return DND.DROP_DEFAULT; + return DND.DROP_LINK; + } + if (ctrl && shift) return DND.DROP_LINK; + if (ctrl)return DND.DROP_COPY; + if (shift)return DND.DROP_MOVE; + return DND.DROP_DEFAULT; +} + +/** + * Returns a list of the data types that can be transferred to this DropTarget. + * + * @return a list of the data types that can be transferred to this DropTarget + */ +public Transfer[] getTransfer() { + return transferAgents; +} + +void onDispose () { + if (control == null) return; + + COM.RevokeDragDrop(control.handle); + + if (controlListener != null) + control.removeListener(SWT.Dispose, controlListener); + controlListener = null; + control.setData(DND.DROP_TARGET_KEY, null); + transferAgents = null; + control = null; + + COM.CoLockObjectExternal(iDropTarget.getAddress(), false, true); + + this.Release(); + + COM.CoFreeUnusedLibraries(); +} + +int opToOs(int operation) { + int osOperation = 0; + if ((operation & DND.DROP_COPY) != 0){ + osOperation |= COM.DROPEFFECT_COPY; + } + if ((operation & DND.DROP_LINK) != 0) { + osOperation |= COM.DROPEFFECT_LINK; + } + if ((operation & DND.DROP_MOVE) != 0) { + osOperation |= COM.DROPEFFECT_MOVE; + } + return osOperation; +} + +int osToOp(int osOperation){ + int operation = 0; + if ((osOperation & COM.DROPEFFECT_COPY) != 0){ + operation |= DND.DROP_COPY; + } + if ((osOperation & COM.DROPEFFECT_LINK) != 0) { + operation |= DND.DROP_LINK; + } + if ((osOperation & COM.DROPEFFECT_MOVE) != 0) { + operation |= DND.DROP_MOVE; + } + return operation; +} + +/* QueryInterface([in] iid, [out] ppvObject) + * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject + * must be incremented before returning. Caller is responsible for releasing ppvObject. + */ +int QueryInterface(int /*long*/ riid, int /*long*/ ppvObject) { + + if (riid == 0 || ppvObject == 0) + return COM.E_INVALIDARG; + GUID guid = new GUID(); + COM.MoveMemory(guid, riid, GUID.sizeof); + if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDropTarget)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iDropTarget.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + + COM.MoveMemory(ppvObject, new int /*long*/[] {0}, OS.PTR_SIZEOF); + return COM.E_NOINTERFACE; +} + +int Release() { + refCount--; + + if (refCount == 0) { + disposeCOMInterfaces(); + COM.CoFreeUnusedLibraries(); + } + + return refCount; +} + +void refresh() { + if (control == null || control.isDisposed()) return; + int /*long*/ handle = control.handle; + RECT lpRect = new RECT(); + if (OS.GetUpdateRect(handle, lpRect, false)) { + OS.ImageList_DragShowNolock(false); + OS.RedrawWindow(handle, lpRect, 0, OS.RDW_UPDATENOW | OS.RDW_INVALIDATE); + OS.ImageList_DragShowNolock(true); + } +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when a drag and drop operation is in progress. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see DropTargetListener + * @see #addDropListener + * @see #getDropListeners + */ +public void removeDropListener(DropTargetListener listener) { + if (listener == null) DND.error (SWT.ERROR_NULL_ARGUMENT); + removeListener (DND.DragEnter, listener); + removeListener (DND.DragLeave, listener); + removeListener (DND.DragOver, listener); + removeListener (DND.DragOperationChanged, listener); + removeListener (DND.Drop, listener); + removeListener (DND.DropAccept, listener); +} + +/** + * Specifies the drop effect for this DropTarget. This drop effect will be + * used during a drag and drop to display the drag under effect on the + * target widget. + * + * @param effect the drop effect that is registered for this DropTarget + * + * @since 3.3 + */ +public void setDropTargetEffect(DropTargetEffect effect) { + dropEffect = effect; +} + +boolean setEventData(DNDEvent event, int /*long*/ pDataObject, int grfKeyState, int pt_x, int pt_y, int /*long*/ pdwEffect) { + if (pDataObject == 0 || pdwEffect == 0) return false; + + // get allowed operations + int style = getStyle(); + int[] operations = new int[1]; + OS.MoveMemory(operations, pdwEffect, 4); + operations[0] = osToOp(operations[0]) & style; + if (operations[0] == DND.DROP_NONE) return false; + + // get current operation + int operation = getOperationFromKeyState(grfKeyState); + keyOperation = operation; + if (operation == DND.DROP_DEFAULT) { + if ((style & DND.DROP_DEFAULT) == 0) { + operation = (operations[0] & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + } else { + if ((operation & operations[0]) == 0) operation = DND.DROP_NONE; + } + + // Get allowed transfer types + TransferData[] dataTypes = new TransferData[0]; + IDataObject dataObject = new IDataObject(pDataObject); + dataObject.AddRef(); + try { + int /*long*/[] address = new int /*long*/[1]; + if (dataObject.EnumFormatEtc(COM.DATADIR_GET, address) != COM.S_OK) { + return false; + } + IEnumFORMATETC enumFormatetc = new IEnumFORMATETC(address[0]); + try { + // Loop over enumerator and save any types that match what we are looking for + int /*long*/ rgelt = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, FORMATETC.sizeof); + try { + int[] pceltFetched = new int[1]; + enumFormatetc.Reset(); + while (enumFormatetc.Next(1, rgelt, pceltFetched) == COM.S_OK && pceltFetched[0] == 1) { + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, rgelt, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + transferData.pIDataObject = pDataObject; + for (int i = 0; i < transferAgents.length; i++){ + Transfer transfer = transferAgents[i]; + if (transfer != null && transfer.isSupportedType(transferData)){ + TransferData[] newDataTypes = new TransferData[dataTypes.length + 1]; + System.arraycopy(dataTypes, 0, newDataTypes, 0, dataTypes.length); + newDataTypes[dataTypes.length] = transferData; + dataTypes = newDataTypes; + break; + } + } + } + } finally { + OS.GlobalFree(rgelt); + } + } finally { + enumFormatetc.Release(); + } + } finally { + dataObject.Release(); + } + if (dataTypes.length == 0) return false; + + event.widget = this; + event.x = pt_x; + event.y = pt_y; + event.time = OS.GetMessageTime(); + event.feedback = DND.FEEDBACK_SELECT; + event.dataTypes = dataTypes; + event.dataType = dataTypes[0]; + if (dropEffect != null) { + event.item = dropEffect.getItem(pt_x, pt_y); + } + event.operations = operations[0]; + event.detail = operation; + return true; +} + +/** + * Specifies the data types that can be transferred to this DropTarget. If data is + * being dragged that does not match one of these types, the drop target will be notified of + * the drag and drop operation but the currentDataType will be null and the operation + * will be DND.NONE. + * + * @param transferAgents a list of Transfer objects which define the types of data that can be + * dropped on this target + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if transferAgents is null</li> + * </ul> + */ +public void setTransfer(Transfer[] transferAgents){ + if (transferAgents == null) DND.error(SWT.ERROR_NULL_ARGUMENT); + this.transferAgents = transferAgents; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/FileTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/FileTransfer.java new file mode 100755 index 0000000000..8acb6456d1 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/FileTransfer.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; + +/** + * The class <code>FileTransfer</code> provides a platform specific mechanism + * for converting a list of files represented as a java <code>String[]</code> to a + * platform specific representation of the data and vice versa. + * Each <code>String</code> in the array contains the absolute path for a single + * file or directory. + * + * <p>An example of a java <code>String[]</code> containing a list of files is shown + * below:</p> + * + * <code><pre> + * File file1 = new File("C:\temp\file1"); + * File file2 = new File("C:\temp\file2"); + * String[] fileData = new String[2]; + * fileData[0] = file1.getAbsolutePath(); + * fileData[1] = file2.getAbsolutePath(); + * </code></pre> + * + * @see Transfer + */ +public class FileTransfer extends ByteArrayTransfer { + + private static FileTransfer _instance = new FileTransfer(); + private static final String CF_HDROP = "CF_HDROP "; //$NON-NLS-1$ + private static final int CF_HDROPID = COM.CF_HDROP; + private static final String CF_HDROP_SEPARATOR = "\0"; //$NON-NLS-1$ + +private FileTransfer() {} + +/** + * Returns the singleton instance of the FileTransfer class. + * + * @return the singleton instance of the FileTransfer class + */ +public static FileTransfer getInstance () { + return _instance; +} + +/** + * This implementation of <code>javaToNative</code> converts a list of file names + * represented by a java <code>String[]</code> to a platform specific representation. + * Each <code>String</code> in the array contains the absolute path for a single + * file or directory. + * + * @param object a java <code>String[]</code> containing the file names to be converted + * @param transferData an empty <code>TransferData</code> object that will + * be filled in on return with the platform specific format of the data + * + * @see Transfer#nativeToJava + */ +public void javaToNative(Object object, TransferData transferData) { + if (!checkFile(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + String[] fileNames = (String[]) object; + StringBuffer allFiles = new StringBuffer(); + for (int i = 0; i < fileNames.length; i++) { + allFiles.append(fileNames[i]); + allFiles.append(CF_HDROP_SEPARATOR); // each name is null terminated + } + TCHAR buffer = new TCHAR(0, allFiles.toString(), true); // there is an extra null terminator at the very end + DROPFILES dropfiles = new DROPFILES(); + dropfiles.pFiles = DROPFILES.sizeof; + dropfiles.pt_x = dropfiles.pt_y = 0; + dropfiles.fNC = 0; + dropfiles.fWide = OS.IsUnicode ? 1 : 0; + // Allocate the memory because the caller (DropTarget) has not handed it in + // The caller of this method must release the data when it is done with it. + int byteCount = buffer.length() * TCHAR.sizeof; + int /*long*/ newPtr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, DROPFILES.sizeof + byteCount); + OS.MoveMemory(newPtr, dropfiles, DROPFILES.sizeof); + OS.MoveMemory(newPtr + DROPFILES.sizeof, buffer, byteCount); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = newPtr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform specific + * representation of a list of file names to a java <code>String[]</code>. + * Each String in the array contains the absolute path for a single file or directory. + * + * @param transferData the platform specific representation of the data to be converted + * @return a java <code>String[]</code> containing a list of file names if the conversion + * was successful; otherwise null + * + * @see Transfer#javaToNative + */ +public Object nativeToJava(TransferData transferData) { + if (!isSupportedType(transferData) || transferData.pIDataObject == 0) return null; + + // get file names from IDataObject + IDataObject dataObject = new IDataObject(transferData.pIDataObject); + dataObject.AddRef(); + FORMATETC formatetc = new FORMATETC(); + formatetc.cfFormat = COM.CF_HDROP; + formatetc.ptd = 0; + formatetc.dwAspect = COM.DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.tymed = COM.TYMED_HGLOBAL; + STGMEDIUM stgmedium = new STGMEDIUM(); + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = getData(dataObject, formatetc, stgmedium); + dataObject.Release(); + if (transferData.result != COM.S_OK) return null; + // How many files are there? + int count = OS.DragQueryFile(stgmedium.unionField, 0xFFFFFFFF, null, 0); + String[] fileNames = new String[count]; + for (int i = 0; i < count; i++) { + // How long is the name ? + int size = OS.DragQueryFile(stgmedium.unionField, i, null, 0) + 1; + TCHAR lpszFile = new TCHAR(0, size); + // Get file name and append it to string + OS.DragQueryFile(stgmedium.unionField, i, lpszFile, size); + fileNames[i] = lpszFile.toString(0, lpszFile.strlen()); + } + OS.DragFinish(stgmedium.unionField); // frees data associated with HDROP data + return fileNames; +} + +protected int[] getTypeIds(){ + return new int[] {CF_HDROPID}; +} + +protected String[] getTypeNames(){ + return new String[] {CF_HDROP}; +} +boolean checkFile(Object object) { + if (object == null || !(object instanceof String[]) || ((String[])object).length == 0) return false; + String[] strings = (String[])object; + for (int i = 0; i < strings.length; i++) { + if (strings[i] == null || strings[i].length() == 0) return false; + } + return true; +} + +protected boolean validate(Object object) { + return checkFile(object); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/HTMLTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/HTMLTransfer.java new file mode 100644 index 0000000000..9664d49d67 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/HTMLTransfer.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; + +/** + * The class <code>HTMLTransfer</code> provides a platform specific mechanism + * for converting text in HTML format represented as a java <code>String</code> + * to a platform specific representation of the data and vice versa. + * + * <p>An example of a java <code>String</code> containing HTML text is shown + * below:</p> + * + * <code><pre> + * String htmlData = "<p>This is a paragraph of text.</p>"; + * </code></pre> + * + * @see Transfer + */ +public class HTMLTransfer extends ByteArrayTransfer { + + static HTMLTransfer _instance = new HTMLTransfer(); + static final String HTML_FORMAT = "HTML Format"; //$NON-NLS-1$ + static final int HTML_FORMATID = registerType(HTML_FORMAT); + static final String NUMBER = "00000000"; //$NON-NLS-1$ + static final String HEADER = "Version:0.9\r\nStartHTML:"+NUMBER+"\r\nEndHTML:"+NUMBER+"\r\nStartFragment:"+NUMBER+"\r\nEndFragment:"+NUMBER+"\r\n"; + static final String PREFIX = "<html><body><!--StartFragment-->"; //$NON-NLS-1$ + static final String SUFFIX = "<!--EndFragment--></body></html>"; //$NON-NLS-1$ + static final String StartFragment = "StartFragment:"; //$NON-NLS-1$ + static final String EndFragment = "EndFragment:"; //$NON-NLS-1$ + +private HTMLTransfer() {} + +/** + * Returns the singleton instance of the HTMLTransfer class. + * + * @return the singleton instance of the HTMLTransfer class + */ +public static HTMLTransfer getInstance () { + return _instance; +} + +/** + * This implementation of <code>javaToNative</code> converts HTML-formatted text + * represented by a java <code>String</code> to a platform specific representation. + * + * @param object a java <code>String</code> containing HTML text + * @param transferData an empty <code>TransferData</code> object that will + * be filled in on return with the platform specific format of the data + * + * @see Transfer#nativeToJava + */ +public void javaToNative (Object object, TransferData transferData){ + if (!checkHTML(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + String string = (String)object; + int count = string.length(); + char[] chars = new char[count + 1]; + string.getChars(0, count, chars, 0); + /* NOTE: CF_HTML uses UTF-8 encoding. */ + int cchMultiByte = OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, -1, null, 0, null, null); + if (cchMultiByte == 0) { + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.DV_E_STGMEDIUM; + return; + } + int startHTML = HEADER.length(); + int startFragment = startHTML + PREFIX.length(); + int endFragment = startFragment + cchMultiByte - 1; + int endHTML = endFragment + SUFFIX.length(); + + StringBuffer buffer = new StringBuffer(HEADER); + int maxLength = NUMBER.length(); + //startHTML + int start = buffer.toString().indexOf(NUMBER); + String temp = Integer.toString(startHTML); + buffer.replace(start + maxLength-temp.length(), start + maxLength, temp); + //endHTML + start = buffer.toString().indexOf(NUMBER, start); + temp = Integer.toString(endHTML); + buffer.replace(start + maxLength-temp.length(), start + maxLength, temp); + //startFragment + start = buffer.toString().indexOf(NUMBER, start); + temp = Integer.toString(startFragment); + buffer.replace(start + maxLength-temp.length(), start + maxLength, temp); + //endFragment + start = buffer.toString().indexOf(NUMBER, start); + temp = Integer.toString(endFragment); + buffer.replace(start + maxLength-temp.length(), start + maxLength, temp); + + buffer.append(PREFIX); + buffer.append(string); + buffer.append(SUFFIX); + + count = buffer.length(); + chars = new char[count + 1]; + buffer.getChars(0, count, chars, 0); + cchMultiByte = OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, -1, null, 0, null, null); + int /*long*/ lpMultiByteStr = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, cchMultiByte); + OS.WideCharToMultiByte(OS.CP_UTF8, 0, chars, -1, lpMultiByteStr, cchMultiByte, null, null); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = lpMultiByteStr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + return; +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform specific + * representation of HTML text to a java <code>String</code>. + * + * @param transferData the platform specific representation of the data to be converted + * @return a java <code>String</code> containing HTML text if the conversion was successful; + * otherwise null + * + * @see Transfer#javaToNative + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.pIDataObject == 0) return null; + IDataObject data = new IDataObject(transferData.pIDataObject); + data.AddRef(); + STGMEDIUM stgmedium = new STGMEDIUM(); + FORMATETC formatetc = transferData.formatetc; + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = getData(data, formatetc, stgmedium); + data.Release(); + if (transferData.result != COM.S_OK) return null; + int /*long*/ hMem = stgmedium.unionField; + + try { + int /*long*/ lpMultiByteStr = OS.GlobalLock(hMem); + if (lpMultiByteStr == 0) return null; + try { + /* NOTE: CF_HTML uses UTF-8 encoding. + * The MSDN documentation for MultiByteToWideChar states that dwFlags must be set to 0 for UTF-8. + * Otherwise, the function fails with ERROR_INVALID_FLAGS. */ + int cchWideChar = OS.MultiByteToWideChar (OS.CP_UTF8, 0, lpMultiByteStr, -1, null, 0); + if (cchWideChar == 0) return null; + char[] lpWideCharStr = new char [cchWideChar - 1]; + OS.MultiByteToWideChar (OS.CP_UTF8, 0, lpMultiByteStr, -1, lpWideCharStr, lpWideCharStr.length); + String string = new String(lpWideCharStr); + int fragmentStart = 0, fragmentEnd = 0; + int start = string.indexOf(StartFragment) + StartFragment.length(); + int end = start + 1; + while (end < string.length()) { + String s = string.substring(start, end); + try { + fragmentStart = Integer.parseInt(s); + end++; + } catch (NumberFormatException e) { + break; + } + } + start = string.indexOf(EndFragment) + EndFragment.length(); + end = start + 1; + while (end < string.length()) { + String s = string.substring(start, end); + try { + fragmentEnd = Integer.parseInt(s); + end++; + } catch (NumberFormatException e) { + break; + } + } + if (fragmentEnd <= fragmentStart || fragmentEnd > OS.strlen(lpMultiByteStr)) return null; + cchWideChar = OS.MultiByteToWideChar (OS.CP_UTF8, 0, lpMultiByteStr+fragmentStart, fragmentEnd - fragmentStart, lpWideCharStr, lpWideCharStr.length); + if (cchWideChar == 0) return null; + String s = new String(lpWideCharStr, 0, cchWideChar); + /* + * Firefox includes <!--StartFragment --> in the fragment, so remove it. + */ + String foxStart = "<!--StartFragment -->\r\n"; //$NON-NLS-1$ + int prefix = s.indexOf(foxStart); + if (prefix != -1) { + prefix += foxStart.length(); + s = s.substring(prefix); + } + return s; + } finally { + OS.GlobalUnlock(hMem); + } + } finally { + OS.GlobalFree(hMem); + } +} +protected int[] getTypeIds(){ + return new int[] {HTML_FORMATID}; +} +protected String[] getTypeNames(){ + return new String[] {HTML_FORMAT}; +} +boolean checkHTML(Object object) { + return (object != null && object instanceof String && ((String)object).length() > 0); +} +protected boolean validate(Object object) { + return checkHTML(object); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/ImageTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/ImageTransfer.java new file mode 100644 index 0000000000..426adf1bcf --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/ImageTransfer.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.*; + +/** + * The class <code>ImageTransfer</code> provides a platform specific mechanism + * for converting an Image represented as a java <code>ImageData</code> to a + * platform specific representation of the data and vice versa. + * + * <p>An example of a java <code>ImageData</code> is shown below:</p> + * + * <code><pre> + * Image image = new Image(display, "C:\temp\img1.gif"); + * ImageData imgData = image.getImageData(); + * </code></pre> + * + * @see Transfer + * + * @since 3.4 + */ +public class ImageTransfer extends ByteArrayTransfer { + + private static ImageTransfer _instance = new ImageTransfer(); + private static final String CF_DIB = "CF_DIB"; //$NON-NLS-1$ + private static final int CF_DIBID = COM.CF_DIB; + +private ImageTransfer() {} + +/** + * Returns the singleton instance of the ImageTransfer class. + * + * @return the singleton instance of the ImageTransfer class + */ +public static ImageTransfer getInstance () { + return _instance; +} + +/** + * This implementation of <code>javaToNative</code> converts an ImageData object represented + * by java <code>ImageData</code> to a platform specific representation. + * + * @param object a java <code>ImageData</code> containing the ImageData to be converted + * @param transferData an empty <code>TransferData</code> object that will + * be filled in on return with the platform specific format of the data + * + * @see Transfer#nativeToJava + */ +public void javaToNative(Object object, TransferData transferData) { + if (!checkImage(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + ImageData imgData = (ImageData)object; + if (imgData == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + + int imageSize = imgData.data.length; + int imageHeight = imgData.height; + int bytesPerLine = imgData.bytesPerLine; + + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biSizeImage = imageSize; + bmiHeader.biWidth = imgData.width; + bmiHeader.biHeight = imageHeight; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = (short)imgData.depth; + bmiHeader.biCompression = OS.DIB_RGB_COLORS; + + int colorSize = 0; + if (bmiHeader.biBitCount <= 8) { + colorSize += (1 << bmiHeader.biBitCount) * 4; + } + byte[] bmi = new byte[BITMAPINFOHEADER.sizeof + colorSize]; + OS.MoveMemory(bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + + RGB[] rgbs = imgData.palette.getRGBs(); + if (rgbs != null && colorSize > 0) { + int offset = BITMAPINFOHEADER.sizeof; + for (int j = 0; j < rgbs.length; j++) { + bmi[offset] = (byte)rgbs[j].blue; + bmi[offset + 1] = (byte)rgbs[j].green; + bmi[offset + 2] = (byte)rgbs[j].red; + bmi[offset + 3] = 0; + offset += 4; + } + } + int /*long*/ newPtr = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, BITMAPINFOHEADER.sizeof + colorSize + imageSize); + OS.MoveMemory(newPtr, bmi, bmi.length); + int /*long*/ pBitDest = newPtr + BITMAPINFOHEADER.sizeof + colorSize; + + if (imageHeight <= 0) { + OS.MoveMemory(pBitDest, imgData.data, imageSize); + } else { + int offset = 0; + pBitDest += bytesPerLine * (imageHeight - 1); + byte[] scanline = new byte[bytesPerLine]; + for (int i = 0; i < imageHeight; i++) { + System.arraycopy(imgData.data, offset, scanline, 0, bytesPerLine); + OS.MoveMemory(pBitDest, scanline, bytesPerLine); + offset += bytesPerLine; + pBitDest -= bytesPerLine; + } + } + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = newPtr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; +} + + +/** + * This implementation of <code>nativeToJava</code> converts a platform specific + * representation of an image to java <code>ImageData</code>. + * + * @param transferData the platform specific representation of the data to be converted + * @return a java <code>ImageData</code> of the image if the conversion was successful; + * otherwise null + * + * @see Transfer#javaToNative + */ +public Object nativeToJava(TransferData transferData) { + if (!isSupportedType(transferData) || transferData.pIDataObject == 0) return null; + IDataObject dataObject = new IDataObject(transferData.pIDataObject); + dataObject.AddRef(); + FORMATETC formatetc = new FORMATETC(); + formatetc.cfFormat = COM.CF_DIB; + formatetc.ptd = 0; + formatetc.dwAspect = COM.DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.tymed = COM.TYMED_HGLOBAL; + STGMEDIUM stgmedium = new STGMEDIUM(); + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = getData(dataObject, formatetc, stgmedium); + + if (transferData.result != COM.S_OK) return null; + int /*long*/ hMem = stgmedium.unionField; + dataObject.Release(); + try { + int /*long*/ ptr = OS.GlobalLock(hMem); + if (ptr == 0) return null; + try { + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); + OS.MoveMemory(bmiHeader, ptr, BITMAPINFOHEADER.sizeof); + int /*long*/[] pBits = new int /*long*/[1]; + int /*long*/ memDib = OS.CreateDIBSection(0, ptr, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (memDib == 0) SWT.error(SWT.ERROR_NO_HANDLES); + int /*long*/ bits = ptr + bmiHeader.biSize; + if (bmiHeader.biBitCount <= 8) { + bits += (bmiHeader.biClrUsed == 0 ? (1 << bmiHeader.biBitCount) : bmiHeader.biClrUsed) * 4; + } else if (bmiHeader.biCompression == OS.BI_BITFIELDS) { + bits += 12; + } + if (bmiHeader.biHeight < 0) { + OS.MoveMemory(pBits[0], bits, bmiHeader.biSizeImage); + } else { + DIBSECTION dib = new DIBSECTION(); + OS.GetObject(memDib, DIBSECTION.sizeof, dib); + int biHeight = dib.biHeight; + int scanline = dib.biSizeImage / biHeight; + int /*long*/ pDestBits = pBits[0]; + int /*long*/ pSourceBits = bits + scanline * (biHeight - 1); + for (int i = 0; i < biHeight; i++) { + OS.MoveMemory(pDestBits, pSourceBits, scanline); + pDestBits += scanline; + pSourceBits -= scanline; + } + } + Image image = Image.win32_new(null, SWT.BITMAP, memDib); + ImageData data = image.getImageData(); + OS.DeleteObject(memDib); + image.dispose(); + return data; + } finally { + OS.GlobalUnlock(hMem); + } + } finally { + OS.GlobalFree(hMem); + } +} + +protected int[] getTypeIds(){ + return new int[] {CF_DIBID}; +} + +protected String[] getTypeNames(){ + return new String[] {CF_DIB}; +} +boolean checkImage(Object object) { + if (object == null || !(object instanceof ImageData)) return false; + return true; +} + +protected boolean validate(Object object) { + return checkImage(object); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/OleEnumFORMATETC.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/OleEnumFORMATETC.java new file mode 100755 index 0000000000..c1c8c1fbd6 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/OleEnumFORMATETC.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.internal.ole.win32.*; + +final class OleEnumFORMATETC { + + private COMObject iUnknown; + private COMObject iEnumFORMATETC; + + private int refCount; + private int index; + + private FORMATETC[] formats; + +OleEnumFORMATETC() { + + createCOMInterfaces(); + +} +int AddRef() { + refCount++; + return refCount; +} +private void createCOMInterfaces() { + // register each of the interfaces that this object implements + iUnknown = new COMObject(new int[] {2, 0, 0}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + }; + iEnumFORMATETC = new COMObject(new int[] {2, 0, 0, 3, 1, 0, 1}){ + public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);} + public int /*long*/ method1(int /*long*/[] args) {return AddRef();} + public int /*long*/ method2(int /*long*/[] args) {return Release();} + public int /*long*/ method3(int /*long*/[] args) {return Next((int)/*64*/args[0], args[1], args[2]);} + public int /*long*/ method4(int /*long*/[] args) {return Skip((int)/*64*/args[0]);} + public int /*long*/ method5(int /*long*/[] args) {return Reset();} + // method6 Clone - not implemented + }; +} +private void disposeCOMInterfaces() { + + if (iUnknown != null) + iUnknown.dispose(); + iUnknown = null; + + if (iEnumFORMATETC != null) + iEnumFORMATETC.dispose(); + iEnumFORMATETC = null; +} +int /*long*/ getAddress() { + return iEnumFORMATETC.getAddress(); +} +private FORMATETC[] getNextItems(int numItems){ + + if (formats == null || numItems < 1) return null; + + int endIndex = index + numItems - 1; + if (endIndex > (formats.length - 1)) endIndex = formats.length - 1; + if (index > endIndex) return null; + + FORMATETC[] items = new FORMATETC[endIndex - index + 1]; + for (int i = 0; i < items.length; i++){ + items[i] = formats[index]; + index++; + } + + return items; +} +private int Next(int celt, int /*long*/ rgelt, int /*long*/ pceltFetched) { + /* Retrieves the next celt items in the enumeration sequence. + If there are fewer than the requested number of elements left in the sequence, + it retrieves the remaining elements. + The number of elements actually retrieved is returned through pceltFetched + (unless the caller passed in NULL for that parameter). + */ + + if (rgelt == 0) return COM.E_INVALIDARG; + if (pceltFetched == 0 && celt != 1) return COM.E_INVALIDARG; + + FORMATETC[] nextItems = getNextItems(celt); + if (nextItems != null) { + for (int i = 0; i < nextItems.length; i++) { + COM.MoveMemory(rgelt + i*FORMATETC.sizeof, nextItems[i], FORMATETC.sizeof); + } + + if (pceltFetched != 0) + COM.MoveMemory(pceltFetched, new int[] {nextItems.length}, 4); + + if (nextItems.length == celt) return COM.S_OK; + + } else { + if (pceltFetched != 0) + COM.MoveMemory(pceltFetched, new int[] {0}, 4); + COM.MoveMemory(rgelt, new FORMATETC(), FORMATETC.sizeof); + + } + return COM.S_FALSE; +} +private int QueryInterface(int /*long*/ riid, int /*long*/ ppvObject) { + + if (riid == 0 || ppvObject == 0) return COM.E_NOINTERFACE; + + GUID guid = new GUID(); + COM.MoveMemory(guid, riid, GUID.sizeof); + + if (COM.IsEqualGUID(guid, COM.IIDIUnknown)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iUnknown.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + if (COM.IsEqualGUID(guid, COM.IIDIEnumFORMATETC)) { + COM.MoveMemory(ppvObject, new int /*long*/[] {iEnumFORMATETC.getAddress()}, OS.PTR_SIZEOF); + AddRef(); + return COM.S_OK; + } + COM.MoveMemory(ppvObject, new int /*long*/[] {0}, OS.PTR_SIZEOF); + return COM.E_NOINTERFACE; +} +int Release() { + refCount--; + + if (refCount == 0) { + disposeCOMInterfaces(); + COM.CoFreeUnusedLibraries(); + } + + return refCount; +} +private int Reset() { + //Resets the enumeration sequence to the beginning. + index = 0; + return COM.S_OK; +} +void setFormats(FORMATETC[] newFormats) { + formats = newFormats; + index = 0; +} +private int Skip(int celt) { + //Skips over the next specified number of elements in the enumeration sequence. + if (celt < 1 ) return COM.E_INVALIDARG; + + index += celt; + if (index > (formats.length - 1)){ + index = formats.length - 1; + return COM.S_FALSE; + } + return COM.S_OK; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/RTFTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/RTFTransfer.java new file mode 100755 index 0000000000..f8da4986c4 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/RTFTransfer.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; + +/** + * The class <code>RTFTransfer</code> provides a platform specific mechanism + * for converting text in RTF format represented as a java <code>String</code> + * to a platform specific representation of the data and vice versa. + * + * <p>An example of a java <code>String</code> containing RTF text is shown + * below:</p> + * + * <code><pre> + * String rtfData = "{\\rtf1{\\colortbl;\\red255\\green0\\blue0;}\\uc1\\b\\i Hello World}"; + * </code></pre> + * + * @see Transfer + */ +public class RTFTransfer extends ByteArrayTransfer { + + private static RTFTransfer _instance = new RTFTransfer(); + private static final String CF_RTF = "Rich Text Format"; //$NON-NLS-1$ + private static final int CF_RTFID = registerType(CF_RTF); + +private RTFTransfer() {} + +/** + * Returns the singleton instance of the RTFTransfer class. + * + * @return the singleton instance of the RTFTransfer class + */ +public static RTFTransfer getInstance () { + return _instance; +} + +/** + * This implementation of <code>javaToNative</code> converts RTF-formatted text + * represented by a java <code>String</code> to a platform specific representation. + * + * @param object a java <code>String</code> containing RTF text + * @param transferData an empty <code>TransferData</code> object that will + * be filled in on return with the platform specific format of the data + * + * @see Transfer#nativeToJava + */ +public void javaToNative (Object object, TransferData transferData){ + if (!checkRTF(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + // CF_RTF is stored as a null terminated byte array + String string = (String)object; + int count = string.length(); + char[] chars = new char[count + 1]; + string.getChars(0, count, chars, 0); + int codePage = OS.GetACP(); + int cchMultiByte = OS.WideCharToMultiByte(codePage, 0, chars, -1, null, 0, null, null); + if (cchMultiByte == 0) { + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.DV_E_STGMEDIUM; + return; + } + int /*long*/ lpMultiByteStr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, cchMultiByte); + OS.WideCharToMultiByte(codePage, 0, chars, -1, lpMultiByteStr, cchMultiByte, null, null); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = lpMultiByteStr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + return; +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform specific + * representation of RTF text to a java <code>String</code>. + * + * @param transferData the platform specific representation of the data to be converted + * @return a java <code>String</code> containing RTF text if the conversion was successful; + * otherwise null + * + * @see Transfer#javaToNative + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.pIDataObject == 0) return null; + IDataObject data = new IDataObject(transferData.pIDataObject); + data.AddRef(); + STGMEDIUM stgmedium = new STGMEDIUM(); + FORMATETC formatetc = transferData.formatetc; + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = getData(data, formatetc, stgmedium); + data.Release(); + if (transferData.result != COM.S_OK) return null; + int /*long*/ hMem = stgmedium.unionField; + try { + int /*long*/ lpMultiByteStr = OS.GlobalLock(hMem); + if (lpMultiByteStr == 0) return null; + try { + int codePage = OS.GetACP(); + int cchWideChar = OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, null, 0); + if (cchWideChar == 0) return null; + char[] lpWideCharStr = new char [cchWideChar - 1]; + OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, lpWideCharStr, lpWideCharStr.length); + return new String(lpWideCharStr); + } finally { + OS.GlobalUnlock(hMem); + } + } finally { + OS.GlobalFree(hMem); + } +} + +protected int[] getTypeIds(){ + return new int[] {CF_RTFID}; +} + +protected String[] getTypeNames(){ + return new String[] {CF_RTF}; +} + +boolean checkRTF(Object object) { + return (object != null && object instanceof String && ((String)object).length() > 0); +} + +protected boolean validate(Object object) { + return checkRTF(object); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDragSourceEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDragSourceEffect.java new file mode 100644 index 0000000000..bc25bd33ae --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDragSourceEffect.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.widgets.*; + +/** + * This class provides default implementations to display a source image + * when a drag is initiated from a <code>Table</code>. + * + * <p>Classes that wish to provide their own source image for a <code>Table</code> can + * extend the <code>TableDragSourceEffect</code> class, override the + * <code>TableDragSourceEffect.dragStart</code> method and set the field + * <code>DragSourceEvent.image</code> with their own image.</p> + * + * Subclasses that override any methods of this class must call the corresponding + * <code>super</code> method to get the default drag source effect implementation. + * + * @see DragSourceEffect + * @see DragSourceEvent + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.3 + */ +public class TableDragSourceEffect extends DragSourceEffect { + Image dragSourceImage = null; + + /** + * Creates a new <code>TableDragSourceEffect</code> to handle drag effect + * from the specified <code>Table</code>. + * + * @param table the <code>Table</code> that the user clicks on to initiate the drag + */ + public TableDragSourceEffect(Table table) { + super(table); + } + + /** + * This implementation of <code>dragFinished</code> disposes the image + * that was created in <code>TableDragSourceEffect.dragStart</code>. + * + * Subclasses that override this method should call <code>super.dragFinished(event)</code> + * to dispose the image in the default implementation. + * + * @param event the information associated with the drag finished event + */ + public void dragFinished(DragSourceEvent event) { + if (dragSourceImage != null) dragSourceImage.dispose(); + dragSourceImage = null; + } + + /** + * This implementation of <code>dragStart</code> will create a default + * image that will be used during the drag. The image should be disposed + * when the drag is completed in the <code>TableDragSourceEffect.dragFinished</code> + * method. + * + * Subclasses that override this method should call <code>super.dragStart(event)</code> + * to use the image from the default implementation. + * + * @param event the information associated with the drag start event + */ + public void dragStart(DragSourceEvent event) { + event.image = getDragSourceImage(event); + } + + Image getDragSourceImage(DragSourceEvent event) { + if (dragSourceImage != null) dragSourceImage.dispose(); + dragSourceImage = null; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + SHDRAGIMAGE shdi = new SHDRAGIMAGE(); + int DI_GETDRAGIMAGE = OS.RegisterWindowMessage (new TCHAR (0, "ShellGetDragImage", true)); //$NON-NLS-1$ + if (OS.SendMessage (control.handle, DI_GETDRAGIMAGE, 0, shdi) != 0) { + if ((control.getStyle() & SWT.MIRRORED) != 0) { + event.offsetX = shdi.sizeDragImage.cx - shdi.ptOffset.x; + } else { + event.offsetX = shdi.ptOffset.x; + } + event.offsetY = shdi.ptOffset.y; + int /*long*/ hImage = shdi.hbmpDragImage; + if (hImage != 0) { + BITMAP bm = new BITMAP (); + OS.GetObject (hImage, BITMAP.sizeof, bm); + int srcWidth = bm.bmWidth; + int srcHeight = bm.bmHeight; + + /* Create resources */ + int /*long*/ hdc = OS.GetDC (0); + int /*long*/ srcHdc = OS.CreateCompatibleDC (hdc); + int /*long*/ oldSrcBitmap = OS.SelectObject (srcHdc, hImage); + int /*long*/ memHdc = OS.CreateCompatibleDC (hdc); + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER (); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = srcWidth; + bmiHeader.biHeight = -srcHeight; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = 32; + bmiHeader.biCompression = OS.BI_RGB; + byte [] bmi = new byte[BITMAPINFOHEADER.sizeof]; + OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + int /*long*/ [] pBits = new int /*long*/ [1]; + int /*long*/ memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (memDib == 0) SWT.error (SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject (memHdc, memDib); + + BITMAP dibBM = new BITMAP (); + OS.GetObject (memDib, BITMAP.sizeof, dibBM); + int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight; + + /* Get the foreground pixels */ + OS.BitBlt (memHdc, 0, 0, srcWidth, srcHeight, srcHdc, 0, 0, OS.SRCCOPY); + byte[] srcData = new byte [sizeInBytes]; + OS.MoveMemory (srcData, dibBM.bmBits, sizeInBytes); + + PaletteData palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); + ImageData data = new ImageData(srcWidth, srcHeight, bm.bmBitsPixel, palette, bm.bmWidthBytes, srcData); + if (shdi.crColorKey == -1) { + byte[] alphaData = new byte[srcWidth * srcHeight]; + int spinc = dibBM.bmWidthBytes - srcWidth * 4; + int ap = 0, sp = 3; + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + alphaData [ap++] = srcData [sp]; + sp += 4; + } + sp += spinc; + } + data.alphaData = alphaData; + } else { + data.transparentPixel = shdi.crColorKey << 8; + } + dragSourceImage = new Image(control.getDisplay(), data); + OS.SelectObject (memHdc, oldMemBitmap); + OS.DeleteDC (memHdc); + OS.DeleteObject (memDib); + OS.SelectObject (srcHdc, oldSrcBitmap); + OS.DeleteDC (srcHdc); + OS.ReleaseDC (0, hdc); + OS.DeleteObject (hImage); + return dragSourceImage; + } + } + return null; + } + Table table = (Table) control; + //TEMPORARY CODE + if (table.isListening (SWT.EraseItem) || table.isListening (SWT.PaintItem)) return null; + TableItem[] selection = table.getSelection(); + if (selection.length == 0) return null; + int /*long*/ tableImageList = OS.SendMessage (table.handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0); + if (tableImageList != 0) { + int count = Math.min(selection.length, 10); + Rectangle bounds = selection[0].getBounds(0); + for (int i = 1; i < count; i++) { + bounds = bounds.union(selection[i].getBounds(0)); + } + int /*long*/ hDC = OS.GetDC(0); + int /*long*/ hDC1 = OS.CreateCompatibleDC(hDC); + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { + if ((table.getStyle() & SWT.RIGHT_TO_LEFT) != 0) { + OS.SetLayout(hDC1, OS.LAYOUT_RTL | OS.LAYOUT_BITMAPORIENTATIONPRESERVED); + } + } + int /*long*/ bitmap = OS.CreateCompatibleBitmap(hDC, bounds.width, bounds.height); + int /*long*/ hOldBitmap = OS.SelectObject(hDC1, bitmap); + RECT rect = new RECT(); + rect.right = bounds.width; + rect.bottom = bounds.height; + int /*long*/ hBrush = OS.GetStockObject(OS.WHITE_BRUSH); + OS.FillRect(hDC1, rect, hBrush); + for (int i = 0; i < count; i++) { + TableItem selected = selection[i]; + Rectangle cell = selected.getBounds(0); + POINT pt = new POINT(); + int /*long*/ imageList = OS.SendMessage (table.handle, OS.LVM_CREATEDRAGIMAGE, table.indexOf(selected), pt); + OS.ImageList_Draw(imageList, 0, hDC1, cell.x - bounds.x, cell.y - bounds.y, OS.ILD_SELECTED); + OS.ImageList_Destroy(imageList); + } + OS.SelectObject(hDC1, hOldBitmap); + OS.DeleteDC (hDC1); + OS.ReleaseDC (0, hDC); + Display display = table.getDisplay(); + dragSourceImage = Image.win32_new(display, SWT.BITMAP, bitmap); + return dragSourceImage; + } + return null; + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDropTargetEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDropTargetEffect.java new file mode 100644 index 0000000000..6d27948cc1 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TableDropTargetEffect.java @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.widgets.*; + +/** + * This class provides a default drag under effect (eg. select, insert and scroll) + * when a drag occurs over a <code>Table</code>. + * + * <p>Classes that wish to provide their own drag under effect for a <code>Table</code> + * can extend the <code>TableDropTargetEffect</code> and override any applicable methods + * in <code>TableDropTargetEffect</code> to display their own drag under effect.</p> + * + * Subclasses that override any methods of this class must call the corresponding + * <code>super</code> method to get the default drag under effect implementation. + * + * <p>The feedback value is either one of the FEEDBACK constants defined in + * class <code>DND</code> which is applicable to instances of this class, + * or it must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>DND</code> effect constants. + * </p> + * <p> + * <dl> + * <dt><b>Feedback:</b></dt> + * <dd>FEEDBACK_SELECT, FEEDBACK_SCROLL</dd> + * </dl> + * </p> + * + * @see DropTargetAdapter + * @see DropTargetEvent + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.3 + */ +public class TableDropTargetEffect extends DropTargetEffect { + static final int SCROLL_HYSTERESIS = 200; // milli seconds + + int scrollIndex = -1; + long scrollBeginTime; + TableItem dropHighlight; + + /** + * Creates a new <code>TableDropTargetEffect</code> to handle the drag under effect on the specified + * <code>Table</code>. + * + * @param table the <code>Table</code> over which the user positions the cursor to drop the data + */ + public TableDropTargetEffect(Table table) { + super(table); + } + + int checkEffect(int effect) { + // Some effects are mutually exclusive. Make sure that only one of the mutually exclusive effects has been specified. + if ((effect & DND.FEEDBACK_SELECT) != 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER & ~DND.FEEDBACK_INSERT_BEFORE; + if ((effect & DND.FEEDBACK_INSERT_BEFORE) != 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER; + return effect; + } + + /** + * This implementation of <code>dragEnter</code> provides a default drag under effect + * for the feedback specified in <code>event.feedback</code>. + * + * For additional information see <code>DropTargetAdapter.dragEnter</code>. + * + * Subclasses that override this method should call <code>super.dragEnter(event)</code> + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag enter event + * + * @see DropTargetAdapter + * @see DropTargetEvent + */ + public void dragEnter(DropTargetEvent event) { + scrollBeginTime = 0; + scrollIndex = -1; + dropHighlight = null; + } + + /** + * This implementation of <code>dragLeave</code> provides a default drag under effect + * for the feedback specified in <code>event.feedback</code>. + * + * For additional information see <code>DropTargetAdapter.dragLeave</code>. + * + * Subclasses that override this method should call <code>super.dragLeave(event)</code> + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag leave event + * + * @see DropTargetAdapter + * @see DropTargetEvent + */ + public void dragLeave(DropTargetEvent event) { + Table table = (Table) control; + int /*long*/ handle = table.handle; + if (dropHighlight != null) { + LVITEM lvItem = new LVITEM (); + lvItem.stateMask = OS.LVIS_DROPHILITED; + OS.SendMessage(handle, OS.LVM_SETITEMSTATE, -1, lvItem); + dropHighlight = null; + } + scrollBeginTime = 0; + scrollIndex = -1; + } + + /** + * This implementation of <code>dragOver</code> provides a default drag under effect + * for the feedback specified in <code>event.feedback</code>. The class description + * lists the FEEDBACK constants that are applicable to the class. + * + * For additional information see <code>DropTargetAdapter.dragOver</code>. + * + * Subclasses that override this method should call <code>super.dragOver(event)</code> + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag over event + * + * @see DropTargetAdapter + * @see DropTargetEvent + * @see DND#FEEDBACK_SELECT + * @see DND#FEEDBACK_SCROLL + */ + public void dragOver(DropTargetEvent event) { + Table table = (Table) getControl(); + int effect = checkEffect(event.feedback); + int /*long*/ handle = table.handle; + Point coordinates = new Point(event.x, event.y); + coordinates = table.toControl(coordinates); + LVHITTESTINFO pinfo = new LVHITTESTINFO(); + pinfo.x = coordinates.x; + pinfo.y = coordinates.y; + OS.SendMessage(handle, OS.LVM_HITTEST, 0, pinfo); + if ((effect & DND.FEEDBACK_SCROLL) == 0) { + scrollBeginTime = 0; + scrollIndex = -1; + } else { + if (pinfo.iItem != -1 && scrollIndex == pinfo.iItem && scrollBeginTime != 0) { + if (System.currentTimeMillis() >= scrollBeginTime) { + int top = Math.max (0, (int)/*64*/OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)); + int count = (int)/*64*/OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + int index = (scrollIndex - 1 < top) ? Math.max(0, scrollIndex - 1) : Math.min(count - 1, scrollIndex + 1); + boolean scroll = true; + if (pinfo.iItem == top) { + scroll = pinfo.iItem != index; + } else { + RECT itemRect = new RECT (); + itemRect.left = OS.LVIR_BOUNDS; + if (OS.SendMessage (handle, OS.LVM_GETITEMRECT, pinfo.iItem, itemRect) != 0) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + POINT pt = new POINT (); + pt.x = itemRect.left; + pt.y = itemRect.top; + if (OS.PtInRect (rect, pt)) { + pt.y = itemRect.bottom; + if (OS.PtInRect (rect, pt)) scroll = false; + } + } + } + if (scroll) { + OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 0); + table.redraw(); + } + scrollBeginTime = 0; + scrollIndex = -1; + } + } else { + scrollBeginTime = System.currentTimeMillis() + SCROLL_HYSTERESIS; + scrollIndex = pinfo.iItem; + } + } + + if (pinfo.iItem != -1 && (effect & DND.FEEDBACK_SELECT) != 0) { + TableItem item = table.getItem(pinfo.iItem); + if (dropHighlight != item) { + LVITEM lvItem = new LVITEM(); + lvItem.stateMask = OS.LVIS_DROPHILITED; + OS.SendMessage(handle, OS.LVM_SETITEMSTATE, -1, lvItem); + lvItem.state = OS.LVIS_DROPHILITED; + OS.SendMessage(handle, OS.LVM_SETITEMSTATE, pinfo.iItem, lvItem); + dropHighlight = item; + } + } else { + if (dropHighlight != null) { + LVITEM lvItem = new LVITEM (); + lvItem.stateMask = OS.LVIS_DROPHILITED; + OS.SendMessage(handle, OS.LVM_SETITEMSTATE, -1, lvItem); + dropHighlight = null; + } + } + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TextTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TextTransfer.java new file mode 100755 index 0000000000..94b3cf0fed --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TextTransfer.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; + +/** + * The class <code>TextTransfer</code> provides a platform specific mechanism + * for converting plain text represented as a java <code>String</code> + * to a platform specific representation of the data and vice versa. + * + * <p>An example of a java <code>String</code> containing plain text is shown + * below:</p> + * + * <code><pre> + * String textData = "Hello World"; + * </code></pre> + * + * @see Transfer + */ +public class TextTransfer extends ByteArrayTransfer { + + private static TextTransfer _instance = new TextTransfer(); + private static final String CF_UNICODETEXT = "CF_UNICODETEXT"; //$NON-NLS-1$ + private static final String CF_TEXT = "CF_TEXT"; //$NON-NLS-1$ + private static final int CF_UNICODETEXTID = COM.CF_UNICODETEXT; + private static final int CF_TEXTID = COM.CF_TEXT; + +private TextTransfer() {} + +/** + * Returns the singleton instance of the TextTransfer class. + * + * @return the singleton instance of the TextTransfer class + */ +public static TextTransfer getInstance () { + return _instance; +} + +/** + * This implementation of <code>javaToNative</code> converts plain text + * represented by a java <code>String</code> to a platform specific representation. + * + * @param object a java <code>String</code> containing text + * @param transferData an empty <code>TransferData</code> object that will + * be filled in on return with the platform specific format of the data + * + * @see Transfer#nativeToJava + */ +public void javaToNative (Object object, TransferData transferData){ + if (!checkText(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + transferData.result = COM.E_FAIL; + String string = (String)object; + switch (transferData.type) { + case COM.CF_UNICODETEXT: { + int charCount = string.length (); + char[] chars = new char[charCount+1]; + string.getChars (0, charCount, chars, 0); + int byteCount = chars.length * 2; + int /*long*/ newPtr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, byteCount); + OS.MoveMemory(newPtr, chars, byteCount); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = newPtr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + break; + } + case COM.CF_TEXT: { + int count = string.length(); + char[] chars = new char[count + 1]; + string.getChars(0, count, chars, 0); + int codePage = OS.GetACP(); + int cchMultiByte = OS.WideCharToMultiByte(codePage, 0, chars, -1, null, 0, null, null); + if (cchMultiByte == 0) { + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.DV_E_STGMEDIUM; + return; + } + int /*long*/ lpMultiByteStr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, cchMultiByte); + OS.WideCharToMultiByte(codePage, 0, chars, -1, lpMultiByteStr, cchMultiByte, null, null); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = lpMultiByteStr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + break; + } + } + return; +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform specific + * representation of plain text to a java <code>String</code>. + * + * @param transferData the platform specific representation of the data to be converted + * @return a java <code>String</code> containing text if the conversion was successful; otherwise null + * + * @see Transfer#javaToNative + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.pIDataObject == 0) return null; + + IDataObject data = new IDataObject(transferData.pIDataObject); + data.AddRef(); + FORMATETC formatetc = transferData.formatetc; + STGMEDIUM stgmedium = new STGMEDIUM(); + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = getData(data, formatetc, stgmedium); + data.Release(); + if (transferData.result != COM.S_OK) return null; + int /*long*/ hMem = stgmedium.unionField; + try { + switch (transferData.type) { + case CF_UNICODETEXTID: { + /* Ensure byteCount is a multiple of 2 bytes */ + int size = OS.GlobalSize(hMem) / 2 * 2; + if (size == 0) return null; + char[] chars = new char[size/2]; + int /*long*/ ptr = OS.GlobalLock(hMem); + if (ptr == 0) return null; + try { + OS.MoveMemory(chars, ptr, size); + int length = chars.length; + for (int i=0; i<chars.length; i++) { + if (chars [i] == '\0') { + length = i; + break; + } + } + return new String (chars, 0, length); + } finally { + OS.GlobalUnlock(hMem); + } + } + case CF_TEXTID: { + int /*long*/ lpMultiByteStr = OS.GlobalLock(hMem); + if (lpMultiByteStr == 0) return null; + try { + int codePage = OS.GetACP(); + int cchWideChar = OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, null, 0); + if (cchWideChar == 0) return null; + char[] lpWideCharStr = new char [cchWideChar - 1]; + OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, lpWideCharStr, lpWideCharStr.length); + return new String(lpWideCharStr); + } finally { + OS.GlobalUnlock(hMem); + } + } + } + } finally { + OS.GlobalFree(hMem); + } + return null; +} + +protected int[] getTypeIds(){ + return new int[] {CF_UNICODETEXTID, CF_TEXTID}; +} + +protected String[] getTypeNames(){ + return new String[] {CF_UNICODETEXT, CF_TEXT}; +} + +boolean checkText(Object object) { + return (object != null && object instanceof String && ((String)object).length() > 0); +} + +protected boolean validate(Object object) { + return checkText(object); +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/Transfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/Transfer.java new file mode 100755 index 0000000000..4a7f318b21 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/Transfer.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + + +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; + +/** + * <code>Transfer</code> provides a mechanism for converting between a java + * representation of data and a platform specific representation of data and + * vice versa. It is used in data transfer operations such as drag and drop and + * clipboard copy/paste. + * + * <p>You should only need to become familiar with this class if you are + * implementing a Transfer subclass and you are unable to subclass the + * ByteArrayTransfer class.</p> + * + * @see ByteArrayTransfer + * @see <a href="http://www.eclipse.org/swt/snippets/#dnd">Drag and Drop snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: DNDExample</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public abstract class Transfer { + +private static final int RETRY_LIMIT = 10; +/* + * Feature in Windows. When another application has control + * of the clipboard, the clipboard is locked and it's not + * possible to retrieve data until the other application is + * finished. To allow other applications to get the + * data, use PeekMessage() to enable cross thread + * message sends. + */ +int getData(IDataObject dataObject, FORMATETC pFormatetc, STGMEDIUM pmedium) { + if (dataObject.GetData(pFormatetc, pmedium) == COM.S_OK) return COM.S_OK; + try {Thread.sleep(50);} catch (Throwable t) {} + int result = dataObject.GetData(pFormatetc, pmedium); + int retryCount = 0; + while (result != COM.S_OK && retryCount++ < RETRY_LIMIT) { + MSG msg = new MSG(); + OS.PeekMessage(msg, 0, 0, 0, OS.PM_NOREMOVE | OS.PM_NOYIELD); + try {Thread.sleep(50);} catch (Throwable t) {} + result = dataObject.GetData(pFormatetc, pmedium); + } + return result; +} + +/** + * Returns a list of the platform specific data types that can be converted using + * this transfer agent. + * + * <p>Only the data type fields of the <code>TransferData</code> objects are filled + * in.</p> + * + * @return a list of the data types that can be converted using this transfer agent + */ +abstract public TransferData[] getSupportedTypes(); + +/** + * Returns true if the <code>TransferData</code> data type can be converted + * using this transfer agent, or false otherwise (including if transferData is + * <code>null</code>). + * + * @param transferData a platform specific description of a data type; only the data + * type fields of the <code>TransferData</code> object need to be filled in + * + * @return true if the transferData data type can be converted using this transfer + * agent + */ +abstract public boolean isSupportedType(TransferData transferData); + +/** + * Returns the platform specific ids of the data types that can be converted using + * this transfer agent. + * + * @return the platform specific ids of the data types that can be converted using + * this transfer agent + */ +abstract protected int[] getTypeIds(); + +/** + * Returns the platform specific names of the data types that can be converted + * using this transfer agent. + * + * @return the platform specific names of the data types that can be converted + * using this transfer agent. + */ +abstract protected String[] getTypeNames(); + +/** + * Converts a java representation of data to a platform specific representation of + * the data. + * + * <p>On a successful conversion, the transferData.result field will be set as follows: + * <ul> + * <li>Windows: COM.S_OK + * <li>Motif: 1 + * <li>GTK: 1 + * <li>Photon: 1 + * </ul></p> + * + * <p>If this transfer agent is unable to perform the conversion, the transferData.result + * field will be set to a failure value as follows: + * <ul> + * <li>Windows: COM.DV_E_TYMED or COM.E_FAIL + * <li>Motif: 0 + * <li>GTK: 0 + * <li>Photon: 0 + * </ul></p> + * + * @param object a java representation of the data to be converted; the type of + * Object that is passed in is dependent on the <code>Transfer</code> subclass. + * + * @param transferData an empty TransferData object; this object will be + * filled in on return with the platform specific representation of the data + * + * @exception org.eclipse.swt.SWTException <ul> + * <li>ERROR_INVALID_DATA - if object does not contain data in a valid format or is <code>null</code></li> + * </ul> + */ +abstract protected void javaToNative (Object object, TransferData transferData); + +/** + * Converts a platform specific representation of data to a java representation. + * + * @param transferData the platform specific representation of the data to be + * converted + * + * @return a java representation of the converted data if the conversion was + * successful; otherwise null. If transferData is <code>null</code> then + * <code>null</code> is returned. The type of Object that is returned is + * dependent on the <code>Transfer</code> subclass. + */ +abstract protected Object nativeToJava(TransferData transferData); + +/** + * Registers a name for a data type and returns the associated unique identifier. + * + * <p>You may register the same type more than once, the same unique identifier + * will be returned if the type has been previously registered.</p> + * + * <p>Note: On windows, do <b>not</b> call this method with pre-defined + * Clipboard Format types such as CF_TEXT or CF_BITMAP because the + * pre-defined identifier will not be returned</p> + * + * @param formatName the name of a data type + * + * @return the unique identifier associated with this data type + */ +public static int registerType(String formatName) { + // Look name up in the registry + // If name is not in registry, add it and return assigned value. + // If name already exists in registry, return its assigned value + TCHAR chFormatName = new TCHAR(0, formatName, true); + return OS.RegisterClipboardFormat(chFormatName); +} + +/** + * Test that the object is of the correct format for this Transfer class. + * + * @param object a java representation of the data to be converted + * + * @return true if object is of the correct form for this transfer type + * + * @since 3.1 + */ +protected boolean validate(Object object) { + return true; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TransferData.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TransferData.java new file mode 100755 index 0000000000..a1c57b813b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TransferData.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.internal.ole.win32.*; + +/** + * The <code>TransferData</code> class is a platform specific data structure for + * describing the type and the contents of data being converted by a transfer agent. + * + * <p>As an application writer, you do not need to know the specifics of + * TransferData. TransferData instances are passed to a subclass of Transfer + * and the Transfer object manages the platform specific issues. + * You can ask a Transfer subclass if it can handle this data by calling + * Transfer.isSupportedType(transferData).</p> + * + * <p>You should only need to become familiar with the fields in this class if you + * are implementing a Transfer subclass and you are unable to subclass the + * ByteArrayTransfer class.</p> + * + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + */ +public class TransferData { + /** + * The type is a unique identifier of a system format or user defined format. + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public int type; + + /** + * The formatetc structure is a generalized data transfer format, enhanced to + * encompass a target device, the aspect, or view of the data, and + * a storage medium. + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public FORMATETC formatetc; + + /** + * The stgmedium structure is a generalized global memory handle used for + * data transfer operations. + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public STGMEDIUM stgmedium; + + /** + * The result field contains the result of converting a + * java data type into a platform specific value. + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + * <p>The value of result is 1 if the conversion was successful. + * The value of result is 0 if the conversion failed.</p> + */ + public int result = COM.E_FAIL; + + /** + * The pIDataObject is the address of an IDataObject OLE Interface which + * provides access to the data associated with the transfer. + * (Warning: This field is platform dependent) + * <p> + * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT + * public API. It is marked public only so that it can be shared + * within the packages provided by SWT. It is not available on all + * platforms and should never be accessed from application code. + * </p> + */ + public int /*long*/ pIDataObject; + + static boolean sameType(TransferData data1, TransferData data2) { + if (data1 == data2) return true; + if (data1 == null || data2 == null) return false; + return (data1.type == data2.type && + data1.formatetc.cfFormat == data2.formatetc.cfFormat && + data1.formatetc.dwAspect == data2.formatetc.dwAspect && + data1.formatetc.tymed == data2.formatetc.tymed); + } + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDragSourceEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDragSourceEffect.java new file mode 100644 index 0000000000..645549a14f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDragSourceEffect.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.widgets.*; + +/** + * This class provides default implementations to display a source image + * when a drag is initiated from a <code>Tree</code>. + * + * <p>Classes that wish to provide their own source image for a <code>Tree</code> can + * extend <code>TreeDragSourceEffect</code> class and override the <code>TreeDragSourceEffect.dragStart</code> + * method and set the field <code>DragSourceEvent.image</code> with their own image.</p> + * + * Subclasses that override any methods of this class must call the corresponding + * <code>super</code> method to get the default drag under effect implementation. + * + * @see DragSourceEffect + * @see DragSourceEvent + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.3 + */ +public class TreeDragSourceEffect extends DragSourceEffect { + Image dragSourceImage = null; + + /** + * Creates a new <code>TreeDragSourceEffect</code> to handle drag effect + * from the specified <code>Tree</code>. + * + * @param tree the <code>Tree</code> that the user clicks on to initiate the drag + */ + public TreeDragSourceEffect(Tree tree) { + super(tree); + } + + /** + * This implementation of <code>dragFinished</code> disposes the image + * that was created in <code>TreeDragSourceEffect.dragStart</code>. + * + * Subclasses that override this method should call <code>super.dragFinished(event)</code> + * to dispose the image in the default implementation. + * + * @param event the information associated with the drag finished event + */ + public void dragFinished(DragSourceEvent event) { + if (dragSourceImage != null) dragSourceImage.dispose(); + dragSourceImage = null; + } + + /** + * This implementation of <code>dragStart</code> will create a default + * image that will be used during the drag. The image should be disposed + * when the drag is completed in the <code>TreeDragSourceEffect.dragFinished</code> + * method. + * + * Subclasses that override this method should call <code>super.dragStart(event)</code> + * to use the image from the default implementation. + * + * @param event the information associated with the drag start event + */ + public void dragStart(DragSourceEvent event) { + event.image = getDragSourceImage(event); + } + + Image getDragSourceImage(DragSourceEvent event) { + if (dragSourceImage != null) dragSourceImage.dispose(); + dragSourceImage = null; + if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) { + SHDRAGIMAGE shdi = new SHDRAGIMAGE(); + int DI_GETDRAGIMAGE = OS.RegisterWindowMessage (new TCHAR (0, "ShellGetDragImage", true)); //$NON-NLS-1$ + if (OS.SendMessage (control.handle, DI_GETDRAGIMAGE, 0, shdi) != 0) { + if ((control.getStyle() & SWT.MIRRORED) != 0) { + event.offsetX = shdi.sizeDragImage.cx - shdi.ptOffset.x; + } else { + event.offsetX = shdi.ptOffset.x; + } + event.offsetY = shdi.ptOffset.y; + int /*long*/ hImage = shdi.hbmpDragImage; + if (hImage != 0) { + BITMAP bm = new BITMAP (); + OS.GetObject (hImage, BITMAP.sizeof, bm); + int srcWidth = bm.bmWidth; + int srcHeight = bm.bmHeight; + + /* Create resources */ + int /*long*/ hdc = OS.GetDC (0); + int /*long*/ srcHdc = OS.CreateCompatibleDC (hdc); + int /*long*/ oldSrcBitmap = OS.SelectObject (srcHdc, hImage); + int /*long*/ memHdc = OS.CreateCompatibleDC (hdc); + BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER (); + bmiHeader.biSize = BITMAPINFOHEADER.sizeof; + bmiHeader.biWidth = srcWidth; + bmiHeader.biHeight = -srcHeight; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = 32; + bmiHeader.biCompression = OS.BI_RGB; + byte [] bmi = new byte[BITMAPINFOHEADER.sizeof]; + OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof); + int /*long*/ [] pBits = new int /*long*/ [1]; + int /*long*/ memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0); + if (memDib == 0) SWT.error (SWT.ERROR_NO_HANDLES); + int /*long*/ oldMemBitmap = OS.SelectObject (memHdc, memDib); + + BITMAP dibBM = new BITMAP (); + OS.GetObject (memDib, BITMAP.sizeof, dibBM); + int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight; + + /* Get the foreground pixels */ + OS.BitBlt (memHdc, 0, 0, srcWidth, srcHeight, srcHdc, 0, 0, OS.SRCCOPY); + byte[] srcData = new byte [sizeInBytes]; + OS.MoveMemory (srcData, dibBM.bmBits, sizeInBytes); + + PaletteData palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); + ImageData data = new ImageData(srcWidth, srcHeight, bm.bmBitsPixel, palette, bm.bmWidthBytes, srcData); + if (shdi.crColorKey == -1) { + byte[] alphaData = new byte[srcWidth * srcHeight]; + int spinc = dibBM.bmWidthBytes - srcWidth * 4; + int ap = 0, sp = 3; + for (int y = 0; y < srcHeight; ++y) { + for (int x = 0; x < srcWidth; ++x) { + alphaData [ap++] = srcData [sp]; + sp += 4; + } + sp += spinc; + } + data.alphaData = alphaData; + } else { + data.transparentPixel = shdi.crColorKey << 8; + } + dragSourceImage = new Image (control.getDisplay (), data); + OS.SelectObject (memHdc, oldMemBitmap); + OS.DeleteDC (memHdc); + OS.DeleteObject (memDib); + OS.SelectObject (srcHdc, oldSrcBitmap); + OS.DeleteDC (srcHdc); + OS.ReleaseDC (0, hdc); + OS.DeleteObject (hImage); + return dragSourceImage; + } + } + return null; + } + + Tree tree = (Tree) control; + //TEMPORARY CODE + if (tree.isListening (SWT.EraseItem) || tree.isListening (SWT.PaintItem)) return null; + TreeItem[] selection = tree.getSelection(); + if (selection.length == 0) return null; + int /*long*/ treeImageList = OS.SendMessage (tree.handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0); + if (treeImageList != 0) { + int count = Math.min(selection.length, 10); + Rectangle bounds = selection[0].getBounds(0); + for (int i = 1; i < count; i++) { + bounds = bounds.union(selection[i].getBounds(0)); + } + int /*long*/ hDC = OS.GetDC(tree.handle); + int /*long*/ hDC1 = OS.CreateCompatibleDC(hDC); + int /*long*/ bitmap = OS.CreateCompatibleBitmap(hDC, bounds.width, bounds.height); + int /*long*/ hOldBitmap = OS.SelectObject(hDC1, bitmap); + RECT rect = new RECT(); + rect.right = bounds.width; + rect.bottom = bounds.height; + int /*long*/ hBrush = OS.GetStockObject(OS.WHITE_BRUSH); + OS.FillRect(hDC1, rect, hBrush); + for (int i = 0; i < count; i++) { + TreeItem selected = selection[i]; + Rectangle cell = selected.getBounds(0); + int /*long*/ imageList = OS.SendMessage(tree.handle, OS.TVM_CREATEDRAGIMAGE, 0, selected.handle); + OS.ImageList_Draw(imageList, 0, hDC1, cell.x - bounds.x, cell.y - bounds.y, OS.ILD_SELECTED); + OS.ImageList_Destroy(imageList); + } + OS.SelectObject(hDC1, hOldBitmap); + OS.DeleteDC (hDC1); + OS.ReleaseDC (tree.handle, hDC); + Display display = tree.getDisplay(); + dragSourceImage = Image.win32_new(display, SWT.BITMAP, bitmap); + return dragSourceImage; + } + return null; + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDropTargetEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDropTargetEffect.java new file mode 100644 index 0000000000..04776faaac --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/TreeDropTargetEffect.java @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.widgets.*; + +/** + * This class provides a default drag under effect (eg. select, insert, scroll and expand) + * when a drag occurs over a <code>Tree</code>. + * + * <p>Classes that wish to provide their own drag under effect for a <code>Tree</code> + * can extend the <code>TreeDropTargetEffect</code> class and override any applicable methods + * in <code>TreeDropTargetEffect</code> to display their own drag under effect.</p> + * + * Subclasses that override any methods of this class must call the corresponding + * <code>super</code> method to get the default drag under effect implementation. + * + * <p>The feedback value is either one of the FEEDBACK constants defined in + * class <code>DND</code> which is applicable to instances of this class, + * or it must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>DND</code> effect constants. + * </p> + * <p> + * <dl> + * <dt><b>Feedback:</b></dt> + * <dd>FEEDBACK_SELECT, FEEDBACK_INSERT_BEFORE, FEEDBACK_INSERT_AFTER, FEEDBACK_EXPAND, FEEDBACK_SCROLL</dd> + * </dl> + * </p><p> + * Note: Only one of the styles FEEDBACK_SELECT, FEEDBACK_INSERT_BEFORE or + * FEEDBACK_INSERT_AFTER may be specified. + * </p> + * + * @see DropTargetAdapter + * @see DropTargetEvent + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.3 + */ +public class TreeDropTargetEffect extends DropTargetEffect { + static final int SCROLL_HYSTERESIS = 200; // milli seconds + static final int EXPAND_HYSTERESIS = 1000; // milli seconds + + int /*long*/ dropIndex; + int /*long*/ scrollIndex; + long scrollBeginTime; + int /*long*/ expandIndex; + long expandBeginTime; + TreeItem insertItem; + boolean insertBefore; + + /** + * Creates a new <code>TreeDropTargetEffect</code> to handle the drag under effect on the specified + * <code>Tree</code>. + * + * @param tree the <code>Tree</code> over which the user positions the cursor to drop the data + */ + public TreeDropTargetEffect(Tree tree) { + super(tree); + } + + int checkEffect(int effect) { + // Some effects are mutually exclusive. Make sure that only one of the mutually exclusive effects has been specified. + if ((effect & DND.FEEDBACK_SELECT) != 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER & ~DND.FEEDBACK_INSERT_BEFORE; + if ((effect & DND.FEEDBACK_INSERT_BEFORE) != 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER; + return effect; + } + + /** + * This implementation of <code>dragEnter</code> provides a default drag under effect + * for the feedback specified in <code>event.feedback</code>. + * + * For additional information see <code>DropTargetAdapter.dragEnter</code>. + * + * Subclasses that override this method should call <code>super.dragEnter(event)</code> + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag enter event + * + * @see DropTargetAdapter + * @see DropTargetEvent + */ + public void dragEnter(DropTargetEvent event) { + dropIndex = -1; + insertItem = null; + expandBeginTime = 0; + expandIndex = -1; + scrollBeginTime = 0; + scrollIndex = -1; + } + + /** + * This implementation of <code>dragLeave</code> provides a default drag under effect + * for the feedback specified in <code>event.feedback</code>. + * + * For additional information see <code>DropTargetAdapter.dragLeave</code>. + * + * Subclasses that override this method should call <code>super.dragLeave(event)</code> + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag leave event + * + * @see DropTargetAdapter + * @see DropTargetEvent + */ + public void dragLeave(DropTargetEvent event) { + Tree tree = (Tree) control; + int /*long*/ handle = tree.handle; + if (dropIndex != -1) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = dropIndex; + tvItem.mask = OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_DROPHILITED; + tvItem.state = 0; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + dropIndex = -1; + } + if (insertItem != null) { + tree.setInsertMark(null, false); + insertItem = null; + } + expandBeginTime = 0; + expandIndex = -1; + scrollBeginTime = 0; + scrollIndex = -1; + } + + /** + * This implementation of <code>dragOver</code> provides a default drag under effect + * for the feedback specified in <code>event.feedback</code>. + * + * For additional information see <code>DropTargetAdapter.dragOver</code>. + * + * Subclasses that override this method should call <code>super.dragOver(event)</code> + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag over event + * + * @see DropTargetAdapter + * @see DropTargetEvent + * @see DND#FEEDBACK_SELECT + * @see DND#FEEDBACK_INSERT_BEFORE + * @see DND#FEEDBACK_INSERT_AFTER + * @see DND#FEEDBACK_SCROLL + */ + public void dragOver(DropTargetEvent event) { + Tree tree = (Tree) getControl(); + int effect = checkEffect(event.feedback); + int /*long*/ handle = tree.handle; + Point coordinates = new Point(event.x, event.y); + coordinates = tree.toControl(coordinates); + TVHITTESTINFO lpht = new TVHITTESTINFO (); + lpht.x = coordinates.x; + lpht.y = coordinates.y; + OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); + int /*long*/ hItem = lpht.hItem; + if ((effect & DND.FEEDBACK_SCROLL) == 0) { + scrollBeginTime = 0; + scrollIndex = -1; + } else { + if (hItem != -1 && scrollIndex == hItem && scrollBeginTime != 0) { + if (System.currentTimeMillis() >= scrollBeginTime) { + int /*long*/ topItem = OS.SendMessage(handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + int /*long*/ nextItem = OS.SendMessage(handle, OS.TVM_GETNEXTITEM, hItem == topItem ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE, hItem); + boolean scroll = true; + if (hItem == topItem) { + scroll = nextItem != 0; + } else { + RECT itemRect = new RECT (); + if (OS.TreeView_GetItemRect (handle, nextItem, itemRect, true)) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + POINT pt = new POINT (); + pt.x = itemRect.left; + pt.y = itemRect.top; + if (OS.PtInRect (rect, pt)) { + pt.y = itemRect.bottom; + if (OS.PtInRect (rect, pt)) scroll = false; + } + } + } + if (scroll) { + OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, nextItem); + tree.redraw(); + } + scrollBeginTime = 0; + scrollIndex = -1; + } + } else { + scrollBeginTime = System.currentTimeMillis() + SCROLL_HYSTERESIS; + scrollIndex = hItem; + } + } + if ((effect & DND.FEEDBACK_EXPAND) == 0) { + expandBeginTime = 0; + expandIndex = -1; + } else { + if (hItem != -1 && expandIndex == hItem && expandBeginTime != 0) { + if (System.currentTimeMillis() >= expandBeginTime) { + if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem) != 0) { + TreeItem item = (TreeItem)tree.getDisplay().findWidget(tree.handle, hItem); + if (item != null && !item.getExpanded()) { + item.setExpanded(true); + tree.redraw(); + Event expandEvent = new Event (); + expandEvent.item = item; + tree.notifyListeners(SWT.Expand, expandEvent); + } + } + expandBeginTime = 0; + expandIndex = -1; + } + } else { + expandBeginTime = System.currentTimeMillis() + EXPAND_HYSTERESIS; + expandIndex = hItem; + } + } + if (dropIndex != -1 && (dropIndex != hItem || (effect & DND.FEEDBACK_SELECT) == 0)) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = dropIndex; + tvItem.mask = OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_DROPHILITED; + tvItem.state = 0; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + dropIndex = -1; + } + if (hItem != -1 && hItem != dropIndex && (effect & DND.FEEDBACK_SELECT) != 0) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = hItem; + tvItem.mask = OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_DROPHILITED; + tvItem.state = OS.TVIS_DROPHILITED; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + dropIndex = hItem; + } + if ((effect & DND.FEEDBACK_INSERT_BEFORE) != 0 || (effect & DND.FEEDBACK_INSERT_AFTER) != 0) { + boolean before = (effect & DND.FEEDBACK_INSERT_BEFORE) != 0; + /* + * Bug in Windows. When TVM_SETINSERTMARK is used to set + * an insert mark for a tree and an item is expanded or + * collapsed near the insert mark, the tree does not redraw + * the insert mark properly. The fix is to hide and show + * the insert mark whenever an item is expanded or collapsed. + * Since the insert mark can not be queried from the tree, + * use the Tree API rather than calling the OS directly. + */ + TreeItem item = (TreeItem)tree.getDisplay().findWidget(tree.handle, hItem); + if (item != null) { + if (item != insertItem || before != insertBefore) { + tree.setInsertMark(item, before); + } + insertItem = item; + insertBefore = before; + } else { + if (insertItem != null) { + tree.setInsertMark(null, false); + } + insertItem = null; + } + } else { + if (insertItem != null) { + tree.setInsertMark(null, false); + } + insertItem = null; + } + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/URLTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/URLTransfer.java new file mode 100644 index 0000000000..5aedfb1ff1 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/win32/org/eclipse/swt/dnd/URLTransfer.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2000, 20007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.internal.ole.win32.*; +import org.eclipse.swt.internal.win32.*; + +/** + * The class <code>URLTransfer</code> provides a platform specific mechanism + * for converting text in URL format represented as a java <code>String</code> + * to a platform specific representation of the data and vice versa. The string + * must contain a fully specified url. + * + * <p>An example of a java <code>String</code> containing a URL is shown below:</p> + * + * <code><pre> + * String url = "http://www.eclipse.org"; + * </code></pre> + * + * @see Transfer + * @since 3.4 + */ +public class URLTransfer extends ByteArrayTransfer { + + static URLTransfer _instance = new URLTransfer(); + static final String CFSTR_INETURL = "UniformResourceLocator"; //$NON-NLS-1$ + static final int CFSTR_INETURLID = registerType(CFSTR_INETURL); + +private URLTransfer() {} + +/** + * Returns the singleton instance of the URLTransfer class. + * + * @return the singleton instance of the URLTransfer class + */ +public static URLTransfer getInstance () { + return _instance; +} + +/** + * This implementation of <code>javaToNative</code> converts a URL + * represented by a java <code>String</code> to a platform specific representation. + * + * @param object a java <code>String</code> containing a URL + * @param transferData an empty <code>TransferData</code> object that will + * be filled in on return with the platform specific format of the data + * + * @see Transfer#nativeToJava + */ +public void javaToNative (Object object, TransferData transferData){ + if (!checkURL(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + transferData.result = COM.E_FAIL; + // URL is stored as a null terminated byte array + String url = ((String)object); + int count = url.length(); + char[] chars = new char[count + 1]; + url.getChars(0, count, chars, 0); + int codePage = OS.GetACP(); + int cchMultiByte = OS.WideCharToMultiByte(codePage, 0, chars, -1, null, 0, null, null); + if (cchMultiByte == 0) { + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.DV_E_STGMEDIUM; + return; + } + int /*long*/ lpMultiByteStr = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, cchMultiByte); + OS.WideCharToMultiByte(codePage, 0, chars, -1, lpMultiByteStr, cchMultiByte, null, null); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = lpMultiByteStr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + return; +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform + * specific representation of a URL to a java <code>String</code>. + * + * @param transferData the platform specific representation of the data to be converted + * @return a java <code>String</code> containing a URL if the conversion was successful; + * otherwise null + * + * @see Transfer#javaToNative + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.pIDataObject == 0) return null; + IDataObject data = new IDataObject(transferData.pIDataObject); + data.AddRef(); + STGMEDIUM stgmedium = new STGMEDIUM(); + FORMATETC formatetc = transferData.formatetc; + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = getData(data, formatetc, stgmedium); + data.Release(); + if (transferData.result != COM.S_OK) return null; + int /*long*/ hMem = stgmedium.unionField; + try { + int /*long*/ lpMultiByteStr = OS.GlobalLock(hMem); + if (lpMultiByteStr == 0) return null; + try { + int codePage = OS.GetACP(); + int cchWideChar = OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, null, 0); + if (cchWideChar == 0) return null; + char[] lpWideCharStr = new char [cchWideChar - 1]; + OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, lpWideCharStr, lpWideCharStr.length); + return new String(lpWideCharStr); + } finally { + OS.GlobalUnlock(hMem); + } + } finally { + OS.GlobalFree(hMem); + } +} + +protected int[] getTypeIds(){ + return new int[] {CFSTR_INETURLID}; +} + +protected String[] getTypeNames(){ + return new String[] {CFSTR_INETURL}; +} + +boolean checkURL(Object object) { + return object != null && (object instanceof String) && ((String)object).length() > 0; +} + +protected boolean validate(Object object) { + return checkURL(object); +} +} |