diff options
author | Carolyn MacLeod <carolyn> | 2008-01-31 16:28:05 +0000 |
---|---|---|
committer | Carolyn MacLeod <carolyn> | 2008-01-31 16:28:05 +0000 |
commit | 7f3d866c4140cd4f8ccd06beca69339b6dfdde1e (patch) | |
tree | 85e76f04eb718297017548595bb42d4ded026d6a | |
parent | 57bc9da816b78e8225c369df2233b6334cde29f9 (diff) | |
download | eclipse.platform.swt-7f3d866c4140cd4f8ccd06beca69339b6dfdde1e.tar.gz eclipse.platform.swt-7f3d866c4140cd4f8ccd06beca69339b6dfdde1e.tar.xz eclipse.platform.swt-7f3d866c4140cd4f8ccd06beca69339b6dfdde1e.zip |
initial - copy of carbon
16 files changed, 3599 insertions, 0 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/ByteArrayTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/ByteArrayTransfer.java new file mode 100644 index 0000000000..c138c1c0e8 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/ByteArrayTransfer.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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; + + +/** + * 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. See + * <code>Transfer</code> for additional information. + * + * <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> + */ +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]; + } + return data; +} + +public boolean isSupportedType(TransferData transferData){ + if (transferData == null) return false; + int[] types = getTypeIds(); + for (int i = 0; i < types.length; i++) { + if (transferData.type == types[i]) return true; + } + return false; +} + +/** + * This implementation of <code>javaToNative</code> converts a java + * <code>byte[]</code> to a platform specific representation. For additional + * information see <code>Transfer#javaToNative</code>. + * + * @see Transfer#javaToNative + * + * @param object a java <code>byte[]</code> containing the data to be converted + * @param transferData an empty <code>TransferData</code> object; this + * object will be filled in on return with the platform specific format of the data + */ +protected void javaToNative (Object object, TransferData transferData) { + if (!checkByteArray(object) && !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + byte[] orig = (byte[])object; + byte[] buffer = new byte[orig.length]; + System.arraycopy(orig, 0, buffer, 0, orig.length); + transferData.data = new byte[1][]; + transferData.data[0] = buffer; + transferData.result = 0; +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform specific + * representation of a byte array to a java <code>byte[]</code>. + * For additional information see <code>Transfer#nativeToJava</code>. + * + * @see Transfer#nativeToJava + * + * @param transferData the platform specific representation of the data to be + * been converted + * @return a java <code>byte[]</code> containing the converted data if the + * conversion was successful; otherwise null + */ +protected Object nativeToJava(TransferData transferData) { + if (!isSupportedType(transferData) || transferData.data == null) return null; + if (transferData.data.length == 0 || transferData.data[0].length == 0) return null; + return transferData.data[0]; +} +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/cocoa/org/eclipse/swt/dnd/Clipboard.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/Clipboard.java new file mode 100644 index 0000000000..fc4839d51b --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/Clipboard.java @@ -0,0 +1,567 @@ +/******************************************************************************* + * 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.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.internal.carbon.OS; + +/** + * 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> + */ +public class Clipboard { + + Display display; + int scrap = 0; + +/** + * 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; +} + +/** + * 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 || scrap == 0) return; + int oldScrap = scrap; + scrap = 0; + int[] currentScrap = new int[1]; + if (OS.GetCurrentScrap(currentScrap) != OS.noErr) return; + if (currentScrap[0] == oldScrap) { + OS.ClearCurrentScrap(); + } +} + +/** + * 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); + 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; + int[] scrap = new int[1]; + if (OS.GetCurrentScrap(scrap) != OS.noErr) return null; + int[] typeIds = transfer.getTypeIds(); + int[] size = new int[1]; + // get data from system clipboard + for (int i=0; i<typeIds.length; i++) { + int type = typeIds[i]; + size[0] = 0; + if (OS.GetScrapFlavorSize(scrap[0], type, size) == OS.noErr && size[0] > 0) { + byte[] buffer = new byte[size[0]]; + if (OS.GetScrapFlavorData(scrap[0], type, size, buffer) == OS.noErr) { + TransferData tdata = new TransferData(); + tdata.type = type; + tdata.data = new byte[1][]; + tdata.data[0] = buffer; + return transfer.nativeToJava(tdata); + } + } + } + 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; + if (OS.ClearCurrentScrap() != OS.noErr) { + DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD); + } + scrap = 0; + int[] currentScrap = new int[1]; + if (OS.GetCurrentScrap(currentScrap) != OS.noErr) { + DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD); + } + scrap = currentScrap[0]; + // copy data directly over to System clipboard (not deferred) + for (int i=0; i<dataTypes.length; i++) { + int[] typeIds = dataTypes[i].getTypeIds(); + for (int j=0; j<typeIds.length; j++) { + TransferData transferData = new TransferData(); + transferData.type = typeIds[j]; + dataTypes[i].javaToNative(data[i], transferData); + if (transferData.result != OS.noErr) { + DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD); + } + //Drag and Drop can handle multiple items in one transfer but the + //Clipboard can not. + byte[] datum = transferData.data[0]; + if (OS.PutScrapFlavor(scrap, transferData.type, 0, datum.length, datum) != OS.noErr){ + DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD); + } + } + } +} + +/** + * 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]; + int[] types = _getAvailableTypes(); + TransferData[] result = new TransferData[types.length]; + for (int i = 0; i < types.length; i++) { + result[i] = new TransferData(); + result[i].type = types[i]; + } + return result; +} + +/** + * 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(); + int[] types = _getAvailableTypes(); + String[] names = new String[types.length]; + for (int i = 0; i < types.length; i++) { + int type = types[i]; + StringBuffer sb = new StringBuffer(); + sb.append((char)((type & 0xff000000) >> 24)); + sb.append((char)((type & 0x00ff0000) >> 16)); + sb.append((char)((type & 0x0000ff00) >> 8)); + sb.append((char)((type & 0x000000ff) >> 0)); + names[i] = sb.toString(); + } + return names; +} + +int[] _getAvailableTypes() { + int[] types = new int[0]; + int[] scrap = new int[1]; + if (OS.GetCurrentScrap(scrap) != OS.noErr) return types; + int[] count = new int[1]; + if (OS.GetScrapFlavorCount(scrap[0], count) != OS.noErr || count[0] == 0) return types; + int[] info = new int[count[0] * 2]; + if (OS.GetScrapFlavorInfoList(scrap[0], count, info) != OS.noErr) return types; + types = new int[count[0]]; + for (int i= 0; i < count [0]; i++) { + types[i] = info[i*2]; + } + return types; +} +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/DragSource.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/DragSource.java new file mode 100644 index 0000000000..cd00d4444d --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/DragSource.java @@ -0,0 +1,521 @@ +/******************************************************************************* + * 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.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.internal.Callback; +import org.eclipse.swt.internal.carbon.CGPoint; +import org.eclipse.swt.internal.carbon.EventRecord; +import org.eclipse.swt.internal.carbon.OS; +import org.eclipse.swt.internal.carbon.Point; + +/** + * + * <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> + */ +public class DragSource extends Widget { + + // info for registering as a drag source + Control control; + Listener controlListener; + Transfer[] transferAgents = new Transfer[0]; + DragSourceEffect dragEffect; + + static final String DEFAULT_DRAG_SOURCE_EFFECT = "DEFAULT_DRAG_SOURCE_EFFECT"; //$NON-NLS-1$ + static Callback DragSendDataProc; + + static { + DragSendDataProc = new Callback(DragSource.class, "DragSendDataProc", 4); //$NON-NLS-1$ + int dragSendDataProcAddress = DragSendDataProc.getAddress(); + if (dragSendDataProcAddress == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + } + +/** + * 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); + + 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) { + 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; +} + +static int DragSendDataProc(int theType, int dragSendRefCon, int theItemRef, int theDrag) { + DragSource source = FindDragSource(dragSendRefCon, theDrag); + if (source == null) return OS.cantGetFlavorErr; + return source.dragSendDataProc(theType, dragSendRefCon, theItemRef, theDrag); +} + +static DragSource FindDragSource(int dragSendRefCon, int theDrag) { + if (dragSendRefCon == 0) return null; + Display display = Display.findDisplay(Thread.currentThread()); + if (display == null || display.isDisposed()) return null; + Widget widget = display.findWidget(dragSendRefCon); + if (widget == null) return null; + return (DragSource)widget.getData(DND.DRAG_SOURCE_KEY); +} + +/** + * 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 #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); +} + +protected void checkSubclass () { + String name = getClass().getName (); + String validName = DragSource.class.getName(); + if (!validName.equals(name)) { + DND.error (SWT.ERROR_INVALID_SUBCLASS); + } +} + +void drag(Event dragEvent) { + DNDEvent event = new DNDEvent(); + event.widget = this; + event.x = dragEvent.x; + event.y = dragEvent.y; + event.time = dragEvent.time; + event.doit = true; + notifyListeners(DND.DragStart, event); + if (!event.doit || transferAgents == null || transferAgents.length == 0) return; + + int[] theDrag = new int[1]; + if (OS.NewDrag(theDrag) != OS.noErr) { + event = new DNDEvent(); + event.widget = this; + event.time = (int)System.currentTimeMillis(); + event.doit = false; + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragEnd, event); + return; + } + + Point pt = new Point(); + OS.GetGlobalMouse (pt); + + for (int i = 0; i < transferAgents.length; i++) { + Transfer transfer = transferAgents[i]; + if (transfer != null) { + int[] types = transfer.getTypeIds(); + if (transfer instanceof FileTransfer) { + TransferData transferData = new TransferData(); + transferData.type = types[0]; + DNDEvent event2 = new DNDEvent(); + event2.widget = this; + event2.time = (int)System.currentTimeMillis(); + event2.dataType = transferData; + notifyListeners(DND.DragSetData, event2); + if (event2.data != null) { + for (int j = 0; j < types.length; j++) { + transferData.type = types[j]; + transfer.javaToNative(event2.data, transferData); + if (transferData.result == OS.noErr) { + for (int k = 0; k < transferData.data.length; k++) { + byte[] datum = transferData.data[k]; + OS.AddDragItemFlavor(theDrag[0], 1 + k, types[j], datum, datum.length, 0); + } + } + } + } + } else { + for (int j = 0; j < types.length; j++) { + OS.AddDragItemFlavor(theDrag[0], 1, types[j], null, 0, 0); + } + } + } + } + + OS.SetDragSendProc(theDrag[0], DragSendDataProc.getAddress(), control.handle); + + int theRegion = 0; + Image newImage = null; + try { + theRegion = OS.NewRgn(); + OS.SetRectRgn(theRegion, (short)(pt.h), (short)(pt.v), (short)(pt.h+20), (short)(pt.v+20)); + + int operations = opToOsOp(getStyle()); + //set operations twice - local and not local + OS.SetDragAllowableActions(theDrag[0], operations, true); + OS.SetDragAllowableActions(theDrag[0], operations, false); + + Image image = event.image; + if (image != null) { + CGPoint imageOffsetPt = new CGPoint(); + imageOffsetPt.x = 0; + imageOffsetPt.y = 0; + /* + * Bug in the Macintosh. For some reason, it seems that SetDragImageWithCGImage() + * expects an image with the alpha, otherwise the image does not draw. The fix is + * to make sure that the image has an alpha by creating a new image with alpha + * when necessary. + */ + if (OS.CGImageGetAlphaInfo(image.handle) == OS.kCGImageAlphaNoneSkipFirst) { + ImageData data = image.getImageData(); + data.alpha = 0xFF; + newImage = new Image(image.getDevice(), data); + image = newImage; + } + OS.SetDragImageWithCGImage(theDrag[0], image.handle, imageOffsetPt, 0); + } + EventRecord theEvent = new EventRecord(); + theEvent.message = OS.kEventMouseMoved; + theEvent.modifiers = (short)OS.GetCurrentEventKeyModifiers(); + theEvent.what = (short)OS.osEvt; + theEvent.where_h = pt.h; + theEvent.where_v = pt.v; + int result = OS.TrackDrag(theDrag[0], theEvent, theRegion); + int operation = DND.DROP_NONE; + if (result == OS.noErr) { + int[] outAction = new int[1]; + OS.GetDragDropAction(theDrag[0], outAction); + operation = osOpToOp(outAction[0]); + } + event = new DNDEvent(); + event.widget = this; + event.time = (int)System.currentTimeMillis(); + event.doit = result == OS.noErr; + event.detail = operation; + notifyListeners(DND.DragEnd, event); + } finally { + if (theRegion != 0) OS.DisposeRgn(theRegion); + if (newImage != null) newImage.dispose(); + } + OS.DisposeDrag(theDrag[0]); +} + +int dragSendDataProc(int theType, int dragSendRefCon, int theItemRef, int theDrag) { + if (theType == 0) return OS.badDragFlavorErr; + TransferData transferData = new TransferData(); + transferData.type = theType; + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = (int)System.currentTimeMillis(); + event.dataType = transferData; + notifyListeners(DND.DragSetData, event); + 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 OS.badDragFlavorErr; + transfer.javaToNative(event.data, transferData); + if (transferData.result != OS.noErr) return transferData.result; + // Except for FileTransfer (see #drag), only one item can be transferred + // in a Drag operation + byte[] datum = transferData.data[0]; + if (datum == null) return OS.cantGetFlavorErr; + return OS.SetDragItemFlavorData(theDrag, theItemRef, theType, datum, datum.length, 0); +} + +/** + * 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; +} + +/** + * 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; +} + +void onDispose() { + if (control == null) + return; + 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; +} + +int opToOsOp(int operation) { + int osOperation = 0; + if ((operation & DND.DROP_COPY) != 0){ + osOperation |= OS.kDragActionCopy; + } + if ((operation & DND.DROP_LINK) != 0) { + osOperation |= OS.kDragActionAlias; + } + if ((operation & DND.DROP_MOVE) != 0) { + osOperation |= OS.kDragActionMove; + } + if ((operation & DND.DROP_TARGET_MOVE) != 0) { + osOperation |= OS.kDragActionDelete; + } + return osOperation; +} + +int osOpToOp(int osOperation){ + int operation = 0; + if ((osOperation & OS.kDragActionCopy) != 0){ + operation |= DND.DROP_COPY; + } + if ((osOperation & OS.kDragActionAlias) != 0) { + operation |= DND.DROP_LINK; + } + if ((osOperation & OS.kDragActionDelete) != 0) { + operation |= DND.DROP_TARGET_MOVE; + } + if ((osOperation & OS.kDragActionMove) != 0) { + operation |= DND.DROP_MOVE; + } + if (osOperation == OS.kDragActionAll) { + operation = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK; + } + return operation; +} + +/** + * 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 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 + */ +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); +} + +/** + * 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/cocoa/org/eclipse/swt/dnd/DropTarget.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/DropTarget.java new file mode 100644 index 0000000000..24918055dd --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/DropTarget.java @@ -0,0 +1,769 @@ +/******************************************************************************* + * 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.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.carbon.*; + +/** + * + * Class <code>DropTarget</code> defines the target object for a drag and drop transfer. + * + * IMPORTANT: This class is <em>not</em> intended to be subclassed. + * + * <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> + */ +public class DropTarget extends Widget { + + Control control; + Listener controlListener; + Transfer[] transferAgents = new Transfer[0]; + DropTargetEffect dropEffect; + int feedback = DND.FEEDBACK_NONE; + + // 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 - Simulate events when mouse is not moving + long dragOverStart; + Runnable dragOverHeartbeat; + DNDEvent dragOverEvent; + + // workaround - OS events are relative to the application, not the control. + // Track which control is the current target to determine when drag and + // drop enters or leaves a widget. + static DropTarget CurrentDropTarget = null; + + static final String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$ + static final int DRAGOVER_HYSTERESIS = 50; + + static Callback DragTrackingHandler; + static Callback DragReceiveHandler; + + static { + DragTrackingHandler = new Callback(DropTarget.class, "DragTrackingHandler", 4); //$NON-NLS-1$ + int dragTrackingHandlerAddress = DragTrackingHandler.getAddress(); + if (dragTrackingHandlerAddress == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + DragReceiveHandler = new Callback(DropTarget.class, "DragReceiveHandler", 3); //$NON-NLS-1$ + int dragReceiveHandlerAddress = DragReceiveHandler.getAddress(); + if (dragReceiveHandlerAddress == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + OS.InstallTrackingHandler(dragTrackingHandlerAddress, 0, null); + OS.InstallReceiveHandler(dragReceiveHandlerAddress, 0, null); + } + +/** + * 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 (DragTrackingHandler == null || DragTrackingHandler == null) { + DND.error(DND.ERROR_CANNOT_INIT_DROP); + } + if (control.getData(DND.DROP_TARGET_KEY) != null) { + DND.error(DND.ERROR_CANNOT_INIT_DROP); + } + control.setData(DND.DROP_TARGET_KEY, this); + + 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); + } + + dragOverHeartbeat = new Runnable() { + public void run() { + Control control = DropTarget.this.control; + if (control == null || control.isDisposed() || dragOverStart == 0) return; + long time = System.currentTimeMillis(); + int delay = DRAGOVER_HYSTERESIS; + if (time < dragOverStart) { + delay = (int)(dragOverStart - time); + } else { + int allowedOperations = dragOverEvent.operations; + TransferData[] allowedTypes = dragOverEvent.dataTypes; + //pass a copy of data types in to listeners in case application modifies it + TransferData[] dataTypes = new TransferData[allowedTypes.length]; + System.arraycopy(allowedTypes, 0, dataTypes, 0, dataTypes.length); + + DNDEvent event = new DNDEvent(); + event.widget = dragOverEvent.widget; + event.x = dragOverEvent.x; + event.y = dragOverEvent.y; + event.time = (int)time; + event.feedback = DND.FEEDBACK_SELECT; + event.dataTypes = dataTypes; + event.dataType = selectedDataType; + event.operations = dragOverEvent.operations; + event.detail = selectedOperation; + if (dropEffect != null) { + event.item = dropEffect.getItem(event.x, event.y); + } + selectedDataType = null; + selectedOperation = DND.DROP_NONE; + notifyListeners(DND.DragOver, event); + if (event.dataType != null) { + for (int i = 0; i < allowedTypes.length; i++) { + if (allowedTypes[i].type == event.dataType.type) { + selectedDataType = event.dataType; + break; + } + } + } + if (selectedDataType != null && (event.detail & allowedOperations) != 0) { + selectedOperation = event.detail; + } + } + control = DropTarget.this.control; + if (control == null || control.isDisposed()) return; + control.getDisplay().timerExec(delay, dragOverHeartbeat); + } + }; +} + +static int checkStyle (int style) { + if (style == SWT.NONE) return DND.DROP_MOVE; + return style; +} + +static int DragReceiveHandler(int theWindow, int handlerRefCon, int theDrag) { + DropTarget target = FindDropTarget(theWindow, theDrag); + if (target == null) return OS.noErr; + return target.dragReceiveHandler(theWindow, handlerRefCon, theDrag); +} + +static int DragTrackingHandler(int message, int theWindow, int handlerRefCon, int theDrag) { + if (message == OS.kDragTrackingLeaveHandler || message == OS.kDragTrackingEnterHandler) { + CurrentDropTarget = null; + return OS.noErr; + } + DropTarget target = FindDropTarget(theWindow, theDrag); + if (CurrentDropTarget != null) { + if (target == null || CurrentDropTarget.control.handle != target.control.handle) { + CurrentDropTarget.dragTrackingHandler(OS.kDragTrackingLeaveWindow, theWindow, handlerRefCon, theDrag); + CurrentDropTarget = target; + message = OS.kDragTrackingEnterWindow; + } + } else { + CurrentDropTarget = target; + message = OS.kDragTrackingEnterWindow; + } + if (target == null) return OS.noErr; + return target.dragTrackingHandler(message, theWindow, handlerRefCon, theDrag); +} + +static DropTarget FindDropTarget(int theWindow, int theDrag) { + Display display = Display.findDisplay(Thread.currentThread()); + if (display == null || display.isDisposed()) return null; + Point mouse = new Point(); + OS.GetDragMouse(theDrag, mouse, null); + int[] theRoot = new int[1]; + OS.GetRootControl(theWindow, theRoot); + int[] theControl = new int[1]; + Rect rect = new Rect(); + OS.GetWindowBounds (theWindow, (short) OS.kWindowContentRgn, rect); + CGPoint inPoint = new CGPoint(); + inPoint.x = mouse.h - rect.left; + inPoint.y = mouse.v - rect.top; + OS.HIViewGetSubviewHit(theRoot[0], inPoint, true, theControl); + if (!OS.IsControlEnabled(theControl[0])) return null; + Widget widget = display.findWidget(theControl[0]); + if (widget == null) return null; + return (DropTarget)widget.getData(DND.DROP_TARGET_KEY); +} +/** + * 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 #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); +} + +protected void checkSubclass () { + String name = getClass().getName (); + String validName = DropTarget.class.getName(); + if (!validName.equals(name)) { + DND.error (SWT.ERROR_INVALID_SUBCLASS); + } +} + +int dragReceiveHandler(int theWindow, int handlerRefCon, int theDrag) { + updateDragOverHover(0, null); + if (keyOperation == -1) return OS.dragNotAcceptedErr; + + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = (int)System.currentTimeMillis(); + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragLeave, event); + + event = new DNDEvent(); + if (!setEventData(theDrag, event)) { + return OS.dragNotAcceptedErr; + } + + keyOperation = -1; + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, event.dataTypes.length); + event.dataType = selectedDataType; + event.detail = selectedOperation; + selectedDataType = null; + selectedOperation = DND.DROP_NONE; + notifyListeners(DND.DropAccept, event); + + if (event.dataType != null) { + for (int i = 0; i < allowedDataTypes.length; i++) { + if (allowedDataTypes[i].type == event.dataType.type) { + selectedDataType = allowedDataTypes[i]; + break; + } + } + } + if (selectedDataType != null && (event.detail & allowedOperations) != 0) { + selectedOperation = event.detail; + } + if (selectedOperation == DND.DROP_NONE) { + // this was not a successful drop + return OS.dragNotAcceptedErr; + } + // ask drag source for dropped data + byte[][] data = new byte[0][]; + // locate all the items with data of the desired type + short[] numItems = new short[1]; + OS.CountDragItems(theDrag, numItems); + for (short i = 0; i < numItems[0]; i++) { + int[] theItemRef = new int[1]; + OS.GetDragItemReferenceNumber(theDrag, (short) (i+1), theItemRef); + int[] size = new int[1]; + OS.GetFlavorDataSize(theDrag, theItemRef[0], selectedDataType.type, size); + if (size[0] > 0) { + byte[] buffer = new byte[size[0]]; + OS.GetFlavorData(theDrag, theItemRef[0], selectedDataType.type, buffer, size, 0); + byte[][] newData = new byte[data.length + 1][]; + System.arraycopy(data, 0, newData, 0, data.length); + newData[data.length] = buffer; + data = newData; + } + } + // 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)) { + selectedDataType.data = data; + object = transfer.nativeToJava(selectedDataType); + break; + } + } + + if (object == null) { + selectedOperation = DND.DROP_NONE; + } + + event.dataType = selectedDataType; + event.detail = selectedOperation; + event.data = object; + notifyListeners(DND.Drop, event); + selectedOperation = DND.DROP_NONE; + if ((allowedOperations & event.detail) == event.detail) { + selectedOperation = event.detail; + } + //notify source of action taken + int action = opToOsOp(selectedOperation); + OS.SetDragDropAction(theDrag, action); + return (selectedOperation == DND.DROP_NONE) ? OS.dragNotAcceptedErr : OS.noErr; +} + +int dragTrackingHandler(int message, int theWindow, int handlerRefCon, int theDrag) { + + if (message == OS.kDragTrackingLeaveWindow) { + updateDragOverHover(0, null); + OS.SetThemeCursor(OS.kThemeArrowCursor); + if (keyOperation == -1) return OS.dragNotAcceptedErr; + keyOperation = -1; + + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = (int)System.currentTimeMillis(); + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragLeave, event); + return OS.noErr; + } + + int oldKeyOperation = keyOperation; + + if (message == OS.kDragTrackingEnterWindow) { + selectedDataType = null; + selectedOperation = 0; + } + + DNDEvent event = new DNDEvent(); + if (!setEventData(theDrag, event)) { + keyOperation = -1; + OS.SetThemeCursor(OS.kThemeNotAllowedCursor); + return OS.dragNotAcceptedErr; + } + + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + + switch (message) { + case OS.kDragTrackingEnterWindow: + event.type = DND.DragEnter; + break; + case OS.kDragTrackingInWindow: + if (keyOperation == oldKeyOperation) { + event.type = DND.DragOver; + event.dataType = selectedDataType; + event.detail = selectedOperation; + }else { + event.type = DND.DragOperationChanged; + event.dataType = selectedDataType; + } + break; + } + + updateDragOverHover(DRAGOVER_HYSTERESIS, event); + selectedDataType = null; + selectedOperation = DND.DROP_NONE; + notifyListeners(event.type, event); + + if (event.detail == DND.DROP_DEFAULT) { + event.detail = (allowedOperations & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + + if (event.dataType != null) { + for (int i = 0; i < allowedDataTypes.length; i++) { + if (allowedDataTypes[i].type == event.dataType.type) { + selectedDataType = allowedDataTypes[i]; + break; + } + } + } + + if (selectedDataType != null && (allowedOperations & event.detail) != 0) { + selectedOperation = event.detail; + } + + OS.SetDragDropAction(theDrag, opToOsOp(selectedOperation)); + + switch (selectedOperation) { + case DND.DROP_COPY: + OS.SetThemeCursor(OS.kThemeCopyArrowCursor); + break; + case DND.DROP_LINK: + OS.SetThemeCursor(OS.kThemeAliasArrowCursor); + break; + case DND.DROP_MOVE: + OS.SetThemeCursor(OS.kThemeArrowCursor); + break; + default: + OS.SetThemeCursor(OS.kThemeNotAllowedCursor); + } + + if (message == OS.kDragTrackingEnterWindow) { + dragOverHeartbeat.run(); + } + return OS.noErr; +} + +/** + * 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 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 theDrag) { + short[] modifiers = new short[1]; + OS.GetDragModifiers(theDrag, modifiers, null, null); + boolean option = (modifiers[0] & OS.optionKey) == OS.optionKey; + boolean command = (modifiers[0] & OS.cmdKey) == OS.cmdKey; + if (option && command) return DND.DROP_LINK; + if (option) return DND.DROP_COPY; + if (command) 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; + if (controlListener != null) + control.removeListener(SWT.Dispose, controlListener); + controlListener = null; + control.setData(DND.DROP_TARGET_KEY, null); + transferAgents = null; + control = null; +} + +int opToOsOp(int operation) { + int osOperation = 0; + if ((operation & DND.DROP_COPY) != 0){ + osOperation |= OS.kDragActionCopy; + } + if ((operation & DND.DROP_LINK) != 0) { + osOperation |= OS.kDragActionAlias; + } + if ((operation & DND.DROP_MOVE) != 0) { + osOperation |= OS.kDragActionMove; + } + return osOperation; +} + +int osOpToOp(int osOperation){ + int operation = 0; + if ((osOperation & OS.kDragActionCopy) != 0){ + operation |= DND.DROP_COPY; + } + if ((osOperation & OS.kDragActionAlias) != 0) { + operation |= DND.DROP_LINK; + } + if ((osOperation & OS.kDragActionMove) != 0) { + operation |= DND.DROP_MOVE; + } + if (osOperation == OS.kDragActionAll) { + operation = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK; + } + return operation; +} + +/** + * 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 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 + */ +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(int theDrag, DNDEvent event) { + if (theDrag == 0) return false; + + // get allowed operations + int style = getStyle(); + int[] outActions = new int[1]; + OS.GetDragAllowableActions(theDrag, outActions); + int operations = osOpToOp(outActions[0]) & style; + if (operations == DND.DROP_NONE) return false; + + //get current operation + int operation = getOperationFromKeyState(theDrag); + keyOperation = operation; + if (operation == DND.DROP_DEFAULT) { + if ((style & DND.DROP_DEFAULT) == 0) { + operation = (operations & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + } else { + if ((operation & operations) == 0) operation = DND.DROP_NONE; + } + + // get allowed transfer types + short[] numItems = new short[1]; + OS.CountDragItems(theDrag, numItems); + int[] flavors = new int[10]; + int index = -1; + //Get a unique list of flavors + for (short i = 0; i < numItems[0]; i++) { + int[] theItemRef = new int[1]; + OS.GetDragItemReferenceNumber(theDrag, (short) (i+1), theItemRef); + short[] numFlavors = new short[1]; + OS.CountDragItemFlavors(theDrag, theItemRef[0], numFlavors); + int[] theType = new int[1]; + for (int j = 0; j < numFlavors[0]; j++) { + theType[0] = 0; + if (OS.GetFlavorType(theDrag, theItemRef[0], (short) (j+1), theType) == OS.noErr) { + boolean unique = true; + for (int k = 0; k < flavors.length; k++) { + if (flavors[k] == theType[0]) { + unique = false; + break; + } + } + if (unique) { + if (index == flavors.length - 1) { + int[] temp = new int[flavors.length + 10]; + System.arraycopy(flavors, 0, temp, 0, flavors.length); + flavors = temp; + } + flavors[++index] = theType[0]; + } + } + } + } + if (index == -1) return false; + + TransferData[] dataTypes = new TransferData[index+1]; + index = -1; + for (int i = 0; i < dataTypes.length; i++) { + if (flavors[i] != 0) { + TransferData data = new TransferData(); + data.type = flavors[i]; + for (int j = 0; j < transferAgents.length; j++) { + Transfer transfer = transferAgents[j]; + if (transfer != null && transfer.isSupportedType(data)) { + dataTypes[++index] = data; + break; + } + } + } + } + if (index == -1) return false; + + if (index < dataTypes.length - 1) { + TransferData[] temp = new TransferData[index + 1]; + System.arraycopy(dataTypes, 0, temp, 0, index + 1); + dataTypes = temp; + } + + Point mouse = new Point(); + OS.GetDragMouse(theDrag, mouse, null); + + event.widget = this; + event.x = mouse.h; + event.y = mouse.v; + event.time = (int)System.currentTimeMillis(); + event.feedback = DND.FEEDBACK_SELECT; + event.dataTypes = dataTypes; + event.dataType = dataTypes[0]; + event.operations = operations; + event.detail = operation; + if (dropEffect != null) { + event.item = dropEffect.getItem(event.x, event.y); + } + + 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; +} + +void updateDragOverHover(long delay, DNDEvent event) { + if (delay == 0) { + dragOverStart = 0; + dragOverEvent = null; + return; + } + dragOverStart = System.currentTimeMillis() + delay; + if (dragOverEvent == null) dragOverEvent = new DNDEvent(); + dragOverEvent.x = event.x; + dragOverEvent.y = event.y; + dragOverEvent.dataTypes = event.dataTypes; + dragOverEvent.operations = event.operations; + dragOverEvent.dataType = event.dataType; + dragOverEvent.detail = event.detail; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/FileTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/FileTransfer.java new file mode 100644 index 0000000000..91ed4251cd --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/FileTransfer.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Outhink - support for typeFileURL + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import java.io.*; +import org.eclipse.swt.internal.carbon.*; + +/** + * 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. + * See <code>Transfer</code> for additional information. + * + * <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> + */ +public class FileTransfer extends ByteArrayTransfer { + + static FileTransfer _instance = new FileTransfer(); + static final String HFS = "hfs "; //$NON-NLS-1$ + static final String FURL = "furl"; //$NON-NLS-1$ + static final int HFSID = registerType(HFS); + static final int FURLID = registerType(FURL); + +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. For additional information see + * <code>Transfer#javaToNative</code>. + * + * @param object a java <code>String[]</code> containing the file names to be + * converted + * @param transferData an empty <code>TransferData</code> object; this + * object will be filled in on return with the platform specific format of the data + */ +public void javaToNative(Object object, TransferData transferData) { + if (!checkFile(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + String[] files = (String[])object; + transferData.result = -1; + byte[][] data = new byte[files.length][]; + for (int i = 0; i < data.length; i++) { + File file = new File(files[i]); + boolean isDirectory = file.isDirectory(); + String fileName = files[i]; + char [] chars = new char [fileName.length ()]; + fileName.getChars (0, chars.length, chars, 0); + int cfstring = OS.CFStringCreateWithCharacters (OS.kCFAllocatorDefault, chars, chars.length); + if (cfstring == 0) return; + try { + int url = OS.CFURLCreateWithFileSystemPath(OS.kCFAllocatorDefault, cfstring, OS.kCFURLPOSIXPathStyle, isDirectory); + if (url == 0) return; + try { + if (transferData.type == HFSID) { + byte[] fsRef = new byte[80]; + if (!OS.CFURLGetFSRef(url, fsRef)) return; + byte[] fsSpec = new byte[70]; + if (OS.FSGetCatalogInfo(fsRef, 0, null, null, fsSpec, null) != OS.noErr) return; + byte[] hfsflavor = new byte[10 + fsSpec.length]; + byte[] finfo = new byte[16]; + OS.FSpGetFInfo(fsSpec, finfo); + System.arraycopy(finfo, 0, hfsflavor, 0, 10); + System.arraycopy(fsSpec, 0, hfsflavor, 10, fsSpec.length); + data[i] = hfsflavor; + } + if (transferData.type == FURLID) { + int encoding = OS.CFStringGetSystemEncoding(); + int theData = OS.CFURLCreateData(OS.kCFAllocatorDefault, url, encoding, true); + if (theData == 0) return; + try { + int length = OS.CFDataGetLength(theData); + byte[] buffer = new byte[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFDataGetBytes(theData, range, buffer); + data[i] = buffer; + } finally { + OS.CFRelease(theData); + } + } + } finally { + OS.CFRelease(url); + } + } finally { + OS.CFRelease(cfstring); + } + } + transferData.data = data; + transferData.result = 0; +} +/** + * 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. + * For additional information see <code>Transfer#nativeToJava</code>. + * + * @param transferData the platform specific representation of the data to be + * been converted + * @return a java <code>String[]</code> containing a list of file names if the + * conversion was successful; otherwise null + */ +public Object nativeToJava(TransferData transferData) { + if (!isSupportedType(transferData) || transferData.data == null) return null; + if (transferData.data.length == 0) return null; + int count = transferData.data.length; + String[] fileNames = new String[count]; + for (int i=0; i<count; i++) { + byte[] data = transferData.data[i]; + int url = 0; + if (transferData.type == HFSID) { + byte[] fsspec = new byte[data.length - 10]; + System.arraycopy(data, 10, fsspec, 0, fsspec.length); + byte[] fsRef = new byte[80]; + if (OS.FSpMakeFSRef(fsspec, fsRef) != OS.noErr) return null; + url = OS.CFURLCreateFromFSRef(OS.kCFAllocatorDefault, fsRef); + if (url == 0) return null; + } + if (transferData.type == FURLID) { + int encoding = OS.kCFStringEncodingUTF8; + url = OS.CFURLCreateWithBytes(OS.kCFAllocatorDefault, data, data.length, encoding, 0); + if (url == 0) return null; + } + try { + int path = OS.CFURLCopyFileSystemPath(url, OS.kCFURLPOSIXPathStyle); + if (path == 0) return null; + try { + int length = OS.CFStringGetLength(path); + if (length == 0) return null; + char[] buffer= new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(path, range, buffer); + fileNames[i] = new String(buffer); + } finally { + OS.CFRelease(path); + } + } finally { + OS.CFRelease(url); + } + } + return fileNames; +} + +protected int[] getTypeIds(){ + return new int[] {FURLID, HFSID}; +} + +protected String[] getTypeNames(){ + return new String[] {FURL, HFS}; +} + +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/cocoa/org/eclipse/swt/dnd/HTMLTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/HTMLTransfer.java new file mode 100644 index 0000000000..64e81d2fbb --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/HTMLTransfer.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * 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.carbon.OS; + +/** + * 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. See + * <code>Transfer</code> for additional information. + * + * <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> + */ +public class HTMLTransfer extends ByteArrayTransfer { + + static HTMLTransfer _instance = new HTMLTransfer(); + static final String HTML = "HTML"; //$NON-NLS-1$ + static final int HTMLID = registerType(HTML); + +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. + * For additional information see <code>Transfer#javaToNative</code>. + * + * @param object a java <code>String</code> containing HTML text + * @param transferData an empty <code>TransferData</code> object; this + * object will be filled in on return with the platform specific format of the data + */ +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]; + string.getChars(0, count, chars, 0); + byte[] buffer = new byte[chars.length * 2]; + OS.memmove(buffer, chars, buffer.length); + transferData.data = new byte[1][]; + transferData.data[0] = buffer; + transferData.result = OS.noErr; +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform specific + * representation of HTML text to a java <code>String</code>. + * For additional information see <code>Transfer#nativeToJava</code>. + * + * @param transferData the platform specific representation of the data to be + * been converted + * @return a java <code>String</code> containing HTML text if the + * conversion was successful; otherwise null + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.data == null) return null; + if (transferData.data.length == 0 || transferData.data[0].length == 0) return null; + byte[] buffer = transferData.data[0]; + char[] chars = new char[(buffer.length + 1) / 2]; + OS.memmove(chars, buffer, buffer.length); + return new String(chars); +} + +protected int[] getTypeIds() { + return new int[] {HTMLID}; +} + +protected String[] getTypeNames() { + return new String[] {HTML}; +} + +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/cocoa/org/eclipse/swt/dnd/ImageTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/ImageTransfer.java new file mode 100644 index 0000000000..01add3417f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/ImageTransfer.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright (c) 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 + * Outhink - support for typeFileURL + *******************************************************************************/ +package org.eclipse.swt.dnd; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.carbon.*; +import org.eclipse.swt.widgets.*; + +/** + * 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. The + * <code>ImageData</code> contains infomration about the Image. See + * <code>Transfer</code> for additional information. + * + * <p> + * An example of a java <code>Image</code> containing an ImageData is shown + * below: + * </p> + * + * <code><pre> + * Image image = new Image(display, fileName); + * ImageData imgData = image.getImageData(); + * </code></pre> + */ +public class ImageTransfer extends ByteArrayTransfer { + +static ImageTransfer _instance = new ImageTransfer(); +static final String PICT = "PICT"; //$NON-NLS-1$ +static final String TIFF = "TIFF"; //$NON-NLS-1$ +static final int PICTID = registerType(PICT); +static final int TIFFID = registerType(TIFF); + +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 a java <code>ImageData</code> to a platform + * specific representation. For additional information see + * <code>Transfer#javaToNative</code>. + * + * @param object + * a java <code>ImageData</code> + * @param transferData + * an empty <code>TransferData</code> object; this object will + * be filled in on return with the platform specific format of + * the data + */ +public void javaToNative(Object object, TransferData transferData) { + if (!checkImage(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + transferData.result = -1; + + ImageData imgData = (ImageData) object; + Image image = new Image(Display.getCurrent(), imgData); + int handle = image.handle; + int width = OS.CGImageGetWidth(handle); + int height = OS.CGImageGetHeight(handle); + int alphaInfo = OS.CGImageGetAlphaInfo(handle); + int bpr = OS.CGImageGetBytesPerRow(handle); + + Rect rect = new Rect(); + rect.left = 0; + rect.top = 0; + rect.right = (short) width; + rect.bottom = (short) height; + + int[] gWorld = new int[1]; + int format = OS.k24RGBPixelFormat; + if (alphaInfo != OS.kCGImageAlphaNoneSkipFirst) { + format = OS.k32ARGBPixelFormat; + } + OS.NewGWorldFromPtr(gWorld, format, rect, 0, 0, 0, image.data, bpr); + int[] curPort = new int[1]; + int[] curGWorld = new int[1]; + OS.GetGWorld(curPort, curGWorld); + OS.SetGWorld(gWorld[0], curGWorld[0]); + int pictHandle = OS.OpenPicture(rect); + int portBitMap = OS.GetPortBitMapForCopyBits(gWorld[0]); + OS.CopyBits(portBitMap, portBitMap, rect, rect, (short) OS.srcCopy, 0); + OS.ClosePicture(); + OS.SetGWorld(curPort[0], curGWorld[0]); + OS.DisposeGWorld(gWorld[0]); + int length = OS.GetHandleSize(pictHandle); + OS.HLock(pictHandle); + int[] buffer = new int[1]; + OS.memmove(buffer, pictHandle, 4); + byte[] pictData = new byte[length]; + OS.memmove(pictData, buffer[0], length); + OS.HUnlock(pictHandle); + OS.KillPicture(pictHandle); + image.dispose(); + + transferData.data = new byte[][] { pictData }; + transferData.result = OS.noErr; +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform + * specific representation of an ImageData <code>ImageData</code>. For + * additional information see <code>Transfer#nativeToJava</code>. + * + * @param transferData + * the platform specific representation of the data to be been + * converted + * @return a java <code>ImageData</code> object if the conversion was + * successful; otherwise null + */ +public Object nativeToJava(TransferData transferData) { + if (!isSupportedType(transferData) || transferData.data == null) + return null; + if (transferData.data.length == 0) + return null; + byte[] dataArr = transferData.data[0]; + int size = dataArr.length; + int pictPtr = OS.NewPtr(size); + OS.memmove(pictPtr, dataArr, size); + int dataProvider = OS.CGDataProviderCreateWithData(0, pictPtr, size, 0); + if (dataProvider != 0) { + int pictDataRef = OS.QDPictCreateWithProvider(dataProvider); + // get bounds for the image + CGRect rect = new CGRect(); + OS.QDPictGetBounds(pictDataRef, rect); + int width = (int) rect.width; + int height = (int) rect.height; + + /* Create the image */ + int bpr = width * 4; + int dataSize = height * bpr; + int data = OS.NewPtr(dataSize); + if (data == 0) + SWT.error(SWT.ERROR_NO_HANDLES); + int provider = OS + .CGDataProviderCreateWithData(0, data, dataSize, 0); + if (provider == 0) { + OS.DisposePtr(data); + SWT.error(SWT.ERROR_NO_HANDLES); + } + int colorspace = OS.CGColorSpaceCreateDeviceRGB(); + if (colorspace == 0) + SWT.error(SWT.ERROR_NO_HANDLES); + int handle = OS.CGImageCreate(width, height, 8, 32, bpr, + colorspace, OS.kCGImageAlphaNoneSkipFirst, provider, null, + true, 0); + OS.CGDataProviderRelease(provider); + if (handle == 0) { + OS.DisposePtr(data); + SWT.error(SWT.ERROR_NO_HANDLES); + } + int bpc = OS.CGImageGetBitsPerComponent(handle); + int context = OS.CGBitmapContextCreate(data, width, height, bpc, + bpr, colorspace, OS.kCGImageAlphaNoneSkipFirst); + if (context == 0) { + OS.CGImageRelease(handle); + OS.DisposePtr(data); + SWT.error(SWT.ERROR_NO_HANDLES); + } + int status = OS.QDPictDrawToCGContext(context, rect, pictDataRef); + ImageData imgData = null; + if (status == 0) { + Image image = Image.carbon_new(Display.getCurrent(), + SWT.BITMAP, handle, data); + imgData = image.getImageData(); + image.dispose(); + } + OS.CGContextRelease(context); + OS.QDPictRelease(pictDataRef); + return imgData; + } + return null; +} + +protected int[] getTypeIds() { + return new int[] { PICTID }; +} + +protected String[] getTypeNames() { + return new String[] { PICT }; +} + +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/cocoa/org/eclipse/swt/dnd/RTFTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/RTFTransfer.java new file mode 100644 index 0000000000..fed105d0ce --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/RTFTransfer.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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.carbon.OS; +import org.eclipse.swt.internal.carbon.CFRange; + +/** + * 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. See + * <code>Transfer</code> for additional information. + * + * <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> + */ +public class RTFTransfer extends ByteArrayTransfer { + + static RTFTransfer _instance = new RTFTransfer(); + static final String RTF = "RTF "; //$NON-NLS-1$ + static final int RTFID = registerType(RTF); + +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. + * For additional information see <code>Transfer#javaToNative</code>. + * + * @param object a java <code>String</code> containing RTF text + * @param transferData an empty <code>TransferData</code> object; this + * object will be filled in on return with the platform specific format of the data + */ +public void javaToNative (Object object, TransferData transferData){ + if (!checkRTF(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + transferData.result = -1; + String string = (String)object; + int count = string.length(); + char[] chars = new char[count]; + string.getChars(0, count, chars, 0); + int cfstring = OS.CFStringCreateWithCharacters(OS.kCFAllocatorDefault, chars, count); + if (cfstring == 0) return; + try { + CFRange range = new CFRange(); + range.length = chars.length; + int encoding = OS.CFStringGetSystemEncoding(); + int[] size = new int[1]; + int numChars = OS.CFStringGetBytes(cfstring, range, encoding, (byte)'?', true, null, 0, size); + if (numChars == 0 || size[0] == 0) return; + byte[] buffer = new byte[size[0]]; + numChars = OS.CFStringGetBytes(cfstring, range, encoding, (byte)'?', true, buffer, size [0], size); + if (numChars == 0) return; + transferData.data = new byte[1][]; + transferData.data[0] = buffer; + transferData.result = 0; + } finally { + OS.CFRelease(cfstring); + } +} + +/** + * This implementation of <code>nativeToJava</code> converts a platform specific + * representation of RTF text to a java <code>String</code>. + * For additional information see <code>Transfer#nativeToJava</code>. + * + * @param transferData the platform specific representation of the data to be + * been converted + * @return a java <code>String</code> containing RTF text if the + * conversion was successful; otherwise null + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.data == null) return null; + if (transferData.data.length == 0 || transferData.data[0].length == 0) return null; + byte[] buffer = transferData.data[0]; + int encoding = OS.CFStringGetSystemEncoding(); + int cfstring = OS.CFStringCreateWithBytes(OS.kCFAllocatorDefault, buffer, buffer.length, encoding, true); + if (cfstring == 0) return null; + try { + int length = OS.CFStringGetLength(cfstring); + if (length == 0) return null; + char[] chars = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(cfstring, range, chars); + return new String(chars); + } finally { + OS.CFRelease(cfstring); + } +} + +protected int[] getTypeIds() { + return new int[] {RTFID}; +} + +protected String[] getTypeNames() { + return new String[] {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/cocoa/org/eclipse/swt/dnd/TableDragSourceEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TableDragSourceEffect.java new file mode 100644 index 0000000000..416864d6f5 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TableDragSourceEffect.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 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.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 + * + * @since 3.3 + */ +public class TableDragSourceEffect extends DragSourceEffect { + /** + * 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); + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TableDropTargetEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TableDropTargetEffect.java new file mode 100644 index 0000000000..1f8f804902 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TableDropTargetEffect.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * 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.SWT; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.Callback; +import org.eclipse.swt.internal.carbon.DataBrowserCallbacks; +import org.eclipse.swt.internal.carbon.OS; +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 + * + * @since 3.3 + */ +public class TableDropTargetEffect extends DropTargetEffect { + static final int SCROLL_HYSTERESIS = 150; // milli seconds + + TableItem scrollItem; + long scrollBeginTime; + DataBrowserCallbacks callbacks = null; + + static Callback AcceptDragProc; + static { + AcceptDragProc = new Callback(TableDropTargetEffect.class, "AcceptDragProc", 5); //$NON-NLS-1$ + int acceptDragProc = AcceptDragProc.getAddress(); + if (acceptDragProc == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + } + + static int AcceptDragProc(int theControl, int itemID, int property, int theRect, int theDrag) { + DropTarget target = FindDropTarget(theControl, theDrag); + if (target == null) return 0; + return (target.feedback & DND.FEEDBACK_SELECT) != 0 ? 1 : 0; + } + + static DropTarget FindDropTarget(int theControl, int theDrag) { + if (theControl == 0) return null; + Display display = Display.findDisplay(Thread.currentThread()); + if (display == null || display.isDisposed()) return null; + Widget widget = display.findWidget(theControl); + if (widget == null) return null; + return (DropTarget)widget.getData(DND.DROP_TARGET_KEY); + } + + /** + * 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) { + if (callbacks == null) { + Table table = (Table) control; + DataBrowserCallbacks callbacks = new DataBrowserCallbacks (); + OS.GetDataBrowserCallbacks (table.handle, callbacks); + callbacks.v1_acceptDragCallback = AcceptDragProc.getAddress(); + OS.SetDataBrowserCallbacks(table.handle, callbacks); + } + scrollBeginTime = 0; + scrollItem = 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) { + scrollBeginTime = 0; + scrollItem = null; + } + + /** + * 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) control; + int effect = checkEffect(event.feedback); + + TableItem item = (TableItem)getItem(table, event.x, event.y); + + if ((effect & DND.FEEDBACK_SCROLL) == 0) { + scrollBeginTime = 0; + scrollItem = null; + } else { + if (item != null && item.equals(scrollItem) && scrollBeginTime != 0) { + if (System.currentTimeMillis() >= scrollBeginTime) { + Rectangle area = table.getClientArea(); + int headerHeight = table.getHeaderHeight(); + int itemHeight= table.getItemHeight(); + Point pt = new Point(event.x, event.y); + pt = table.getDisplay().map(null, table, pt); + TableItem nextItem = null; + if (pt.y < area.y + headerHeight + 2 * itemHeight) { + int index = Math.max(0, table.indexOf(item)-1); + nextItem = table.getItem(index); + } + if (pt.y > area.y + area.height - 2 * itemHeight) { + int index = Math.min(table.getItemCount()-1, table.indexOf(item)+1); + nextItem = table.getItem(index); + } + if (nextItem != null) table.showItem(nextItem); + scrollBeginTime = 0; + scrollItem = null; + } + } else { + scrollBeginTime = System.currentTimeMillis() + SCROLL_HYSTERESIS; + scrollItem = item; + } + } + + // store current effect for selection feedback + ((DropTarget)event.widget).feedback = effect; + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TextTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TextTransfer.java new file mode 100644 index 0000000000..e2caaee2bc --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TextTransfer.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * 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.carbon.CFRange; +import org.eclipse.swt.internal.carbon.OS; + +/** + * 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 { + + static TextTransfer _instance = new TextTransfer(); + static final String TEXT = "TEXT"; //$NON-NLS-1$ + static final String UTEXT = "utxt"; //$NON-NLS-1$ + static final int TEXTID = OS.kScrapFlavorTypeText; + static final int UTEXTID = OS.kScrapFlavorTypeUnicode; + +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; this object + * will be filled in on return with the platform specific format of the data + * + * @see Transfer#javaToNative + */ +public void javaToNative (Object object, TransferData transferData) { + if (!checkText(object) || !isSupportedType(transferData)) { + DND.error(DND.ERROR_INVALID_DATA); + } + String string = (String)object; + char[] chars = new char[string.length()]; + string.getChars (0, chars.length, chars, 0); + transferData.result = -1; + switch (transferData.type) { + case TEXTID: { + int cfstring = OS.CFStringCreateWithCharacters(OS.kCFAllocatorDefault, chars, chars.length); + if (cfstring == 0) return; + byte[] buffer = null; + try { + CFRange range = new CFRange(); + range.length = chars.length; + int encoding = OS.CFStringGetSystemEncoding(); + int[] size = new int[1]; + int numChars = OS.CFStringGetBytes(cfstring, range, encoding, (byte)'?', true, null, 0, size); + if (numChars == 0) return; + buffer = new byte[size[0]]; + numChars = OS.CFStringGetBytes(cfstring, range, encoding, (byte)'?', true, buffer, size [0], size); + if (numChars == 0) return; + } finally { + OS.CFRelease(cfstring); + } + transferData.data = new byte[1][]; + transferData.data[0] = buffer; + transferData.result = OS.noErr; + break; + } + case UTEXTID: { + byte[] buffer = new byte[chars.length * 2]; + OS.memmove(buffer, chars, buffer.length); + transferData.data = new byte[1][]; + transferData.data[0] = buffer; + transferData.result = OS.noErr; + break; + } + } +} + +/** + * 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#nativeToJava + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.data == null) return null; + if (transferData.data.length == 0 || transferData.data[0].length == 0) return null; + byte[] buffer = transferData.data[0]; + switch (transferData.type) { + case TEXTID: { + int encoding = OS.CFStringGetSystemEncoding(); + int cfstring = OS.CFStringCreateWithBytes(OS.kCFAllocatorDefault, buffer, buffer.length, encoding, true); + if (cfstring == 0) return null; + try { + int length = OS.CFStringGetLength(cfstring); + if (length == 0) return null; + char[] chars = new char[length]; + CFRange range = new CFRange(); + range.length = length; + OS.CFStringGetCharacters(cfstring, range, chars); + return new String(chars); + } finally { + OS.CFRelease(cfstring); + } + } + case UTEXTID: { + char[] chars = new char[(buffer.length + 1) / 2]; + OS.memmove(chars, buffer, buffer.length); + return new String(chars); + } + } + return null; +} + +protected int[] getTypeIds() { + return new int[] {UTEXTID, TEXTID}; +} + +protected String[] getTypeNames() { + return new String[] {UTEXT, 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/cocoa/org/eclipse/swt/dnd/Transfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/Transfer.java new file mode 100644 index 0000000000..f5a8ee287e --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/Transfer.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * 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; + + +/** + * <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 + */ +public abstract class Transfer { + +/** + * 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) { + int length = formatName.length(); + // TODO - hashcode may not be unique - need another way + if (length > 4) return formatName.hashCode(); + int type = 0; + if (length > 0) type |= (formatName.charAt(0) & 0xff) << 24; + if (length > 1) type |= (formatName.charAt(1) & 0xff) << 16; + if (length > 2) type |= (formatName.charAt(2) & 0xff) << 8; + if (length > 3) type |= formatName.charAt(3) & 0xff; + return type; +} + +/** + * 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/cocoa/org/eclipse/swt/dnd/TransferData.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TransferData.java new file mode 100644 index 0000000000..cd7e4248e7 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TransferData.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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; + + +/** + * 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> + */ +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 data being transferred. + * The data field may contain multiple values. + * (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 byte[][] data; + + /** + * 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; + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TreeDragSourceEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TreeDragSourceEffect.java new file mode 100644 index 0000000000..97cd46f444 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TreeDragSourceEffect.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 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.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 + * + * @since 3.3 + */ +public class TreeDragSourceEffect extends DragSourceEffect { + /** + * 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); + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TreeDropTargetEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TreeDropTargetEffect.java new file mode 100644 index 0000000000..400df25b27 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/TreeDropTargetEffect.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * 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.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.Callback; +import org.eclipse.swt.internal.carbon.DataBrowserCallbacks; +import org.eclipse.swt.internal.carbon.OS; +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 + * + * @since 3.3 + */ +public class TreeDropTargetEffect extends DropTargetEffect { + static final int SCROLL_HYSTERESIS = 150; // milli seconds + static final int EXPAND_HYSTERESIS = 1000; // milli seconds + + int currentEffect = DND.FEEDBACK_NONE; + TreeItem currentItem; + + TreeItem insertItem = null; + boolean insertBefore = false; + + TreeItem scrollItem; + long scrollBeginTime; + + TreeItem expandItem; + long expandBeginTime; + + DataBrowserCallbacks callbacks = null; + + int acceptDragProc(int theControl, int itemID, int property, int theRect, int theDrag) { + return (currentEffect & DND.FEEDBACK_SELECT) != 0 ? 1 : 0; + } + + static Callback AcceptDragProc; + static { + AcceptDragProc = new Callback(TreeDropTargetEffect.class, "AcceptDragProc", 5); //$NON-NLS-1$ + int acceptDragProc = AcceptDragProc.getAddress(); + if (acceptDragProc == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + } + + static int AcceptDragProc(int theControl, int itemID, int property, int theRect, int theDrag) { + DropTarget target = FindDropTarget(theControl, theDrag); + if (target == null) return 0; + return (target.feedback & DND.FEEDBACK_SELECT) != 0 ? 1 : 0; + } + + static DropTarget FindDropTarget(int theControl, int theDrag) { + if (theControl == 0) return null; + Display display = Display.findDisplay(Thread.currentThread()); + if (display == null || display.isDisposed()) return null; + Widget widget = display.findWidget(theControl); + if (widget == null) return null; + return (DropTarget)widget.getData(DND.DROP_TARGET_KEY); + } + + /** + * 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) { + if (callbacks == null) { + Tree table = (Tree) control; + DataBrowserCallbacks callbacks = new DataBrowserCallbacks (); + OS.GetDataBrowserCallbacks (table.handle, callbacks); + callbacks.v1_acceptDragCallback = AcceptDragProc.getAddress(); + OS.SetDataBrowserCallbacks(table.handle, callbacks); + } + insertItem = null; + expandBeginTime = 0; + expandItem = null; + scrollBeginTime = 0; + scrollItem = 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) { + Tree tree = (Tree) control; + if (insertItem != null) { + setInsertMark(tree, null, false); + insertItem = null; + } + expandBeginTime = 0; + expandItem = null; + scrollBeginTime = 0; + scrollItem = null; + } + + /** + * 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) control; + TreeItem item = (TreeItem)getItem(tree, event.x, event.y); + int effect = checkEffect(event.feedback); + if ((effect & DND.FEEDBACK_EXPAND) == 0) { + expandBeginTime = 0; + expandItem = null; + } else { + if (item != null && item.equals(expandItem) && expandBeginTime != 0) { + if (System.currentTimeMillis() >= expandBeginTime) { + if (item.getItemCount() > 0 && !item.getExpanded()) { + Event e = new Event(); + e.x = event.x; + e.y = event.y; + e.item = item; + e.time = (int) System.currentTimeMillis(); + tree.notifyListeners(SWT.Expand, e); + if (item.isDisposed()) return; + item.setExpanded(true); + } + expandBeginTime = 0; + expandItem = null; + } + } else { + expandBeginTime = System.currentTimeMillis() + EXPAND_HYSTERESIS; + expandItem = item; + } + } + + if ((effect & DND.FEEDBACK_SCROLL) == 0) { + scrollBeginTime = 0; + scrollItem = null; + } else { + if (item != null && item.equals(scrollItem) && scrollBeginTime != 0) { + if (System.currentTimeMillis() >= scrollBeginTime) { + Rectangle area = tree.getClientArea(); + int headerHeight = tree.getHeaderHeight(); + int itemHeight= tree.getItemHeight(); + Point pt = new Point(event.x, event.y); + pt = tree.getDisplay().map(null, tree, pt); + TreeItem nextItem = null; + if (pt.y < area.y + headerHeight + 2 * itemHeight) { + nextItem = previousItem(tree, item); + } + if (pt.y > area.y + area.height - 2 * itemHeight) { + nextItem = nextItem(tree, item); + } + if (nextItem != null) tree.showItem(nextItem); + scrollBeginTime = 0; + scrollItem = null; + } + } else { + scrollBeginTime = System.currentTimeMillis() + SCROLL_HYSTERESIS; + scrollItem = item; + } + } + + if ((effect & DND.FEEDBACK_INSERT_AFTER) != 0 || + (effect & DND.FEEDBACK_INSERT_BEFORE) != 0) { + if (currentItem != item || + ((effect & DND.FEEDBACK_INSERT_AFTER) != (currentEffect & DND.FEEDBACK_INSERT_AFTER)) || + ((effect & DND.FEEDBACK_INSERT_BEFORE) != (currentEffect & DND.FEEDBACK_INSERT_BEFORE))) { + setInsertMark(tree, item, (effect & DND.FEEDBACK_INSERT_BEFORE) != 0); + currentEffect = effect; + currentItem = item; + } + } else { + setInsertMark(tree, null, false); + } + // save current effect for selection feedback + ((DropTarget)event.widget).feedback = effect; + } + + void setInsertMark(Tree tree, TreeItem item, boolean before) { + if (item == insertItem && before == insertBefore) return; + insertItem = item; + insertBefore = before; + tree.setInsertMark(item, before); + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/URLTransfer.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/URLTransfer.java new file mode 100644 index 0000000000..0117363de9 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/URLTransfer.java @@ -0,0 +1 @@ +/*******************************************************************************
* Copyright (c) 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.carbon.*;
/**
* 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. See
* <code>Transfer</code> for additional information. The string
* must contain the 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>
*/
public class URLTransfer extends ByteArrayTransfer {
static URLTransfer _instance = new URLTransfer();
static final String URL = "url "; //$NON-NLS-1$
static final int URL_ID = registerType(URL);
static final String URLN = "urln"; //$NON-NLS-1$
static final int URLN_ID = registerType(URLN);
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.
* For additional information see <code>Transfer#javaToNative</code>.
*
* @param object a java <code>String[]</code> containing a URL
* @param transferData an empty <code>TransferData</code> object; this
* object will be filled in on return with the platform specific format of the data
*/
public void javaToNative (Object object, TransferData transferData){
if (!checkURL(object) || !isSupportedType(transferData)) {
DND.error(DND.ERROR_INVALID_DATA);
}
transferData.result = -1;
String url = (String)object;
int count = url.length();
char[] chars = new char[count];
url.getChars(0, count, chars, 0);
int cfstring = OS.CFStringCreateWithCharacters(OS.kCFAllocatorDefault, chars, count);
if (cfstring == 0) return;
try {
CFRange range = new CFRange();
range.length = chars.length;
int encoding = OS.CFStringGetSystemEncoding();
int[] size = new int[1];
int numChars = OS.CFStringGetBytes(cfstring, range, encoding, (byte)'?', true, null, 0, size);
if (numChars == 0 || size[0] == 0) return;
byte[] buffer = new byte[size[0]];
numChars = OS.CFStringGetBytes(cfstring, range, encoding, (byte)'?', true, buffer, size [0], size);
if (numChars == 0) return;
transferData.data = new byte[][] {buffer};
transferData.result = 0;
} finally {
OS.CFRelease(cfstring);
}
}
/**
* This implementation of <code>nativeToJava</code> converts a platform specific
* representation of a URL to a java <code>String</code>.
* For additional information see <code>Transfer#nativeToJava</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
*/
public Object nativeToJava(TransferData transferData){
if (!isSupportedType(transferData) || transferData.data == null) return null;
if (transferData.data.length == 0) return null;
byte[] buffer = transferData.data[0];
int encoding = OS.CFStringGetSystemEncoding();
int cfstring = OS.CFStringCreateWithBytes(OS.kCFAllocatorDefault, buffer, buffer.length, encoding, true);
if (cfstring == 0) return null;
try {
int length = OS.CFStringGetLength(cfstring);
if (length == 0) return null;
char[] chars = new char[length];
CFRange range = new CFRange();
range.length = length;
OS.CFStringGetCharacters(cfstring, range, chars);
return new String(chars);
} finally {
OS.CFRelease(cfstring);
}
}
protected int[] getTypeIds(){
return new int[] {URL_ID, URLN_ID};
}
protected String[] getTypeNames(){
return new String[] {URL, URLN};
}
boolean checkURL(Object object) {
return object != null && (object instanceof String) && ((String)object).length() > 0;
}
protected boolean validate(Object object) {
return checkURL(object);
}
}
\ No newline at end of file |