# HG changeset patch # User Frank Benoit # Date 1202874682 -3600 # Node ID 242e33c0e383aa491339171856911962e93b71bd # Parent fa7d7d66b9ed39163a7eb163967c96af0df0ed5c Added dnd source, ByteArrayTransfer,Clipboard completed diff -r fa7d7d66b9ed -r 242e33c0e383 dsss.conf --- a/dsss.conf Wed Feb 13 03:22:44 2008 +0100 +++ b/dsss.conf Wed Feb 13 04:51:22 2008 +0100 @@ -3,6 +3,7 @@ [dwt] type=library debugflags+=-g -gc -debug +exclude=dwt/dnd [simple.d] diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/ByteArrayTransfer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/ByteArrayTransfer.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,223 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.ByteArrayTransfer; + +import dwt.internal.ole.win32.COM; +import dwt.internal.ole.win32.OBJIDL; +import dwt.internal.win32.OS; + +import dwt.dnd.DND; +import dwt.dnd.Transfer; +import dwt.dnd.TransferData; + +import dwt.dwthelper.utils; + +/** + * The class ByteArrayTransfer provides a platform specific + * mechanism for converting a java byte[] to a platform + * specific representation of the byte array and vice versa. See + * Transfer for additional information. + * + *

ByteArrayTransfer is never used directly but is sub-classed + * by transfer agents that convert between data in a java format such as a + * String and a platform specific byte array. + * + *

If the data you are converting does not map to a + * byte[], you should sub-class Transfer directly + * and do your own mapping to a platform data type.

+ * + *

The following snippet shows a subclass of ByteArrayTransfer that transfers + * data defined by the class MyType.

+ * + *

+ * public class MyType {
+ *  public String fileName;
+ *  public long fileLength;
+ *  public long lastModified;
+ * }
+ * 
+ * + *

+ * 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 is 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 is 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};
+ * }
+ * }
+ * 
+ */ +public abstract class ByteArrayTransfer : Transfer { + +public TransferData[] getSupportedTypes() { + int[] types = getTypeIds(); + TransferData[] data = new TransferData[types.length]; + for (int i = 0; i < types.length; i++) { + data[i] = new TransferData(); + data[i].type = types[i]; + data[i].formatetc = new FORMATETC(); + data[i].formatetc.cfFormat = types[i]; + data[i].formatetc.dwAspect = COM.DVASPECT_CONTENT; + data[i].formatetc.lindex = -1; + data[i].formatetc.tymed = COM.TYMED_HGLOBAL; + } + return data; +} + +public bool isSupportedType(TransferData transferData){ + if (transferData is null) return false; + int[] types = getTypeIds(); + for (int i = 0; i < types.length; i++) { + FORMATETC* format = transferData.formatetc; + if (format.cfFormat is types[i] && + (format.dwAspect & COM.DVASPECT_CONTENT) is COM.DVASPECT_CONTENT && + (format.tymed & COM.TYMED_HGLOBAL) is COM.TYMED_HGLOBAL ) + return true; + } + return false; +} + +/** + * This implementation of javaToNative converts a java + * byte[] to a platform specific representation. For additional + * information see Transfer#javaToNative. + * + * @see Transfer#javaToNative + * + * @param object a java byte[] containing the data to be converted + * @param transferData an empty TransferData 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); + } + // Allocate the memory because the caller (DropTarget) has not handed it in + // The caller of this method must release the data when it is done with it. + byte[] data = (cast(ArrayWrapperByte)object).array; + int size = data.length; + auto newPtr = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, size); + OS.MoveMemory(newPtr, data.ptr, size); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = newPtr; + transferData.stgmedium.pUnkForRelease = null; + transferData.result = COM.S_OK; +} + +/** + * This implementation of nativeToJava converts a platform specific + * representation of a byte array to a java byte[]. + * For additional information see Transfer#nativeToJava. + * + * @see Transfer#nativeToJava + * + * @param transferData the platform specific representation of the data to be + * been converted + * @return a java byte[] containing the converted data if the + * conversion was successful; otherwise null + */ +protected Object nativeToJava(TransferData transferData) { + if (!isSupportedType(transferData) || transferData.pIDataObject is null) return null; + + IDataObject data = transferData.pIDataObject; + data.AddRef(); + FORMATETC* formatetc = transferData.formatetc; + STGMEDIUM* stgmedium = new STGMEDIUM(); + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = data.GetData(formatetc, stgmedium); + data.Release(); + if (transferData.result !is COM.S_OK) return null; + auto hMem = stgmedium.unionField; + int size = OS.GlobalSize(hMem); + byte[] buffer = new byte[size]; + auto ptr = OS.GlobalLock(hMem); + OS.MoveMemory(buffer.ptr, ptr, size); + OS.GlobalUnlock(hMem); + OS.GlobalFree(hMem); + return new ArrayWrapperByte(buffer); +} + +bool checkByteArray(Object object) { + return (object !is null && ( null !is cast(ArrayWrapperByte)object) && (cast(ArrayWrapperByte)object).array.length > 0); +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/Clipboard.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/Clipboard.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,814 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.Clipboard; + + +import dwt.DWT; +import dwt.DWTError; +import dwt.DWTException; +import dwt.internal.ole.win32.COM; +import dwt.internal.ole.win32.OBJIDL; +import dwt.internal.ole.win32.extras; +import dwt.internal.win32.OS; +import dwt.widgets.Display; + +import dwt.dnd.Transfer; +import dwt.dnd.TransferData; +import dwt.dnd.OleEnumFORMATETC; +import dwt.dnd.DND; + +import dwt.dwthelper.utils; +import tango.core.Thread; + +/** + * The Clipboard provides a mechanism for transferring data from one + * application to another or within an application. + * + *

IMPORTANT: This class is not intended to be subclassed.

+ */ +public class Clipboard { + + private Display display; + + // ole interfaces + private _IDataObjectImpl iDataObject; + private int refCount; + private Transfer[] transferAgents; + private Object[] data; + private int CFSTR_PREFERREDDROPEFFECT; + +/** + * Constructs a new instance of this class. Creating an instance of a Clipboard + * may cause system resources to be allocated depending on the platform. It is therefore + * mandatory that the Clipboard instance be disposed when no longer required. + * + * @param display the display on which to allocate the clipboard + * + * @exception DWTException
    + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent
  • + *
  • ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass
  • + *
+ * + * @see Clipboard#dispose + * @see Clipboard#checkSubclass + */ +public this(Display display) { + checkSubclass (); + if (display is null) { + display = Display.getCurrent(); + if (display is null) { + display = Display.getDefault(); + } + } + if (display.getThread() !is Thread.getThis()) { + DND.error(DWT.ERROR_THREAD_INVALID_ACCESS); + } + this.display = display; + TCHAR* chFormatName = StrToTCHARz(0, "Preferred DropEffect"); //$NON-NLS-1$ + CFSTR_PREFERREDDROPEFFECT = OS.RegisterClipboardFormat(chFormatName); + createCOMInterfaces(); + this.AddRef(); +} + +/** + * Checks that this class can be subclassed. + *

+ * The DWT class library is intended to be subclassed + * only at specific, controlled points. This method enforces this + * rule unless it is overridden. + *

+ * IMPORTANT: 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 DWT releases and will be + * strongly platform specific. No support is provided for + * user-written classes which are implemented in this fashion. + *

+ * The ability to subclass outside of the allowed DWT classes + * is intended purely to enable those not on the DWT 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. + *

+ * + * @exception DWTException
    + *
  • ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass
  • + *
+ */ +protected void checkSubclass () { + char[] name = this.classinfo.name; + char[] validName = Clipboard.classinfo.name; + if (validName!=/*eq*/name) { + DND.error (DWT.ERROR_INVALID_SUBCLASS); + } +} + +/** + * Throws an DWTException 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 should be called by + * widget implementors to enforce the standard DWT invariants. + *

+ * Currently, it is an error to invoke any method (other than + * isDisposed()) on a widget that has had its + * dispose() method called. It is also an error + * to call widget methods from any thread that is different + * from the thread that created the widget. + *

+ * In future releases of DWT, there may be more or fewer error + * checks and exceptions may be thrown for different reasons. + *

+ * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +protected void checkWidget () { + Display display = this.display; + if (display is null) DND.error (DWT.ERROR_WIDGET_DISPOSED); + if (display.getThread() !is Thread.getThis ()) DND.error (DWT.ERROR_THREAD_INVALID_ACCESS); + if (display.isDisposed()) DND.error(DWT.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 DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @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. + * + *

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.

+ * + *

The clipboards value is either one of the clipboard constants defined in + * class DND, or must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DND clipboard constants.

+ * + * @param clipboards to be cleared + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @see DND#CLIPBOARD + * @see DND#SELECTION_CLIPBOARD + * + * @since 3.1 + */ +public void clearContents(int clipboards) { + checkWidget(); + if ((clipboards & DND.CLIPBOARD) !is 0) { + /* OleIsCurrentClipboard([in] pDataObject) + * The argument pDataObject is owned by the caller so reference count does not + * need to be incremented. + */ + if (COM.OleIsCurrentClipboard(this.iDataObject) is COM.S_OK) { + /* OleSetClipboard([in] pDataObject) + * The argument pDataObject is owned by the caller so reference count does not + * need to be incremented. + */ + COM.OleSetClipboard(null); + } + } +} + +/** + * 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. + * + *

NOTE: On some platforms the data will not be available once the application + * has exited or the display has been disposed.

+ * + * @exception DWTException
    + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent
  • + *
+ */ +public void dispose () { + if (isDisposed()) return; + if (display.getThread() !is Thread.getThis ()) DND.error(DWT.ERROR_THREAD_INVALID_ACCESS); + /* OleIsCurrentClipboard([in] pDataObject) + * The argument pDataObject is owned by the caller so reference count does not + * need to be incremented. + */ + if (COM.OleIsCurrentClipboard(this.iDataObject) is COM.S_OK) { + COM.OleFlushClipboard(); + } + this.Release(); + display = null; +} + +/** + * Retrieve the data of the specified type currently available on the system + * clipboard. Refer to the specific subclass of Transfer to + * determine the type of object returned. + * + *

The following snippet shows text and RTF text being retrieved from the + * clipboard:

+ * + *
+ *    Clipboard clipboard = new Clipboard(display);
+ *    TextTransfer textTransfer = TextTransfer.getInstance();
+ *    String textData = (String)clipboard.getContents(textTransfer);
+ *    if (textData !is null) System.out.println("Text is "+textData);
+ *    RTFTransfer rtfTransfer = RTFTransfer.getInstance();
+ *    String rtfData = (String)clipboard.getContents(rtfTransfer);
+ *    if (rtfData !is null) System.out.println("RTF Text is "+rtfData);
+ *    clipboard.dispose();
+ *    
+ * + * @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 DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if transfer is null
  • + *
+ * + * @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 Transfer to + * determine the type of object returned. + * + *

The following snippet shows text and RTF text being retrieved from the + * clipboard:

+ * + *
+ *    Clipboard clipboard = new Clipboard(display);
+ *    TextTransfer textTransfer = TextTransfer.getInstance();
+ *    String textData = (String)clipboard.getContents(textTransfer);
+ *    if (textData !is null) System.out.println("Text is "+textData);
+ *    RTFTransfer rtfTransfer = RTFTransfer.getInstance();
+ *    String rtfData = (String)clipboard.getContents(rtfTransfer, DND.CLIPBOARD);
+ *    if (rtfData !is null) System.out.println("RTF Text is "+rtfData);
+ *    clipboard.dispose();
+ *    
+ * + *

The clipboards value is either one of the clipboard constants defined in + * class DND, or must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DND clipboard constants.

+ * + * @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 DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if transfer is null
  • + *
+ * + * @see Transfer + * @see DND#CLIPBOARD + * @see DND#SELECTION_CLIPBOARD + * + * @since 3.1 + */ +public Object getContents(Transfer transfer, int clipboards) { + checkWidget(); + if (transfer is null) DND.error(DWT.ERROR_NULL_ARGUMENT); + if ((clipboards & DND.CLIPBOARD) is 0) return null; + /* + * Bug in Windows. When a new application takes control + * of the clipboard, other applications may open the + * clipboard to determine if they want to record the + * clipboard updates. When this happens, the clipboard + * can not be accessed until the other application is + * finished. To allow the other applications to release + * the clipboard, use PeekMessage() to enable cross thread + * message sends. + */ + IDataObject dataObject; + int retryCount = 0; + /* OleGetClipboard([out] ppDataObject). + * AddRef has already been called on ppDataObject by the callee and must be released by the caller. + */ + int result = COM.OleGetClipboard(&dataObject); + while (result !is COM.S_OK && retryCount++ < 10) { + try {Thread.sleep(0.050);} catch (Exception t) {} + MSG msg; + OS.PeekMessage(&msg, null, 0, 0, OS.PM_NOREMOVE | OS.PM_NOYIELD); + result = COM.OleGetClipboard(&dataObject); + } + if (result !is COM.S_OK) return null; + try { + TransferData[] allowed = transfer.getSupportedTypes(); + for (int i = 0; i < allowed.length; i++) { + if (dataObject.QueryGetData(allowed[i].formatetc) is COM.S_OK) { + TransferData data = allowed[i]; + data.pIDataObject = dataObject; + return transfer.nativeToJava(data); + } + } + } finally { + dataObject.Release(); + } + return null; // No data available for this transfer +} +/** + * Returns true if the clipboard has been disposed, + * and false otherwise. + *

+ * 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. + *

+ * + * @return true when the widget is disposed and false otherwise + * + * @since 3.0 + */ +public bool isDisposed () { + return (display is 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. + * + *

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.

+ * + *

The following snippet shows text and RTF text being set on the copy/paste + * clipboard: + *

+ * + *
+ *  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();
+ * 
+ * + * @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
    + *
  • 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
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * @exception DWTError
    + *
  • ERROR_CANNOT_SET_CLIPBOARD - if the clipboard is locked or otherwise unavailable
  • + *
+ * + *

NOTE: ERROR_CANNOT_SET_CLIPBOARD should be an DWTException, since it is a + * recoverable error, but can not be changed due to backward compatibility.

+ */ +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. + * + *

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.

+ * + *

The clipboards value is either one of the clipboard constants defined in + * class DND, or must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DND clipboard constants.

+ * + *

The following snippet shows text and RTF text being set on the copy/paste + * clipboard: + *

+ * + *
+ *  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();
+ * 
+ * + * @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
    + *
  • 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
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * @exception DWTError
    + *
  • ERROR_CANNOT_SET_CLIPBOARD - if the clipboard is locked or otherwise unavailable
  • + *
+ * + *

NOTE: ERROR_CANNOT_SET_CLIPBOARD should be an DWTException, since it is a + * recoverable error, but can not be changed due to backward compatibility.

+ * + * @see DND#CLIPBOARD + * @see DND#SELECTION_CLIPBOARD + * + * @since 3.1 + */ +public void setContents(Object[] data, Transfer[] dataTypes, int clipboards) { + checkWidget(); + if (data is null || dataTypes is null || data.length !is dataTypes.length || data.length is 0) { + DND.error(DWT.ERROR_INVALID_ARGUMENT); + } + for (int i = 0; i < data.length; i++) { + if (data[i] is null || dataTypes[i] is null || !dataTypes[i].validate(data[i])) { + DND.error(DWT.ERROR_INVALID_ARGUMENT); + } + } + if ((clipboards & DND.CLIPBOARD) is 0) return; + this.data = data; + this.transferAgents = dataTypes; + /* OleSetClipboard([in] pDataObject) + * The argument pDataObject is owned by the caller so the reference count does not + * need to be incremented. + */ + int result = COM.OleSetClipboard(iDataObject); + + /* + * Bug in Windows. When a new application takes control + * of the clipboard, other applications may open the + * clipboard to determine if they want to record the + * clipboard updates. When this happens, the clipboard + * can not be flushed until the other application is + * finished. To allow other applications to get the + * data, use PeekMessage() to enable cross thread + * message sends. + */ + int retryCount = 0; + while (result !is COM.S_OK && retryCount++ < 10) { + try {Thread.sleep(0.050);} catch (Exception t) {} + MSG msg; + OS.PeekMessage(&msg, null, 0, 0, OS.PM_NOREMOVE | OS.PM_NOYIELD); + result = COM.OleSetClipboard(iDataObject); + } + if (result !is COM.S_OK) { + DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD); + } +} +private int AddRef() { + refCount++; + return refCount; +} +private void createCOMInterfaces() { + // register each of the interfaces that this object implements + iDataObject = new _IDataObjectImpl( this ); +} +private void disposeCOMInterfaces() { + iDataObject = null; +} +/* + * EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc) + * Ownership of ppenumFormatetc transfers from callee to caller so reference count on ppenumFormatetc + * must be incremented before returning. Caller is responsible for releasing ppenumFormatetc. + */ +LRESULT EnumFormatEtc(int dwDirection, IEnumFORMATETC* ppenumFormatetc) { + // only allow getting of data - SetData is not currently supported + if (dwDirection is COM.DATADIR_SET) return COM.E_NOTIMPL; + // what types have been registered? + TransferData[] allowedDataTypes = new TransferData[0]; + for (int i = 0; i < transferAgents.length; i++){ + TransferData[] formats = transferAgents[i].getSupportedTypes(); + TransferData[] newAllowedDataTypes = new TransferData[allowedDataTypes.length + formats.length]; + System.arraycopy(allowedDataTypes, 0, newAllowedDataTypes, 0, allowedDataTypes.length); + System.arraycopy(formats, 0, newAllowedDataTypes, allowedDataTypes.length, formats.length); + allowedDataTypes = newAllowedDataTypes; + } + OleEnumFORMATETC enumFORMATETC = new OleEnumFORMATETC(); + enumFORMATETC.AddRef(); + FORMATETC*[] formats = new FORMATETC*[allowedDataTypes.length + 1]; + for (int i = 0; i < allowedDataTypes.length; i++){ + formats[i] = allowedDataTypes[i].formatetc; + } + // include the drop effect format to specify a copy operation + FORMATETC* dropeffect = new FORMATETC(); + dropeffect.cfFormat = CFSTR_PREFERREDDROPEFFECT; + dropeffect.dwAspect = COM.DVASPECT_CONTENT; + dropeffect.lindex = -1; + dropeffect.tymed = COM.TYMED_HGLOBAL; + formats[formats.length -1] = dropeffect; + enumFORMATETC.setFormats(formats); + + // TODO: do we need AddRef() here + *ppenumFormatetc = enumFORMATETC.getAddress(); + return COM.S_OK; +} + +private IDataObject getAddress(){ + return iDataObject; +} + +LRESULT GetData(FORMATETC *pFormatetc, STGMEDIUM *pmedium) { + /* Called by a data consumer to obtain data from a source data object. + The GetData method renders the data described in the specified FORMATETC + structure and transfers it through the specified STGMEDIUM structure. + The caller then assumes responsibility for releasing the STGMEDIUM structure. + */ + if (pFormatetc is null || pmedium is null) return COM.E_INVALIDARG; + if (QueryGetData(pFormatetc) !is COM.S_OK) return COM.DV_E_FORMATETC; + + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.E_FAIL; + + if (transferData.type is CFSTR_PREFERREDDROPEFFECT) { + // specify that a copy operation is to be performed + STGMEDIUM* stgmedium = new STGMEDIUM(); + stgmedium.tymed = COM.TYMED_HGLOBAL; + stgmedium.unionField = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, 4); + stgmedium.unionField = cast(void*)COM.DROPEFFECT_COPY; + stgmedium.pUnkForRelease = null; + COM.MoveMemory(pmedium, stgmedium, STGMEDIUM.sizeof); + return COM.S_OK; + } + + // get matching transfer agent to perform conversion + int transferIndex = -1; + for (int i = 0; i < transferAgents.length; i++){ + if (transferAgents[i].isSupportedType(transferData)){ + transferIndex = i; + break; + } + } + if (transferIndex is -1) return COM.DV_E_FORMATETC; + transferAgents[transferIndex].javaToNative(data[transferIndex], transferData); + COM.MoveMemory(pmedium, transferData.stgmedium, STGMEDIUM.sizeof); + return transferData.result; +} + +LRESULT QueryGetData(FORMATETC * pFormatetc) { + if (transferAgents is null) return COM.E_FAIL; + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + if (transferData.type is CFSTR_PREFERREDDROPEFFECT) return COM.S_OK; + // is this type supported by the transfer agent? + for (int i = 0; i < transferAgents.length; i++){ + if (transferAgents[i].isSupportedType(transferData)) + return COM.S_OK; + } + + return COM.DV_E_FORMATETC; +} +/* QueryInterface([in] iid, [out] ppvObject) + * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject + * must be incremented before returning. Caller is responsible for releasing ppvObject. + */ +HRESULT QueryInterface(GUID* riid, void ** ppvObject) { + if (riid is null || ppvObject is null) return COM.E_INVALIDARG; + if (COM.IsEqualGUID(riid, &COM.IIDIUnknown) || COM.IsEqualGUID(riid, &COM.IIDIDataObject) ) { + *ppvObject = cast(void*)cast(IUnknown)iDataObject; + AddRef(); + return COM.S_OK; + } + *ppvObject = null; + return COM.E_NOINTERFACE; +} +private ULONG Release() { + refCount--; + if (refCount is 0) { + this.data = null; + this.transferAgents = null; + disposeCOMInterfaces(); + COM.CoFreeUnusedLibraries(); + } + return refCount; +} + +/** + * Returns an array of the data types currently available on the system + * clipboard. Use with Transfer.isSupportedType. + * + * @return array of data types currently available on the system clipboard + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @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. + * + *

The clipboards value is either one of the clipboard constants defined in + * class DND, or must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DND clipboard constants.

+ * + * @param clipboards from which to get the data types + * @return array of data types currently available on the specified clipboard + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @see Transfer#isSupportedType + * @see DND#CLIPBOARD + * @see DND#SELECTION_CLIPBOARD + * + * @since 3.1 + */ +public TransferData[] getAvailableTypes(int clipboards) { + checkWidget(); + if ((clipboards & DND.CLIPBOARD) is 0) return null; + FORMATETC*[] types = _getAvailableTypes(); + TransferData[] data = new TransferData[types.length]; + for (int i = 0; i < types.length; i++) { + data[i] = new TransferData(); + data[i].type = types[i].cfFormat; + data[i].formatetc = types[i]; + } + return data; +} + +/** + * Returns a platform specific list of the data types currently available on the + * system clipboard. + * + *

Note: getAvailableTypeNames is a utility for writing a Transfer + * sub-class. It should NOT be used within an application because it provides + * platform specific information.

+ * + * @return a platform specific list of the data types currently available on the + * system clipboard + * + * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ */ +public char[][] getAvailableTypeNames() { + checkWidget(); + FORMATETC*[] types = _getAvailableTypes(); + char[][] names = new char[][](types.length); + int maxSize = 128; + for (int i = 0; i < types.length; i++){ + TCHAR[] buffer = NewTCHARs(0, maxSize); + int size = OS.GetClipboardFormatName(types[i].cfFormat, buffer.ptr, maxSize); + if (size !is 0) { + names[i] = TCHARzToStr(buffer.ptr)[0..size]; + } else { + switch (types[i].cfFormat) { + case COM.CF_HDROP: names[i] = "CF_HDROP"; break; //$NON-NLS-1$ + case COM.CF_TEXT: names[i] = "CF_TEXT"; break; //$NON-NLS-1$ + case COM.CF_BITMAP: names[i] = "CF_BITMAP"; break; //$NON-NLS-1$ + case COM.CF_METAFILEPICT: names[i] = "CF_METAFILEPICT"; break; //$NON-NLS-1$ + case COM.CF_SYLK: names[i] = "CF_SYLK"; break; //$NON-NLS-1$ + case COM.CF_DIF: names[i] = "CF_DIF"; break; //$NON-NLS-1$ + case COM.CF_TIFF: names[i] = "CF_TIFF"; break; //$NON-NLS-1$ + case COM.CF_OEMTEXT: names[i] = "CF_OEMTEXT"; break; //$NON-NLS-1$ + case COM.CF_DIB: names[i] = "CF_DIB"; break; //$NON-NLS-1$ + case COM.CF_PALETTE: names[i] = "CF_PALETTE"; break; //$NON-NLS-1$ + case COM.CF_PENDATA: names[i] = "CF_PENDATA"; break; //$NON-NLS-1$ + case COM.CF_RIFF: names[i] = "CF_RIFF"; break; //$NON-NLS-1$ + case COM.CF_WAVE: names[i] = "CF_WAVE"; break; //$NON-NLS-1$ + case COM.CF_UNICODETEXT: names[i] = "CF_UNICODETEXT"; break; //$NON-NLS-1$ + case COM.CF_ENHMETAFILE: names[i] = "CF_ENHMETAFILE"; break; //$NON-NLS-1$ + case COM.CF_LOCALE: names[i] = "CF_LOCALE"; break; //$NON-NLS-1$ + case COM.CF_MAX: names[i] = "CF_MAX"; break; //$NON-NLS-1$ + default: names[i] = "UNKNOWN"; //$NON-NLS-1$ + } + } + } + return names; +} + +private FORMATETC*[] _getAvailableTypes() { + FORMATETC*[] types = null; + IDataObject dataObject; + /* OleGetClipboard([out] ppDataObject). + * AddRef has already been called on ppDataObject by the callee and must be released by the caller. + */ + if (COM.OleGetClipboard(&dataObject) !is COM.S_OK) return types; + IEnumFORMATETC enumFormatetc; + /* EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc) + * AddRef has already been called on ppenumFormatetc by the callee and must be released by the caller. + */ + int rc = dataObject.EnumFormatEtc(COM.DATADIR_GET, &enumFormatetc); + dataObject.Release(); + if (rc !is COM.S_OK)return types; + // Loop over enumerator and save any types that match what we are looking for + //auto rgelt = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, FORMATETC.sizeof); + uint[1] pceltFetched; + FORMATETC rgelt; + enumFormatetc.Reset(); + while (enumFormatetc.Next(1, &rgelt, pceltFetched.ptr) is COM.S_OK && pceltFetched[0] is 1) { + FORMATETC* formatetc = new FORMATETC(); + COM.MoveMemory(formatetc, &rgelt, FORMATETC.sizeof); + FORMATETC*[] newTypes = new FORMATETC*[types.length + 1]; + SimpleType!(FORMATETC*).arraycopy(types, 0, newTypes, 0, types.length); + newTypes[types.length] = formatetc; + types = newTypes; + } + //OS.GlobalFree(rgelt); + enumFormatetc.Release(); + return types; +} +} + +private class _IDataObjectImpl : IDataObject { + + Clipboard parent; + this(Clipboard p) { parent = p; } +extern (Windows): + // interface of IUnknown + HRESULT QueryInterface(GUID* riid, void ** ppvObject) { return parent.QueryInterface(riid, ppvObject); } + ULONG AddRef() { return parent.AddRef(); } + ULONG Release() { return parent.Release(); } + + // interface IDataObject + LRESULT GetData( FORMATETC *pFormatetc, STGMEDIUM *pmedium) { return parent.GetData(pFormatetc, pmedium); } + LRESULT GetDataHere(FORMATETC * pFormatetc, STGMEDIUM * pmedium) { return COM.E_NOTIMPL; } + LRESULT QueryGetData(FORMATETC* pFormatetc) { return parent.QueryGetData(pFormatetc); } + LRESULT GetCanonicalFormatEtc(FORMATETC* pFormatetcIn, FORMATETC* pFormatetcOut) { return COM.E_NOTIMPL; } + LRESULT SetData(FORMATETC* pFormatetc, STGMEDIUM * pmedium, BOOL fRelease) { return COM.E_NOTIMPL; } + LRESULT EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC * ppenumFormatetc) { return parent.EnumFormatEtc(dwDirection, ppenumFormatetc); } + LRESULT DAdvise(FORMATETC* pFormatetc, DWORD advf, IAdviseSink pAdvSink, DWORD* pdwConnection) { return COM.E_NOTIMPL; } + LRESULT DUnadvise(DWORD dwConnection) { return COM.E_NOTIMPL; } + LRESULT EnumDAdvise(IEnumSTATDATA * ppenumAdvise) { return COM.E_NOTIMPL; } +} + diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DND.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DND.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,272 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DND; + + +import dwt.DWT; +import dwt.DWTError; +import dwt.DWTException; + +import tango.util.Convert; + +/** + * + * Class DND contains all the constants used in defining a + * DragSource or a DropTarget. + * + */ +public class DND { + + /** + * The transfer mechanism for data that is being cut + * and then pasted or copied and then pasted (value is 1). + * + * @see Clipboard + * + * @since 3.1 + */ + public const static int CLIPBOARD = 1 << 0; + + /** + * The transfer mechanism for clients that use the selection + * mechanism (value is 2). + * + * @see Clipboard + * + * @since 3.1 + */ + public const static int SELECTION_CLIPBOARD = 1 << 1; + + /** + * Drag and Drop Operation: no drag/drop operation performed + * (value is 0). + */ + public const static int DROP_NONE = 0; + + /** + * Drag and Drop Operation: a copy of the data in the drag source is + * added to the drop target (value is 1 << 0). + */ + public const static int DROP_COPY = 1 << 0; + + /** + * Drag and Drop Operation: a copy of the data is added to the drop target and + * the original data is removed from the drag source (value is 1 << 1). + */ + public const static int DROP_MOVE = 1 << 1; + + /** + * Drag and Drop Operation: the drop target makes a link to the data in + * the drag source (value is 1 << 2). + */ + public const static int DROP_LINK = 1 << 2; + + /** + * Drag and Drop Operation: the drop target moves the data and the drag source removes + * any references to the data and updates its display. This is not available on all platforms + * and is only used when a non-DWT application is the drop target. In this case, the DWT + * drag source is informed in the dragFinished event that the drop target has moved the data. + * (value is 1 << 3). + * + * @see DragSourceListener#dragFinished + */ + public const static int DROP_TARGET_MOVE = 1 << 3; + + /** + * Drag and Drop Operation: During a dragEnter event or a dragOperationChanged, if no modifier keys + * are pressed, the operation is set to DROP_DEFAULT. The application can choose what the default + * operation should be by setting a new value in the operation field. If no value is choosen, the + * default operation for the platform will be selected (value is 1 << 4). + * + * @see DropTargetListener#dragEnter + * @see DropTargetListener#dragOperationChanged + * @since 2.0 + */ + public const static int DROP_DEFAULT = 1 << 4; + + /** + * DragSource Event: the drop has successfully completed or has been terminated (such as hitting + * the ESC key); perform cleanup such as removing data on a move operation (value is 2000). + */ + public static const int DragEnd = 2000; + + /** + * DragSource Event: the data to be dropped is required from the drag source (value is 2001). + */ + public static const int DragSetData = 2001; + + /** + * DropTarget Event: the cursor has entered the drop target boundaries (value is 2002). + */ + public static const int DragEnter = 2002; + + /** + * DropTarget Event: the cursor has left the drop target boundaries OR the drop + * operation has been cancelled (such as by hitting ECS) OR the drop is about to + * happen (user has released the mous ebutotn over this target) (value is 2003). + */ + public static const int DragLeave = 2003; + + /** + * DropTarget Event: the cursor is over the drop target (value is 2004). + */ + public static const int DragOver = 2004; + + /** + * DropTarget Event: the operation being performed has changed usually due to the user + * changing the selected modifier keys while dragging (value is 2005). + */ + public static const int DragOperationChanged = 2005; + + /** + * DropTarget Event: the data has been dropped (value is 2006). + */ + public static const int Drop = 2006; + + /** + * DropTarget Event: the drop target is given a last chance to modify the drop (value is 2007). + */ + public static const int DropAccept = 2007; + + /** + * DragSource Event: a drag is about to begin (value is 2008). + */ + public static const int DragStart = 2008; + + /** + * DropTarget drag under effect: No effect is shown (value is 0). + */ + public static const int FEEDBACK_NONE = 0; + + /** + * DropTarget drag under effect: The item under the cursor is selected; applies to tables + * and trees (value is 1). + */ + public static const int FEEDBACK_SELECT = 1; + + /** + * DropTarget drag under effect: An insertion mark is shown before the item under the cursor; applies to + * trees (value is 2). + */ + public static const int FEEDBACK_INSERT_BEFORE = 2; + + /** + * DropTarget drag under effect:An insertion mark is shown after the item under the cursor; applies to + * trees (value is 4). + */ + public static const int FEEDBACK_INSERT_AFTER = 4; + + /** + * DropTarget drag under effect: The widget is scrolled up or down to allow the user to drop on items that + * are not currently visible; applies to tables and trees (value is 8). + */ + public static const int FEEDBACK_SCROLL = 8; + + /** + * DropTarget drag under effect: The item currently under the cursor is expanded to allow the user to + * select a drop target from a sub item; applies to trees (value is 16). + */ + public static const int FEEDBACK_EXPAND = 16; + + /** + * Error code: drag source can not be initialized (value is 2000). + */ + public static const int ERROR_CANNOT_INIT_DRAG = 2000; + + /** + * Error code: drop target cannot be initialized (value is 2001). + */ + public static const int ERROR_CANNOT_INIT_DROP = 2001; + + /** + * Error code: Data can not be set on system clipboard (value is 2002). + */ + public static const int ERROR_CANNOT_SET_CLIPBOARD = 2002; + + /** + * Error code: Data does not have correct format for type (value is 2003). + * @since 3.1 + */ + public static const int ERROR_INVALID_DATA = 2003; + + + static const char[] INIT_DRAG_MESSAGE = "Cannot initialize Drag"; //$NON-NLS-1$ + static const char[] INIT_DROP_MESSAGE = "Cannot initialize Drop"; //$NON-NLS-1$ + static const char[] CANNOT_SET_CLIPBOARD_MESSAGE = "Cannot set data in clipboard"; //$NON-NLS-1$ + static const char[] INVALID_DATA_MESSAGE = "Data does not have correct format for type"; //$NON-NLS-1$ + +/** + * Throws an appropriate exception based on the passed in error code. + * + * @param code the DND error code + */ +public static void error (int code) { + error (code, 0); +} + +/** + * Throws an appropriate exception based on the passed in error code. + * The hresult argument should be either 0, or the + * platform specific error code. + *

+ * In DND, errors are reported by throwing one of three exceptions: + *

+ *
java.lang.IllegalArgumentException
+ *
thrown whenever one of the API methods is invoked with an illegal argument
+ *
org.eclipse.swt.DWTException (extends java.lang.RuntimeException)
+ *
thrown whenever a recoverable error happens internally in DWT
+ *
org.eclipse.swt.DWTError (extends java.lang.Error)
+ *
thrown whenever a non-recoverable error happens internally in DWT
+ *
+ * This method provides the logic which maps between error codes + * and one of the above exceptions. + *

+ * + * @param code the DND error code. + * @param hresult the platform specific error code. + * + * @see DWTError + * @see DWTException + * @see IllegalArgumentException + */ +public static void error (int code, int hresult) { + switch (code) { + /* OS Failure/Limit (fatal, may occur only on some platforms) */ + case DND.ERROR_CANNOT_INIT_DRAG:{ + char[] msg = DND.INIT_DRAG_MESSAGE; + if (hresult !is 0) msg ~= " result = "~to!(char[])(hresult); //$NON-NLS-1$ + throw new DWTError (code, msg); + } + case DND.ERROR_CANNOT_INIT_DROP:{ + char[] msg = DND.INIT_DROP_MESSAGE; + if (hresult !is 0) msg ~= " result = "~to!(char[])(hresult); //$NON-NLS-1$ + throw new DWTError (code, msg); + } + case DND.ERROR_CANNOT_SET_CLIPBOARD:{ + char[] msg = DND.CANNOT_SET_CLIPBOARD_MESSAGE; + if (hresult !is 0) msg ~= " result = "~to!(char[])(hresult); //$NON-NLS-1$ + throw new DWTError (code, msg); + } + case DND.ERROR_INVALID_DATA:{ + char[] msg = DND.INVALID_DATA_MESSAGE; + if (hresult !is 0) msg ~= " result = "~to!(char[])(hresult); //$NON-NLS-1$ + throw new DWTException (code, msg); + } + default: + } + + /* Unknown/Undefined Error */ + DWT.error(code); +} + +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DNDEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DNDEvent.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DNDEvent; + + + +import dwt.graphics.Image; +import dwt.widgets.Event; +import dwt.dnd.TransferData; + +class DNDEvent : Event { + public TransferData dataType; + public TransferData[] dataTypes; + public int operations; + public int feedback; + public Image image; +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DNDListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DNDListener.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,137 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DNDListener; + + +import dwt.internal.DWTEventListener; +import dwt.widgets.Event; +import dwt.widgets.TypedListener; +import dwt.widgets.Widget; +import dwt.dnd.DND; +import dwt.dnd.DNDEvent; +import dwt.dnd.DragSourceEvent; +import dwt.dnd.DragSourceEffect; +import dwt.dnd.DragSource; +import dwt.dnd.DragSourceListener; +import dwt.dnd.DropTargetEvent; +import dwt.dnd.DropTargetListener; +import dwt.dnd.DropTargetEffect; +import dwt.dnd.DropTarget; + + +class DNDListener : TypedListener { + Widget dndWidget; +/** + * DNDListener constructor comment. + * @param listener org.eclipse.swt.internal.DWTEventListener + */ +this(DWTEventListener listener) { + super(listener); +} +public override void handleEvent (Event e) { + switch (e.type) { + case DND.DragStart: { + DragSourceEvent event = new DragSourceEvent(cast(DNDEvent)e); + DragSourceEffect sourceEffect = (cast(DragSource) dndWidget).getDragSourceEffect(); + if (sourceEffect !is null) { + sourceEffect.dragStart (event); + } + (cast(DragSourceListener) eventListener).dragStart (event); + event.updateEvent(cast(DNDEvent)e); + break; + } + case DND.DragEnd: { + DragSourceEvent event = new DragSourceEvent(cast(DNDEvent)e); + DragSourceEffect sourceEffect = (cast(DragSource) dndWidget).getDragSourceEffect(); + if (sourceEffect !is null) { + sourceEffect.dragFinished (event); + } + (cast(DragSourceListener) eventListener).dragFinished (event); + event.updateEvent(cast(DNDEvent)e); + break; + } + case DND.DragSetData: { + DragSourceEvent event = new DragSourceEvent(cast(DNDEvent)e); + DragSourceEffect sourceEffect = (cast(DragSource) dndWidget).getDragSourceEffect(); + if (sourceEffect !is null) { + sourceEffect.dragSetData (event); + } + (cast(DragSourceListener) eventListener).dragSetData (event); + event.updateEvent(cast(DNDEvent)e); + break; + } + case DND.DragEnter: { + DropTargetEvent event = new DropTargetEvent(cast(DNDEvent)e); + (cast(DropTargetListener) eventListener).dragEnter (event); + DropTargetEffect dropEffect = (cast(DropTarget) dndWidget).getDropTargetEffect(); + if (dropEffect !is null) { + dropEffect.dragEnter (event); + } + event.updateEvent(cast(DNDEvent)e); + break; + } + case DND.DragLeave: { + DropTargetEvent event = new DropTargetEvent(cast(DNDEvent)e); + (cast(DropTargetListener) eventListener).dragLeave (event); + DropTargetEffect dropEffect = (cast(DropTarget) dndWidget).getDropTargetEffect(); + if (dropEffect !is null) { + dropEffect.dragLeave (event); + } + event.updateEvent(cast(DNDEvent)e); + break; + } + case DND.DragOver: { + DropTargetEvent event = new DropTargetEvent(cast(DNDEvent)e); + (cast(DropTargetListener) eventListener).dragOver (event); + DropTargetEffect dropEffect = (cast(DropTarget) dndWidget).getDropTargetEffect(); + if (dropEffect !is null) { + dropEffect.dragOver (event); + } + event.updateEvent(cast(DNDEvent)e); + break; + } + case DND.Drop: { + DropTargetEvent event = new DropTargetEvent(cast(DNDEvent)e); + (cast(DropTargetListener) eventListener).drop (event); + DropTargetEffect dropEffect = (cast(DropTarget) dndWidget).getDropTargetEffect(); + if (dropEffect !is null) { + dropEffect.drop (event); + } + event.updateEvent(cast(DNDEvent)e); + break; + } + case DND.DropAccept: { + DropTargetEvent event = new DropTargetEvent(cast(DNDEvent)e); + (cast(DropTargetListener) eventListener).dropAccept (event); + DropTargetEffect dropEffect = (cast(DropTarget) dndWidget).getDropTargetEffect(); + if (dropEffect !is null) { + dropEffect.dropAccept (event); + } + event.updateEvent(cast(DNDEvent)e); + break; + } + case DND.DragOperationChanged: { + DropTargetEvent event = new DropTargetEvent(cast(DNDEvent)e); + (cast(DropTargetListener) eventListener).dragOperationChanged (event); + DropTargetEffect dropEffect = (cast(DropTarget) dndWidget).getDropTargetEffect(); + if (dropEffect !is null) { + dropEffect.dragOperationChanged (event); + } + event.updateEvent(cast(DNDEvent)e); + break; + } + default: + + } +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DragSource.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DragSource.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,654 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DragSource; + + +import dwt.DWT; +import dwt.DWTError; +import dwt.DWTException; +import dwt.graphics.Image; +import dwt.graphics.Point; +import dwt.internal.ImageList; +import dwt.internal.ole.win32.COM; +import dwt.internal.win32.OS; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Display; +import dwt.widgets.Event; +import dwt.widgets.Listener; +import dwt.widgets.Table; +import dwt.widgets.Tree; +import dwt.widgets.Widget; + +/** + * + * DragSource defines the source object for a drag and drop transfer. + * + *

IMPORTANT: This class is not intended to be subclassed.

+ * + *

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

+ * + *

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.

+ * + *

The application supplies the content of the data being transferred by implementing the + * DragSourceListener and associating it with the DragSource via DragSource#addDragListener.

+ * + *

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.

+ * + *

The following example shows a Label widget that allows text to be dragged from it.

+ * + *
+ *  // Enable a label as a Drag Source
+ *  Label label = new Label(shell, DWT.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() is 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 is DND.DROP_MOVE)
+ *              label.setText("");
+ *      }
+ *  });
+ * 
+ * + * + *
+ *
Styles
DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK
+ *
Events
DND.DragStart, DND.DragSetData, DND.DragEnd
+ *
+ */ +public class DragSource : Widget { + + // info for registering as a drag source + Control control; + Listener controlListener; + Transfer[] transferAgents = new Transfer[0]; + DragSourceEffect dragEffect; + Composite topControl; + + // ole interfaces + COMObject iDropSource; + COMObject iDataObject; + int refCount; + + //workaround - track the operation performed by the drop target for DragEnd event + int dataEffect = DND.DROP_NONE; + + static final String DEFAULT_DRAG_SOURCE_EFFECT = "DEFAULT_DRAG_SOURCE_EFFECT"; //$NON-NLS-1$ + static final String DRAGSOURCEID = "DragSource"; //$NON-NLS-1$ + static final int CFSTR_PERFORMEDDROPEFFECT = Transfer.registerType("Performed DropEffect"); //$NON-NLS-1$ + +/** + * Creates a new DragSource to handle dragging from the specified Control. + * 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 Control 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 DWTException
    + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent
  • + *
  • ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass
  • + *
+ * @exception DWTError
    + *
  • 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
  • + *
+ * + *

NOTE: ERROR_CANNOT_INIT_DRAG should be an DWTException, since it is a + * recoverable error, but can not be changed due to backward compatibility.

+ * + * @see Widget#dispose + * @see DragSource#checkSubclass + * @see DND#DROP_NONE + * @see DND#DROP_COPY + * @see DND#DROP_MOVE + * @see DND#DROP_LINK + */ +public this(Control control, int style) { + super(control, checkStyle(style)); + this.control = control; + if (control.getData(DRAGSOURCEID) !is null) { + DND.error(DND.ERROR_CANNOT_INIT_DRAG); + } + control.setData(DRAGSOURCEID, this); + createCOMInterfaces(); + this.AddRef(); + + controlListener = new Listener() { + public void handleEvent(Event event) { + if (event.type is DWT.Dispose) { + if (!DragSource.this.isDisposed()) { + DragSource.this.dispose(); + } + } + if (event.type is DWT.DragDetect) { + if (!DragSource.this.isDisposed()) { + DragSource.this.drag(event); + } + } + } + }; + control.addListener(DWT.Dispose, controlListener); + control.addListener(DWT.DragDetect, controlListener); + + this.addListener(DWT.Dispose, new Listener() { + public void handleEvent(Event e) { + DragSource.this.onDispose(); + } + }); + + Object effect = control.getData(DEFAULT_DRAG_SOURCE_EFFECT); + if (effect instanceof DragSourceEffect) { + dragEffect = (DragSourceEffect) effect; + } else if (control instanceof Tree) { + dragEffect = new TreeDragSourceEffect((Tree) control); + } else if (control instanceof Table) { + dragEffect = new TableDragSourceEffect((Table) control); + } +} + +static int checkStyle(int style) { + if (style is DWT.NONE) return DND.DROP_MOVE; + return style; +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when a drag and drop operation is in progress, by sending + * it one of the messages defined in the DragSourceListener + * interface. + * + *

    + *
  • dragStart 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. + *
  • dragSetData is called when the data is required from the drag source. + *
  • dragFinished 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. + *

+ * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the listener is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @see DragSourceListener + * @see #removeDragListener + * @see DragSourceEvent + */ +public void addDragListener(DragSourceListener listener) { + if (listener is null) DND.error(DWT.ERROR_NULL_ARGUMENT); + DNDListener typedListener = new DNDListener(listener); + typedListener.dndWidget = this; + addListener(DND.DragStart, typedListener); + addListener(DND.DragSetData, typedListener); + addListener(DND.DragEnd, typedListener); +} + +private int AddRef() { + refCount++; + return refCount; +} + +private void createCOMInterfaces() { + // register each of the interfaces that this object implements + iDropSource = new COMObject(new int[]{2, 0, 0, 2, 1}){ + public int method0(int[] args) {return QueryInterface(args[0], args[1]);} + public int method1(int[] args) {return AddRef();} + public int method2(int[] args) {return Release();} + public int method3(int[] args) {return QueryContinueDrag(args[0], args[1]);} + public int method4(int[] args) {return GiveFeedback(args[0]);} + }; + + iDataObject = new COMObject(new int[]{2, 0, 0, 2, 2, 1, 2, 3, 2, 4, 1, 1}){ + public int method0(int[] args) {return QueryInterface(args[0], args[1]);} + public int method1(int[] args) {return AddRef();} + public int method2(int[] args) {return Release();} + public int method3(int[] args) {return GetData(args[0], args[1]);} + // method4 GetDataHere - not implemented + public int method5(int[] args) {return QueryGetData(args[0]);} + // method6 GetCanonicalFormatEtc - not implemented + public int method7(int[] args) {return SetData(args[0], args[1], args[2]);} + public int method8(int[] args) {return EnumFormatEtc(args[0], args[1]);} + // method9 DAdvise - not implemented + // method10 DUnadvise - not implemented + // method11 EnumDAdvise - not implemented + }; +} + +protected void checkSubclass() { + String name = getClass().getName(); + String validName = DragSource.class.getName(); + if (!validName.equals(name)) { + DND.error(DWT.ERROR_INVALID_SUBCLASS); + } +} + +private void disposeCOMInterfaces() { + if (iDropSource !is null) + iDropSource.dispose(); + iDropSource = null; + + if (iDataObject !is null) + iDataObject.dispose(); + iDataObject = null; +} + +private void drag(Event dragEvent) { + DNDEvent event = new DNDEvent(); + event.widget = this; + event.x = dragEvent.x; + event.y = dragEvent.y; + event.time = OS.GetMessageTime(); + event.doit = true; + notifyListeners(DND.DragStart,event); + if (!event.doit || transferAgents is null || transferAgents.length is 0 ) return; + + int[] pdwEffect = new int[1]; + int operations = opToOs(getStyle()); + Display display = control.getDisplay(); + String key = "org.eclipse.swt.internal.win32.runMessagesInIdle"; //$NON-NLS-1$ + Object oldValue = display.getData(key); + display.setData(key, new bool(true)); + ImageList imagelist = null; + Image image = event.image; + if (image !is null) { + imagelist = new ImageList(DWT.NONE); + imagelist.add(image); + topControl = control.getShell(); + OS.ImageList_BeginDrag(imagelist.getHandle(), 0, 0, 0); + Point location = topControl.getLocation(); + /* + * Feature in Windows. When ImageList_DragEnter() is called, + * it takes a snapshot of the screen If a drag is started + * when another window is in front, then the snapshot will + * contain part of the other window, causing pixel corruption. + * The fix is to force all paints to be delivered before + * calling ImageList_DragEnter(). + */ + if (OS.IsWinCE) { + OS.UpdateWindow (topControl.handle); + } else { + int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN; + OS.RedrawWindow (topControl.handle, null, 0, flags); + } + OS.ImageList_DragEnter(topControl.handle, dragEvent.x - location.x, dragEvent.y - location.y); + } + int result = COM.DoDragDrop(iDataObject.getAddress(), iDropSource.getAddress(), operations, pdwEffect); + if (imagelist !is null) { + OS.ImageList_DragLeave(topControl.handle); + OS.ImageList_EndDrag(); + imagelist.dispose(); + topControl = null; + } + display.setData(key, oldValue); + int operation = osToOp(pdwEffect[0]); + if (dataEffect is DND.DROP_MOVE) { + operation = (operation is DND.DROP_NONE || operation is DND.DROP_COPY) ? DND.DROP_TARGET_MOVE : DND.DROP_MOVE; + } else { + if (dataEffect !is DND.DROP_NONE) { + operation = dataEffect; + } + } + event = new DNDEvent(); + event.widget = this; + event.time = OS.GetMessageTime(); + event.doit = (result is COM.DRAGDROP_S_DROP); + event.detail = operation; + notifyListeners(DND.DragEnd,event); + dataEffect = DND.DROP_NONE; +} +/* + * EnumFormatEtc([in] dwDirection, [out] ppenumFormatetc) + * Ownership of ppenumFormatetc transfers from callee to caller so reference count on ppenumFormatetc + * must be incremented before returning. Caller is responsible for releasing ppenumFormatetc. + */ +private int EnumFormatEtc(int dwDirection, int ppenumFormatetc) { + // only allow getting of data - SetData is not currently supported + if (dwDirection is COM.DATADIR_SET) return COM.E_NOTIMPL; + + // what types have been registered? + TransferData[] allowedDataTypes = new TransferData[0]; + for (int i = 0; i < transferAgents.length; i++){ + Transfer transferAgent = transferAgents[i]; + if (transferAgent !is null) { + TransferData[] formats = transferAgent.getSupportedTypes(); + TransferData[] newAllowedDataTypes = new TransferData[allowedDataTypes.length + formats.length]; + System.arraycopy(allowedDataTypes, 0, newAllowedDataTypes, 0, allowedDataTypes.length); + System.arraycopy(formats, 0, newAllowedDataTypes, allowedDataTypes.length, formats.length); + allowedDataTypes = newAllowedDataTypes; + } + } + + OleEnumFORMATETC enumFORMATETC = new OleEnumFORMATETC(); + enumFORMATETC.AddRef(); + + FORMATETC[] formats = new FORMATETC[allowedDataTypes.length]; + for (int i = 0; i < formats.length; i++){ + formats[i] = allowedDataTypes[i].formatetc; + } + enumFORMATETC.setFormats(formats); + + OS.MoveMemory(ppenumFormatetc, new int[] {enumFORMATETC.getAddress()}, 4); + return COM.S_OK; +} +/** + * Returns the Control which is registered for this DragSource. This is the control that the + * user clicks in to initiate dragging. + * + * @return the Control which is registered for this DragSource + */ +public Control getControl() { + return control; +} + +private int GetData(int pFormatetc, int pmedium) { + /* Called by a data consumer to obtain data from a source data object. + The GetData method renders the data described in the specified FORMATETC + structure and transfers it through the specified STGMEDIUM structure. + The caller then assumes responsibility for releasing the STGMEDIUM structure. + */ + if (pFormatetc is 0 || pmedium is 0) return COM.E_INVALIDARG; + + if (QueryGetData(pFormatetc) !is COM.S_OK) return COM.DV_E_FORMATETC; + + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.E_FAIL; + + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = OS.GetMessageTime(); + event.dataType = transferData; + notifyListeners(DND.DragSetData,event); + + // get matching transfer agent to perform conversion + Transfer transfer = null; + for (int i = 0; i < transferAgents.length; i++){ + Transfer transferAgent = transferAgents[i]; + if (transferAgent !is null && transferAgent.isSupportedType(transferData)){ + transfer = transferAgent; + break; + } + } + + if (transfer is null) return COM.DV_E_FORMATETC; + transfer.javaToNative(event.data, transferData); + if (transferData.result !is COM.S_OK) return transferData.result; + COM.MoveMemory(pmedium, transferData.stgmedium, STGMEDIUM.sizeof); + return transferData.result; +} + +/** + * Returns the drag effect that is registered for this DragSource. This drag + * effect will be used during a drag and drop operation. + * + * @return the drag effect that is registered for this DragSource + * + * @since 3.3 + */ +public DragSourceEffect getDragSourceEffect() { + return dragEffect; +} + +/** + * Returns the list of data types that can be transferred by this DragSource. + * + * @return the list of data types that can be transferred by this DragSource + */ +public Transfer[] getTransfer(){ + return transferAgents; +} + +private int GiveFeedback(int dwEffect) { + return COM.DRAGDROP_S_USEDEFAULTCURSORS; +} + +private int QueryContinueDrag(int fEscapePressed, int grfKeyState) { + if (fEscapePressed !is 0){ + if (topControl !is null) OS.ImageList_DragLeave(topControl.handle); + return COM.DRAGDROP_S_CANCEL; + } + /* + * Bug in Windows. On some machines that do not have XBUTTONs, + * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set, + * causing mouse capture to become stuck. The fix is to test + * for the extra buttons only when they exist. + */ + int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON; +// if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2; + if ((grfKeyState & mask) is 0) { + if (topControl !is null) OS.ImageList_DragLeave(topControl.handle); + return COM.DRAGDROP_S_DROP; + } + + if (topControl !is null) { + Display display = getDisplay(); + Point pt = display.getCursorLocation(); + Point location = topControl.getLocation(); + OS.ImageList_DragMove(pt.x - location.x, pt.y - location.y); + } + return COM.S_OK; +} + +private void onDispose() { + if (control is null) return; + this.Release(); + if (controlListener !is null){ + control.removeListener(DWT.Dispose, controlListener); + control.removeListener(DWT.DragDetect, controlListener); + } + controlListener = null; + control.setData(DRAGSOURCEID, null); + control = null; + transferAgents = null; +} + +private int opToOs(int operation) { + int osOperation = 0; + if ((operation & DND.DROP_COPY) !is 0){ + osOperation |= COM.DROPEFFECT_COPY; + } + if ((operation & DND.DROP_LINK) !is 0) { + osOperation |= COM.DROPEFFECT_LINK; + } + if ((operation & DND.DROP_MOVE) !is 0) { + osOperation |= COM.DROPEFFECT_MOVE; + } + return osOperation; +} + +private int osToOp(int osOperation){ + int operation = 0; + if ((osOperation & COM.DROPEFFECT_COPY) !is 0){ + operation |= DND.DROP_COPY; + } + if ((osOperation & COM.DROPEFFECT_LINK) !is 0) { + operation |= DND.DROP_LINK; + } + if ((osOperation & COM.DROPEFFECT_MOVE) !is 0) { + operation |= DND.DROP_MOVE; + } + return operation; +} + +private int QueryGetData(int pFormatetc) { + if (transferAgents is null) return COM.E_FAIL; + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, pFormatetc, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + + // is this type supported by the transfer agent? + for (int i = 0; i < transferAgents.length; i++){ + Transfer transfer = transferAgents[i]; + if (transfer !is null && transfer.isSupportedType(transferData)) + return COM.S_OK; + } + + return COM.DV_E_FORMATETC; +} + +/* QueryInterface([in] riid, [out] ppvObject) + * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject + * must be incremented before returning. Caller is responsible for releasing ppvObject. + */ +private int QueryInterface(int riid, int ppvObject) { + if (riid is 0 || ppvObject is 0) + return COM.E_INVALIDARG; + GUID guid = new GUID(); + COM.MoveMemory(guid, riid, GUID.sizeof); + + if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDropSource)) { + OS.MoveMemory(ppvObject, new int[] {iDropSource.getAddress()}, 4); + AddRef(); + return COM.S_OK; + } + + if (COM.IsEqualGUID(guid, COM.IIDIDataObject) ) { + OS.MoveMemory(ppvObject, new int[] {iDataObject.getAddress()}, 4); + AddRef(); + return COM.S_OK; + } + + OS.MoveMemory(ppvObject, new int[] {0}, 4); + return COM.E_NOINTERFACE; +} + +private int Release() { + refCount--; + if (refCount is 0) { + disposeCOMInterfaces(); + COM.CoFreeUnusedLibraries(); + } + return refCount; +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when a drag and drop operation is in progress. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the listener is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @see DragSourceListener + * @see #addDragListener + */ +public void removeDragListener(DragSourceListener listener) { + if (listener is null) DND.error(DWT.ERROR_NULL_ARGUMENT); + removeListener(DND.DragStart, listener); + removeListener(DND.DragSetData, listener); + removeListener(DND.DragEnd, listener); +} + +private int SetData(int pFormatetc, int pmedium, int fRelease) { + if (pFormatetc is 0 || pmedium is 0) return COM.E_INVALIDARG; + FORMATETC formatetc = new FORMATETC(); + COM.MoveMemory(formatetc, pFormatetc, FORMATETC.sizeof); + if (formatetc.cfFormat is CFSTR_PERFORMEDDROPEFFECT && formatetc.tymed is COM.TYMED_HGLOBAL) { + STGMEDIUM stgmedium = new STGMEDIUM(); + COM.MoveMemory(stgmedium, pmedium,STGMEDIUM.sizeof); + int[] ptrEffect = new int[1]; + OS.MoveMemory(ptrEffect, stgmedium.unionField,4); + int[] effect = new int[1]; + OS.MoveMemory(effect, ptrEffect[0],4); + dataEffect = osToOp(effect[0]); + } + if (fRelease is 1) { + COM.ReleaseStgMedium(pmedium); + } + return COM.S_OK; +} + +/** + * Specifies the drag effect for this DragSource. This drag effect will be + * used during a drag and drop operation. + * + * @param effect the drag effect that is registered for this DragSource + * + * @since 3.3 + */ +public void setDragSourceEffect(DragSourceEffect effect) { + dragEffect = effect; +} + +/** + * Specifies the list of data types that can be transferred by this DragSource. + * The application must be able to provide data to match each of these types when + * a successful drop has occurred. + * + * @param transferAgents a list of Transfer objects which define the types of data that can be + * dragged from this source + */ +public void setTransfer(Transfer[] transferAgents){ + this.transferAgents = transferAgents; +} + +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DragSourceAdapter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DragSourceAdapter.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DragSourceAdapter; + +import dwt.dnd.DragSourceListener; +import dwt.dnd.DragSourceEvent; + +/** + * This adapter class provides default implementations for the + * methods described by the DragSourceListener interface. + * + *

Classes that wish to deal with DragSourceEvents can + * extend this class and override only the methods which they are + * interested in.

+ * + * @see DragSourceListener + * @see DragSourceEvent + */ +public class DragSourceAdapter : DragSourceListener { + /** + * This implementation of dragStart permits the drag operation to start. + * For additional information see DragSourceListener.dragStart. + */ + public void dragStart(DragSourceEvent event){} + /** + * This implementation of dragFinished does nothing. + * For additional information see DragSourceListener.dragFinished. + */ + public void dragFinished(DragSourceEvent event){} + /** + * This implementation of dragSetData does nothing. + * For additional information see DragSourceListener.dragSetData. + */ + public void dragSetData(DragSourceEvent event){} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DragSourceEffect.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DragSourceEffect.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,68 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DragSourceEffect; + + +import dwt.DWT; +import dwt.widgets.Control; +import dwt.dnd.DragSourceAdapter; + +/** + * This class provides default implementations to display a drag source + * effect during a drag and drop operation. The current implementation + * does not provide any visual feedback. + * + *

The drag source effect has the same API as the + * DragSourceAdapter so that it can provide custom visual + * feedback when a DragSourceEvent occurs. + *

+ * + *

Classes that wish to provide their own drag source effect such as + * displaying a default source image during a drag can extend the DragSourceEffect + * class, override the DragSourceAdapter.dragStart method and set + * the field DragSourceEvent.image with their own image. + * The image should be disposed when DragSourceAdapter.dragFinished is called. + *

+ * + * @see DragSourceAdapter + * @see DragSourceEvent + * + * @since 3.3 + */ +public class DragSourceEffect : DragSourceAdapter { + Control control = null; + + /** + * Creates a new DragSourceEffect to handle drag effect from the specified Control. + * + * @param control the Control that the user clicks on to initiate the drag + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the control is null
  • + *
+ */ + public this(Control control) { + if (control is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); + this.control = control; + } + + /** + * Returns the Control which is registered for this DragSourceEffect. This is the control that the + * user clicks in to initiate dragging. + * + * @return the Control which is registered for this DragSourceEffect + */ + public Control getControl() { + return control; + } +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DragSourceEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DragSourceEvent.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,111 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DragSourceEvent; + + +import dwt.events.TypedEvent; +import dwt.widgets.Event; +import dwt.graphics.Image; +import dwt.dnd.TransferData; +import dwt.dnd.DNDEvent; + +/** + * The DragSourceEvent contains the event information passed in the methods of the DragSourceListener. + * + * @see DragSourceListener + */ +public class DragSourceEvent : TypedEvent { + /** + * The operation that was performed. + * @see DND#DROP_NONE + * @see DND#DROP_MOVE + * @see DND#DROP_COPY + * @see DND#DROP_LINK + * @see DND#DROP_TARGET_MOVE + */ + public int detail; + + /** + * In dragStart, the doit field determines if the drag and drop operation + * should proceed; in dragFinished, the doit field indicates whether + * the operation was performed successfully. + *

+ * In dragStart: + *

Flag to determine if the drag and drop operation should proceed. + * The application can set this value to false to prevent the drag from starting. + * Set to true by default.

+ * + *

In dragFinished:

+ *

Flag to indicate if the operation was performed successfully. + * True if the operation was performed successfully.

+ */ + public bool doit; + + /** + * In dragStart, the x coordinate (relative to the control) of the + * position the mouse went down to start the drag. + * @since 3.2 + */ + public int x; + /** + * In dragStart, the y coordinate (relative to the control) of the + * position the mouse went down to start the drag . + * @since 3.2 + */ + public int y; + + /** + * The type of data requested. + * Data provided in the data field must be of the same type. + */ + public TransferData dataType; + + /** + * The drag source image to be displayed during the drag. + *

A value of null indicates that no drag image will be displayed.

+ *

The default value is null.

+ * + * @since 3.3 + */ + public Image image; + + static const long serialVersionUID = 3257002142513770808L; + +/** + * Constructs a new instance of this class based on the + * information in the given untyped event. + * + * @param e the untyped event containing the information + */ +public this(DNDEvent e) { + super( cast(Event) e ); + this.data = e.data; + this.detail = e.detail; + this.doit = e.doit; + this.dataType = e.dataType; + this.x = e.x; + this.y = e.y; + this.image = e.image; +} +void updateEvent(DNDEvent e) { + e.widget = this.widget; + e.time = this.time; + e.data = this.data; + e.detail = this.detail; + e.doit = this.doit; + e.dataType = this.dataType; + e.x = this.x; + e.y = this.y; + e.image = this.image; +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DragSourceListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DragSourceListener.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DragSourceListener; + +import dwt.internal.DWTEventListener; +import dwt.dnd.DragSourceEvent; + +/** + * The DragSourceListener class provides event notification to the application for DragSource events. + * + *

When the user drops data on a DropTarget, the application which defines the DragSource + * must provide the dropped data by implementing dragSetData. In the dragSetData, the application + * must support all the data types that were specified in the DragSource#setTransfer method.

+ * + *

After the drop has completed successfully or has been aborted, the application which defines the + * DragSource is required to take the appropriate cleanup action. In the case of a successful + * move operation, the application must remove the data that was transferred.

+ * + */ +public interface DragSourceListener : DWTEventListener { + +/** + * 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. + * + *

The following fields in the DragSourceEvent apply: + *

    + *
  • (in)widget + *
  • (in)time + *
  • (in,out)doit + *

+ * + * @param event the information associated with the drag start event + * + * @see DragSourceEvent + */ +public void dragStart(DragSourceEvent event); + +/** + * The data is required from the drag source. + * + *

The following fields in the DragSourceEvent apply: + *

    + *
  • (in)widget + *
  • (in)time + *
  • (in)dataType - the type of data requested. + *
  • (out)data - the application inserts the actual data here (must match the dataType) + *

+ * + * @param event the information associated with the drag set data event + * + * @see DragSourceEvent + */ +public void dragSetData(DragSourceEvent event); + +/** + * 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. + * + *

The following fields in the DragSourceEvent apply: + *

    + *
  • (in)widget + *
  • (in)time + *
  • (in)doit + *
  • (in)detail + *

+ * + * @param event the information associated with the drag finished event + * + * @see DragSourceEvent + */ +public void dragFinished(DragSourceEvent event); +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DropTarget.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DropTarget.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,709 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DropTarget; + +import dwt.DWT; +import dwt.DWTError; +import dwt.DWTException; +import dwt.internal.ole.win32.COM; +import dwt.internal.win32.OS; +import dwt.widgets.Control; +import dwt.widgets.Event; +import dwt.widgets.Listener; +import dwt.widgets.Table; +import dwt.widgets.Tree; +import dwt.widgets.Widget; + +/** + * + * Class DropTarget defines the target object for a drag and drop transfer. + * + * IMPORTANT: This class is not intended to be subclassed. + * + *

This class identifies the Control 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 Control and a DropTarget. + * The DropTarget can receive data from within the same application or from other applications + * (such as text dragged from a text editor like Word).

+ * + *
+ *  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);
+ * 
+ * + *

The application is notified of data being dragged over this control and of when a drop occurs by + * implementing the interface DropTargetListener which uses the class + * DropTargetEvent. The application can modify the type of drag being performed + * on this Control at any stage of the drag by modifying the event.detail field or the + * event.currentDataType field. When the data is dropped, it is the responsibility of + * the application to copy this data for its own purposes. + * + *

+ *  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 is 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
+ *      }
+ *  });
+ * 
+ * + *
+ *
Styles
DND.DROP_NONE, DND.DROP_COPY, DND.DROP_MOVE, DND.DROP_LINK
+ *
Events
DND.DragEnter, DND.DragLeave, DND.DragOver, DND.DragOperationChanged, + * DND.DropAccept, DND.Drop
+ *
+ */ +public class DropTarget : Widget { + + Control control; + Listener controlListener; + Transfer[] transferAgents = new Transfer[0]; + DropTargetEffect dropEffect; + + // Track application selections + TransferData selectedDataType; + int selectedOperation; + + // workaround - There is no event for "operation changed" so track operation based on key state + int keyOperation = -1; + + // workaround - The dataobject address is only passed as an argument in drag enter and drop. + // To allow applications to query the data values during the drag over operations, + // maintain a reference to it. + IDataObject iDataObject; + + // interfaces + COMObject iDropTarget; + int refCount; + + static final String DEFAULT_DROP_TARGET_EFFECT = "DEFAULT_DROP_TARGET_EFFECT"; //$NON-NLS-1$ + static final String DROPTARGETID = "DropTarget"; //$NON-NLS-1$ + +/** + * Creates a new DropTarget to allow data to be dropped on the specified + * Control. + * 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 Control 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 DWTException
    + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent
  • + *
  • ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass
  • + *
+ * @exception DWTError
    + *
  • 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
  • + *
+ * + *

NOTE: ERROR_CANNOT_INIT_DROP should be an DWTException, since it is a + * recoverable error, but can not be changed due to backward compatibility.

+ * + * @see Widget#dispose + * @see DropTarget#checkSubclass + * @see DND#DROP_NONE + * @see DND#DROP_COPY + * @see DND#DROP_MOVE + * @see DND#DROP_LINK + */ +public this(Control control, int style) { + super (control, checkStyle(style)); + this.control = control; + if (control.getData(DROPTARGETID) !is null) { + DND.error(DND.ERROR_CANNOT_INIT_DROP); + } + control.setData(DROPTARGETID, this); + createCOMInterfaces(); + this.AddRef(); + + if (COM.CoLockObjectExternal(iDropTarget.getAddress(), true, true) !is COM.S_OK) + DND.error(DND.ERROR_CANNOT_INIT_DROP); + if (COM.RegisterDragDrop( control.handle, iDropTarget.getAddress()) !is COM.S_OK) + DND.error(DND.ERROR_CANNOT_INIT_DROP); + + controlListener = new Listener () { + public void handleEvent (Event event) { + if (!DropTarget.this.isDisposed()){ + DropTarget.this.dispose(); + } + } + }; + control.addListener (DWT.Dispose, controlListener); + + this.addListener(DWT.Dispose, new Listener () { + public void handleEvent (Event event) { + onDispose(); + } + }); + + Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT); + if (effect instanceof DropTargetEffect) { + dropEffect = (DropTargetEffect) effect; + } else if (control instanceof Table) { + dropEffect = new TableDropTargetEffect((Table) control); + } else if (control instanceof Tree) { + dropEffect = new TreeDropTargetEffect((Tree) control); + } +} + +static int checkStyle (int style) { + if (style is DWT.NONE) return DND.DROP_MOVE; + return style; +} + +/** + * Adds the listener to the collection of listeners who will + * be notified when a drag and drop operation is in progress, by sending + * it one of the messages defined in the DropTargetListener + * interface. + * + *

    + *
  • dragEnter is called when the cursor has entered the drop target boundaries + *
  • dragLeave is called when the cursor has left the drop target boundaries and just before + * the drop occurs or is cancelled. + *
  • dragOperationChanged is called when the operation being performed has changed + * (usually due to the user changing the selected modifier key(s) while dragging) + *
  • dragOver is called when the cursor is moving over the drop target + *
  • dropAccept 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 event.detail field + *
  • drop is called when the data is being dropped + *

+ * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the listener is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @see DropTargetListener + * @see #removeDropListener + * @see DropTargetEvent + */ +public void addDropListener(DropTargetListener listener) { + if (listener is null) DND.error (DWT.ERROR_NULL_ARGUMENT); + DNDListener typedListener = new DNDListener (listener); + typedListener.dndWidget = this; + addListener (DND.DragEnter, typedListener); + addListener (DND.DragLeave, typedListener); + addListener (DND.DragOver, typedListener); + addListener (DND.DragOperationChanged, typedListener); + addListener (DND.Drop, typedListener); + addListener (DND.DropAccept, typedListener); +} + +int AddRef() { + refCount++; + return refCount; +} + +protected void checkSubclass () { + String name = getClass().getName (); + String validName = DropTarget.class.getName(); + if (!validName.equals(name)) { + DND.error (DWT.ERROR_INVALID_SUBCLASS); + } +} + +void createCOMInterfaces() { + // register each of the interfaces that this object implements + iDropTarget = new COMObject(new int[]{2, 0, 0, 5, 4, 0, 5}){ + public int method0(int[] args) {return QueryInterface(args[0], args[1]);} + public int method1(int[] args) {return AddRef();} + public int method2(int[] args) {return Release();} + public int method3(int[] args) {return DragEnter(args[0], args[1], args[2], args[3], args[4]);} + public int method4(int[] args) {return DragOver(args[0], args[1], args[2], args[3]);} + public int method5(int[] args) {return DragLeave();} + public int method6(int[] args) {return Drop(args[0], args[1], args[2], args[3], args[4]);} + }; + +} + +void disposeCOMInterfaces() { + if (iDropTarget !is null) + iDropTarget.dispose(); + iDropTarget = null; +} + +int DragEnter(int pDataObject, int grfKeyState, int pt_x, int pt_y, int pdwEffect) { + selectedDataType = null; + selectedOperation = DND.DROP_NONE; + if (iDataObject !is null) iDataObject.Release(); + iDataObject = null; + + DNDEvent event = new DNDEvent(); + if (!setEventData(event, pDataObject, grfKeyState, pt_x, pt_y, pdwEffect)) { + OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4); + return COM.S_FALSE; + } + + // Remember the iDataObject because it is not passed into the DragOver callback + iDataObject = new IDataObject(pDataObject); + iDataObject.AddRef(); + + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + notifyListeners(DND.DragEnter, event); + refresh(); + if (event.detail is DND.DROP_DEFAULT) { + event.detail = (allowedOperations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + + selectedDataType = null; + for (int i = 0; i < allowedDataTypes.length; i++) { + if (TransferData.sameType(allowedDataTypes[i], event.dataType)){ + selectedDataType = allowedDataTypes[i]; + break; + } + } + + selectedOperation = DND.DROP_NONE; + if (selectedDataType !is null && ((allowedOperations & event.detail) !is 0)) { + selectedOperation = event.detail; + } + + OS.MoveMemory(pdwEffect, new int[] {opToOs(selectedOperation)}, 4); + return COM.S_OK; +} + +int DragLeave() { + keyOperation = -1; + + if (iDataObject is null) return COM.S_FALSE; + + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = OS.GetMessageTime(); + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragLeave, event); + refresh(); + + iDataObject.Release(); + iDataObject = null; + return COM.S_OK; +} + +int DragOver(int grfKeyState, int pt_x, int pt_y, int pdwEffect) { + if (iDataObject is null) return COM.S_FALSE; + int oldKeyOperation = keyOperation; + + DNDEvent event = new DNDEvent(); + if (!setEventData(event, iDataObject.getAddress(), grfKeyState, pt_x, pt_y, pdwEffect)) { + keyOperation = -1; + OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4); + return COM.S_FALSE; + } + + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + + if (keyOperation is oldKeyOperation) { + event.type = DND.DragOver; + event.dataType = selectedDataType; + event.detail = selectedOperation; + } else { + event.type = DND.DragOperationChanged; + event.dataType = selectedDataType; + } + notifyListeners(event.type, event); + refresh(); + if (event.detail is DND.DROP_DEFAULT) { + event.detail = (allowedOperations & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + + selectedDataType = null; + for (int i = 0; i < allowedDataTypes.length; i++) { + if (TransferData.sameType(allowedDataTypes[i], event.dataType)){ + selectedDataType = allowedDataTypes[i]; + break; + } + } + + selectedOperation = DND.DROP_NONE; + if (selectedDataType !is null && ((allowedOperations & event.detail) is event.detail)) { + selectedOperation = event.detail; + } + + OS.MoveMemory(pdwEffect, new int[] {opToOs(selectedOperation)}, 4); + return COM.S_OK; +} + +int Drop(int pDataObject, int grfKeyState, int pt_x, int pt_y, int pdwEffect) { + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = OS.GetMessageTime(); + if (dropEffect !is null) { + event.item = dropEffect.getItem(pt_x, pt_y); + } + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragLeave, event); + refresh(); + + event = new DNDEvent(); + if (!setEventData(event, pDataObject, grfKeyState, pt_x, pt_y, pdwEffect)) { + keyOperation = -1; + OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4); + return COM.S_FALSE; + } + keyOperation = -1; + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + event.dataType = selectedDataType; + event.detail = selectedOperation; + notifyListeners(DND.DropAccept,event); + refresh(); + + selectedDataType = null; + for (int i = 0; i < allowedDataTypes.length; i++) { + if (TransferData.sameType(allowedDataTypes[i], event.dataType)){ + selectedDataType = allowedDataTypes[i]; + break; + } + } + selectedOperation = DND.DROP_NONE; + if (selectedDataType !is null && (allowedOperations & event.detail) is event.detail) { + selectedOperation = event.detail; + } + + if (selectedOperation is DND.DROP_NONE){ + OS.MoveMemory(pdwEffect, new int[] {COM.DROPEFFECT_NONE}, 4); + return COM.S_OK; + } + + // Get Data in a Java format + Object object = null; + for (int i = 0; i < transferAgents.length; i++){ + Transfer transfer = transferAgents[i]; + if (transfer !is null && transfer.isSupportedType(selectedDataType)){ + object = transfer.nativeToJava(selectedDataType); + break; + } + } + if (object is null){ + selectedOperation = DND.DROP_NONE; + } + + event.detail = selectedOperation; + event.dataType = selectedDataType; + event.data = object; + OS.ImageList_DragShowNolock(false); + try { + notifyListeners(DND.Drop,event); + } finally { + OS.ImageList_DragShowNolock(true); + } + refresh(); + selectedOperation = DND.DROP_NONE; + if ((allowedOperations & event.detail) is event.detail) { + selectedOperation = event.detail; + } + //notify source of action taken + OS.MoveMemory(pdwEffect, new int[] {opToOs(selectedOperation)}, 4); + return COM.S_OK; +} + +/** + * Returns the Control which is registered for this DropTarget. This is the control over which the + * user positions the cursor to drop the data. + * + * @return the Control which is registered for this DropTarget + */ +public Control getControl () { + return control; +} + +/** + * Returns the drop effect for this DropTarget. This drop effect will be + * used during a drag and drop to display the drag under effect on the + * target widget. + * + * @return the drop effect that is registered for this DropTarget + * + * @since 3.3 + */ +public DropTargetEffect getDropTargetEffect() { + return dropEffect; +} + +int getOperationFromKeyState(int grfKeyState) { + bool ctrl = (grfKeyState & OS.MK_CONTROL) !is 0; + bool shift = (grfKeyState & OS.MK_SHIFT) !is 0; + bool alt = (grfKeyState & OS.MK_ALT) !is 0; + if (alt) { + if (ctrl || shift) return DND.DROP_DEFAULT; + return DND.DROP_LINK; + } + if (ctrl && shift) return DND.DROP_LINK; + if (ctrl)return DND.DROP_COPY; + if (shift)return DND.DROP_MOVE; + return DND.DROP_DEFAULT; +} + +/** + * Returns a list of the data types that can be transferred to this DropTarget. + * + * @return a list of the data types that can be transferred to this DropTarget + */ +public Transfer[] getTransfer() { + return transferAgents; +} + +void onDispose () { + if (control is null) return; + + COM.RevokeDragDrop(control.handle); + + if (controlListener !is null) + control.removeListener(DWT.Dispose, controlListener); + controlListener = null; + control.setData(DROPTARGETID, null); + transferAgents = null; + control = null; + + COM.CoLockObjectExternal(iDropTarget.getAddress(), false, true); + + this.Release(); + + COM.CoFreeUnusedLibraries(); +} + +int opToOs(int operation) { + int osOperation = 0; + if ((operation & DND.DROP_COPY) !is 0){ + osOperation |= COM.DROPEFFECT_COPY; + } + if ((operation & DND.DROP_LINK) !is 0) { + osOperation |= COM.DROPEFFECT_LINK; + } + if ((operation & DND.DROP_MOVE) !is 0) { + osOperation |= COM.DROPEFFECT_MOVE; + } + return osOperation; +} + +int osToOp(int osOperation){ + int operation = 0; + if ((osOperation & COM.DROPEFFECT_COPY) !is 0){ + operation |= DND.DROP_COPY; + } + if ((osOperation & COM.DROPEFFECT_LINK) !is 0) { + operation |= DND.DROP_LINK; + } + if ((osOperation & COM.DROPEFFECT_MOVE) !is 0) { + operation |= DND.DROP_MOVE; + } + return operation; +} + +/* QueryInterface([in] iid, [out] ppvObject) + * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject + * must be incremented before returning. Caller is responsible for releasing ppvObject. + */ +int QueryInterface(int riid, int ppvObject) { + + if (riid is 0 || ppvObject is 0) + return COM.E_INVALIDARG; + GUID guid = new GUID(); + COM.MoveMemory(guid, riid, GUID.sizeof); + if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDropTarget)) { + OS.MoveMemory(ppvObject, new int[] {iDropTarget.getAddress()}, 4); + AddRef(); + return COM.S_OK; + } + + OS.MoveMemory(ppvObject, new int[] {0}, 4); + return COM.E_NOINTERFACE; +} + +int Release() { + refCount--; + + if (refCount is 0) { + disposeCOMInterfaces(); + COM.CoFreeUnusedLibraries(); + } + + return refCount; +} + +void refresh() { + if (control is null || control.isDisposed()) return; + int handle = control.handle; + RECT lpRect = new RECT(); + if (OS.GetUpdateRect(handle, lpRect, false)) { + OS.ImageList_DragShowNolock(false); + OS.RedrawWindow(handle, lpRect, 0, OS.RDW_UPDATENOW | OS.RDW_INVALIDATE); + OS.ImageList_DragShowNolock(true); + } +} + +/** + * Removes the listener from the collection of listeners who will + * be notified when a drag and drop operation is in progress. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the listener is null
  • + *
+ * @exception DWTException
    + *
  • ERROR_WIDGET_DISPOSED - if the receiver has been disposed
  • + *
  • ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
  • + *
+ * + * @see DropTargetListener + * @see #addDropListener + */ +public void removeDropListener(DropTargetListener listener) { + if (listener is null) DND.error (DWT.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; +} + +bool setEventData(DNDEvent event, int pDataObject, int grfKeyState, int pt_x, int pt_y, int pdwEffect) { + if (pDataObject is 0 || pdwEffect is 0) return false; + + // get allowed operations + int style = getStyle(); + int[] operations = new int[1]; + OS.MoveMemory(operations, pdwEffect, 4); + operations[0] = osToOp(operations[0]) & style; + if (operations[0] is DND.DROP_NONE) return false; + + // get current operation + int operation = getOperationFromKeyState(grfKeyState); + keyOperation = operation; + if (operation is DND.DROP_DEFAULT) { + if ((style & DND.DROP_DEFAULT) is 0) { + operation = (operations[0] & DND.DROP_MOVE) !is 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + } else { + if ((operation & operations[0]) is 0) operation = DND.DROP_NONE; + } + + // Get allowed transfer types + TransferData[] dataTypes = new TransferData[0]; + IDataObject dataObject = new IDataObject(pDataObject); + dataObject.AddRef(); + try { + int[] address = new int[1]; + if (dataObject.EnumFormatEtc(COM.DATADIR_GET, address) !is COM.S_OK) { + return false; + } + IEnumFORMATETC enumFormatetc = new IEnumFORMATETC(address[0]); + try { + // Loop over enumerator and save any types that match what we are looking for + int rgelt = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, FORMATETC.sizeof); + try { + int[] pceltFetched = new int[1]; + enumFormatetc.Reset(); + while (enumFormatetc.Next(1, rgelt, pceltFetched) is COM.S_OK && pceltFetched[0] is 1) { + TransferData transferData = new TransferData(); + transferData.formatetc = new FORMATETC(); + COM.MoveMemory(transferData.formatetc, rgelt, FORMATETC.sizeof); + transferData.type = transferData.formatetc.cfFormat; + transferData.pIDataObject = pDataObject; + for (int i = 0; i < transferAgents.length; i++){ + Transfer transfer = transferAgents[i]; + if (transfer !is null && transfer.isSupportedType(transferData)){ + TransferData[] newDataTypes = new TransferData[dataTypes.length + 1]; + System.arraycopy(dataTypes, 0, newDataTypes, 0, dataTypes.length); + newDataTypes[dataTypes.length] = transferData; + dataTypes = newDataTypes; + break; + } + } + } + } finally { + OS.GlobalFree(rgelt); + } + } finally { + enumFormatetc.Release(); + } + } finally { + dataObject.Release(); + } + if (dataTypes.length is 0) return false; + + event.widget = this; + event.x = pt_x; + event.y = pt_y; + event.time = OS.GetMessageTime(); + event.feedback = DND.FEEDBACK_SELECT; + event.dataTypes = dataTypes; + event.dataType = dataTypes[0]; + if (dropEffect !is null) { + event.item = dropEffect.getItem(pt_x, pt_y); + } + event.operations = operations[0]; + event.detail = operation; + return true; +} + +/** + * Specifies the data types that can be transferred to this DropTarget. If data is + * being dragged that does not match one of these types, the drop target will be notified of + * the drag and drop operation but the currentDataType will be null and the operation + * will be DND.NONE. + * + * @param transferAgents a list of Transfer objects which define the types of data that can be + * dropped on this target + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if transferAgents is null
  • + *
+ */ +public void setTransfer(Transfer[] transferAgents){ + if (transferAgents is null) DND.error(DWT.ERROR_NULL_ARGUMENT); + this.transferAgents = transferAgents; +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DropTargetAdapter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DropTargetAdapter.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DropTargetAdapter; + +import dwt.dnd.DropTargetListener; +import dwt.dnd.DropTargetEvent; + +/** + * This adapter class provides default implementations for the + * methods described by the DropTargetListener interface. + *

+ * Classes that wish to deal with DropTargetEvents can + * extend this class and override only the methods which they are + * interested in. + *

+ * + * @see DropTargetListener + * @see DropTargetEvent + */ +public class DropTargetAdapter : DropTargetListener { + +/** + * This implementation of dragEnter permits the default + * operation defined in event.detailto be performed on the current data type + * defined in event.currentDataType. + * For additional information see DropTargetListener.dragEnter. + */ +public void dragEnter(DropTargetEvent event){} +/** + * This implementation of dragLeave does nothing. + * For additional information see DropTargetListener.dragOperationChanged. + */ +public void dragLeave(DropTargetEvent event){} +/** + * This implementation of dragOperationChanged permits the default + * operation defined in event.detailto be performed on the current data type + * defined in event.currentDataType. + * For additional information see DropTargetListener.dragOperationChanged. + */ +public void dragOperationChanged(DropTargetEvent event){} +/** + * This implementation of dragOver permits the default + * operation defined in event.detailto be performed on the current data type + * defined in event.currentDataType. + * For additional information see DropTargetListener.dragOver. + */ +public void dragOver(DropTargetEvent event){} +/** + * This implementation of drop does nothing. + * For additional information see DropTargetListener.drop. + */ +public void drop(DropTargetEvent event){} +/** + * This implementation of dropAccept permits the default + * operation defined in event.detailto be performed on the current data type + * defined in event.currentDataType. + * For additional information see DropTargetListener.dropAccept. + */ +public void dropAccept(DropTargetEvent event){} + +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DropTargetEffect.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DropTargetEffect.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,178 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DropTargetEffect; + + +import dwt.DWT; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Control; +import dwt.widgets.Item; +import dwt.widgets.Table; +import dwt.widgets.Tree; +import dwt.widgets.TreeItem; +import dwt.widgets.Widget; +import dwt.dnd.DropTargetAdapter; + + +/** + * This class provides a default drag under effect during a drag and drop. + * The current implementation does not provide any visual feedback. + * + *

The drop target effect has the same API as the + * DropTargetAdapter so that it can provide custom visual + * feedback when a DropTargetEvent occurs. + *

+ * + *

Classes that wish to provide their own drag under effect + * can extend the DropTargetEffect and override any applicable methods + * in DropTargetAdapter to display their own drag under effect.

+ * + *

The feedback value is either one of the FEEDBACK constants defined in + * class DND which is applicable to instances of this class, + * or it must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DND effect constants. + *

+ *

+ *

+ *
Feedback:
+ *
FEEDBACK_EXPAND, FEEDBACK_INSERT_AFTER, FEEDBACK_INSERT_BEFORE, + * FEEDBACK_NONE, FEEDBACK_SELECT, FEEDBACK_SCROLL
+ *
+ *

+ * + * @see DropTargetAdapter + * @see DropTargetEvent + * + * @since 3.3 + */ +public class DropTargetEffect : DropTargetAdapter { + Control control; + + /** + * Creates a new DropTargetEffect to handle the drag under effect on the specified + * Control. + * + * @param control the Control over which the user positions the cursor to drop the data + * + * @exception IllegalArgumentException
    + *
  • ERROR_NULL_ARGUMENT - if the control is null
  • + *
+ */ + public this(Control control) { + if (control is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); + this.control = control; + } + + /** + * Returns the Control which is registered for this DropTargetEffect. This is the control over which the + * user positions the cursor to drop the data. + * + * @return the Control which is registered for this DropTargetEffect + */ + public Control getControl() { + return control; + } + + /** + * Returns the item at the given x-y coordinate in the receiver + * or null if no such item exists. The x-y coordinate is in the + * display relative coordinates. + * + * @param x the x coordinate used to locate the item + * @param y the y coordinate used to locate the item + * @return the item at the given x-y coordinate, or null if the coordinate is not in a selectable item + */ + public Widget getItem(int x, int y) { + if ( auto table = cast(Table)control ) { + return getItem(table, x, y); + } + if ( auto tree = cast(Tree)control ) { + return getItem(tree, x, y); + } + return null; + } + + Widget getItem(Table table, int x, int y) { + Point coordinates = new Point(x, y); + coordinates = table.toControl(coordinates); + Item item = table.getItem(coordinates); + if (item is null) { + Rectangle area = table.getClientArea(); + if (area.contains(coordinates)) { + // Scan across the width of the table. + for (int x1 = area.x; x1 < area.x + area.width; x1++) { + Point pt = new Point(x1, coordinates.y); + item = table.getItem(pt); + if (item !is null) { + break; + } + } + } + } + return item; + } + + Widget getItem(Tree tree, int x, int y) { + Point coordinates = new Point(x, y); + coordinates = tree.toControl(coordinates); + Item item = tree.getItem(coordinates); + if (item is null) { + Rectangle area = tree.getClientArea(); + if (area.contains(coordinates)) { + // Scan across the width of the tree. + for (int x1 = area.x; x1 < area.x + area.width; x1++) { + Point pt = new Point(x1, coordinates.y); + item = tree.getItem(pt); + if (item !is null) { + break; + } + } + } + } + return item; + } + + TreeItem nextItem(Tree tree, TreeItem item) { + if (item is null) return null; + if (item.getExpanded()) return item.getItem(0); + TreeItem childItem = item; + TreeItem parentItem = childItem.getParentItem(); + int index = parentItem is null ? tree.indexOf(childItem) : parentItem.indexOf(childItem); + int count = parentItem is null ? tree.getItemCount() : parentItem.getItemCount(); + while (true) { + if (index + 1 < count) return parentItem is null ? tree.getItem(index + 1) : parentItem.getItem(index + 1); + if (parentItem is null) return null; + childItem = parentItem; + parentItem = childItem.getParentItem(); + index = parentItem is null ? tree.indexOf(childItem) : parentItem.indexOf(childItem); + count = parentItem is null ? tree.getItemCount() : parentItem.getItemCount(); + } + } + + TreeItem previousItem(Tree tree, TreeItem item) { + if (item is null) return null; + TreeItem childItem = item; + TreeItem parentItem = childItem.getParentItem(); + int index = parentItem is null ? tree.indexOf(childItem) : parentItem.indexOf(childItem); + if (index is 0) return parentItem; + TreeItem nextItem = parentItem is null ? tree.getItem(index-1) : parentItem.getItem(index-1); + int count = nextItem.getItemCount(); + while (count > 0 && nextItem.getExpanded()) { + nextItem = nextItem.getItem(count - 1); + count = nextItem.getItemCount(); + } + return nextItem; + } +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DropTargetEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DropTargetEvent.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,121 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DropTargetEvent; + + +import dwt.events.TypedEvent; +import dwt.widgets.Widget; +import dwt.dnd.TransferData; +import dwt.dnd.DNDEvent; +import dwt.widgets.Event; + +/** + * The DropTargetEvent contains the event information passed in the methods of the DropTargetListener. + */ +public class DropTargetEvent : TypedEvent { + /** + * The x-cordinate of the cursor relative to the Display + */ + public int x; + + /** + * The y-cordinate of the cursor relative to the Display + */ + public int y; + + /** + * The operation being performed. + * @see DND#DROP_NONE + * @see DND#DROP_MOVE + * @see DND#DROP_COPY + * @see DND#DROP_LINK + */ + public int detail; + + /** + * A bitwise OR'ing of the operations that the DragSource can support + * (e.g. DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK). + * The detail value must be a member of this list or DND.DROP_NONE. + * @see DND#DROP_NONE + * @see DND#DROP_MOVE + * @see DND#DROP_COPY + * @see DND#DROP_LINK + */ + public int operations; + + /** + * A bitwise OR'ing of the drag under effect feedback to be displayed to the user + * (e.g. DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND). + *

A value of DND.FEEDBACK_NONE indicates that no drag under effect will be displayed.

+ *

Feedback effects will only be applied if they are applicable.

+ *

The default value is DND.FEEDBACK_SELECT.

+ * @see DND#FEEDBACK_NONE + * @see DND#FEEDBACK_SELECT + * @see DND#FEEDBACK_INSERT_BEFORE + * @see DND#FEEDBACK_INSERT_AFTER + * @see DND#FEEDBACK_SCROLL + * @see DND#FEEDBACK_EXPAND + */ + public int feedback; + + /** + * If the associated control is a table or tree, this field contains the item located + * at the cursor coordinates. + */ + public Widget item; + + /** + * The type of data that will be dropped. + */ + public TransferData currentDataType; + + /** + * A list of the types of data that the DragSource is capable of providing. + * The currentDataType must be a member of this list. + */ + public TransferData[] dataTypes; + + static const long serialVersionUID = 3256727264573338678L; + +/** + * Constructs a new instance of this class based on the + * information in the given untyped event. + * + * @param e the untyped event containing the information + */ +public this(DNDEvent e) { + super(cast(Event)e); + this.data = e.data; + this.x = e.x; + this.y = e.y; + this.detail = e.detail; + this.currentDataType = e.dataType; + this.dataTypes = e.dataTypes; + this.operations = e.operations; + this.feedback = e.feedback; + this.item = e.item; +} +void updateEvent(DNDEvent e) { + e.widget = this.widget; + e.time = this.time; + e.data = this.data; + e.x = this.x; + e.y = this.y; + e.detail = this.detail; + e.dataType = this.currentDataType; + e.dataTypes = this.dataTypes; + e.operations = this.operations; + e.feedback = this.feedback; + e.item = this.item; +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/DropTargetListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/DropTargetListener.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,253 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.DropTargetListener; + +import dwt.internal.DWTEventListener; +import dwt.dnd.DropTargetEvent; + +/** + * The DropTargetListener class provides event notification to the application + * for DropTarget events. + * + *

As the user moves the cursor into, over and out of a Control that has been designated + * as a DropTarget, events indicate what operation can be performed and what data can be + * transferred if a drop where to occur at that point. + * The application can respond to these events and change the type of data that will + * be dropped by modifying event.currentDataType, or change the operation that will be performed + * by modifying the event.detail field or stop any drop from happening on the current target + * by setting the event.detail field to DND_DROP_NONE.

+ * + *

When the user causes a drop to happen by releasing the mouse over a valid drop target, + * the application has one last chance to change the data type of the drop through the + * DropAccept event. If the drop is still allowed, the DropAccept event is immediately + * followed by the Drop event. In the Drop event, the application can still change the + * operation that is performed but the data type is fixed.

+ * + * @see DropTargetEvent + * + */ +public interface DropTargetListener : DWTEventListener { + +/** + * The cursor has entered the drop target boundaries. + * + *

The following fields in the DropTargetEvent apply: + *

    + *
  • (in)widget + *
  • (in)time + *
  • (in)x + *
  • (in)y + *
  • (in)dataTypes + *
  • (in,out)currentDataType + *
  • (in)operations + *
  • (in,out)detail + *
  • (in,out)feedback + *

+ * + *

The operation value is determined by the modifier keys pressed by the user. + * If no keys are pressed the event.detail field is set to DND.DROP_DEFAULT. + * If the application does not set the event.detail to something other + * than DND.DROP_DEFAULT the operation will be set to the platform defined standard + * default.

+ * + *

The currentDataType is determined by the first transfer agent specified in + * setTransfer() that matches a data type provided by the drag source.

+ * + *

It is possible to get a DragEnter event when the drag source does not provide any matching data. + * In this case, the default operation is DND.DROP_NONE and the currentDataType is null.

+ * + *

The application can change the operation that will be performed by modifying the + * detail field but the choice must be one of the values in the operations + * field or DND.DROP_NONE.

+ * + *

The application can also change the type of data being requested by + * modifying the currentDataTypes field but the value must be one of the values + * in the dataTypes list.

+ * + * @param event the information associated with the drag enter event + * + * @see DropTargetEvent + */ +public void dragEnter(DropTargetEvent event); + +/** + * The cursor has left the drop target boundaries OR the drop has been cancelled OR the data + * is about to be dropped. + * + *

The following fields in the DropTargetEvent apply: + *

    + *
  • (in)widget + *
  • (in)time + *
  • (in)x + *
  • (in)y + *
  • (in)dataTypes + *
  • (in)currentDataType + *
  • (in)operations + *
  • (in)detail + *

+ * + * @param event the information associated with the drag leave event + * + * @see DropTargetEvent + */ +public void dragLeave(DropTargetEvent event); + +/** + * The operation being performed has changed (usually due to the user changing the selected modifier key(s) + * while dragging). + * + *

The following fields in the DropTargetEvent apply: + *

    + *
  • (in)widget + *
  • (in)time + *
  • (in)x + *
  • (in)y + *
  • (in)dataTypes + *
  • (in,out)currentDataType + *
  • (in)operations + *
  • (in,out)detail + *
  • (in,out)feedback + *

+ * + *

The operation value is determined by the modifier keys pressed by the user. + * If no keys are pressed the event.detail field is set to DND.DROP_DEFAULT. + * If the application does not set the event.detail to something other than + * DND.DROP_DEFAULT the operation will be set to the platform defined standard default.

+ * + *

The currentDataType value is determined by the value assigned to + * currentDataType in previous dragEnter and dragOver calls.

+ * + *

The application can change the operation that will be performed by modifying the + * detail field but the choice must be one of the values in the operations + * field.

+ * + *

The application can also change the type of data being requested by modifying + * the currentDataTypes field but the value must be one of the values in the + * dataTypes list.

+ * + * @param event the information associated with the drag operation changed event + * + * @see DropTargetEvent + */ +public void dragOperationChanged(DropTargetEvent event); + +/** + * The cursor is moving over the drop target. + * + *

The following fields in the DropTargetEvent apply: + *

    + *
  • (in)widget + *
  • (in)time + *
  • (in)x + *
  • (in)y + *
  • (in)dataTypes + *
  • (in,out)currentDataType + *
  • (in)operations + *
  • (in,out)detail + *
  • (in,out)feedback + *

+ * + *

The operation value is determined by the value assigned to + * currentDataType in previous dragEnter and dragOver calls.

+ * + *

The currentDataType value is determined by the value assigned to + * currentDataType in previous dragEnter and dragOver calls.

+ * + *

The application can change the operation that will be performed by modifying the + * detail field but the choice must be one of the values in the operations + * field.

+ * + *

The application can also change the type of data being requested by modifying the + * currentDataTypes field but the value must be one of the values in the + * dataTypes list.

+ * + *

NOTE: At this point the data field is null. On some platforms, it is possible + * to obtain the data being transferred before the transfer occurs but in most platforms this is + * not possible. On those platforms where the data is available, the application can access the + * data as follows:

+ * + *

+ * public void dragOver(DropTargetEvent event) {
+ *       TextTransfer textTransfer = TextTransfer.getInstance();
+ *       String data = (String)textTransfer.nativeToJava(event.currentDataType);
+ *       if (data !is null) {
+ *           System.out.println("Data to be dropped is (Text)"+data);
+ *       }
+ * };
+ * 
+ * + * @param event the information associated with the drag over event + * + * @see DropTargetEvent + */ +public void dragOver(DropTargetEvent event); + +/** + * The data is being dropped. The data field contains java format of the data being dropped. + * To determine the type of the data object, refer to the documentation for the Transfer subclass + * specified in event.currentDataType. + * + *

The following fields in DropTargetEvent apply: + *

    + *
  • (in)widget + *
  • (in)time + *
  • (in)x + *
  • (in)y + *
  • (in,out)detail + *
  • (in)currentDataType + *
  • (in)data + *

+ * + *

The application can refuse to perform the drop operation by setting the detail + * field to DND.DROP_NONE.

+ * + * @param event the information associated with the drop event + * + * @see DropTargetEvent + */ +public void drop(DropTargetEvent event); + +/** + * The drop is about to be performed. + * The drop target is given a last chance to change the nature of the drop. + * + *

The following fields in the DropTargetEvent apply: + *

    + *
  • (in)widget + *
  • (in)time + *
  • (in)x + *
  • (in)y + *
  • (in)dataTypes + *
  • (in,out)currentDataType + *
  • (in)operations + *
  • (in,out)detail + *

+ * + *

The application can veto the drop by setting the event.detail field to + * DND.DROP_NONE.

+ * + *

The application can change the operation that will be performed by modifying the + * detail field but the choice must be one of the values in the + * operations field.

+ * + *

The application can also change the type of data being requested by modifying the + * currentDataTypes field but the value must be one of the values in the < + * code>dataTypes list.

+ * + * @param event the information associated with the drop accept event + * + * @see DropTargetEvent + */ +public void dropAccept(DropTargetEvent event); + +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/FileTransfer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/FileTransfer.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,159 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.FileTransfer; + +import dwt.internal.ole.win32.COM; +import dwt.internal.win32.OS; + + +/** + * The class FileTransfer provides a platform specific mechanism + * for converting a list of files represented as a java String[] to a + * platform specific representation of the data and vice versa. + * Each String in the array contains the absolute path for a single + * file or directory. + * See Transfer for additional information. + * + *

An example of a java String[] containing a list of files is shown + * below:

+ * + *
+ *     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();
+ * 
+ */ +public class FileTransfer : ByteArrayTransfer { + + private static FileTransfer _instance = new FileTransfer(); + private static final String CF_HDROP = "CF_HDROP "; //$NON-NLS-1$ + private static final int CF_HDROPID = COM.CF_HDROP; + private static final String CF_HDROP_SEPARATOR = "\0"; //$NON-NLS-1$ + +private this() {} + +/** + * 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 javaToNative converts a list of file names + * represented by a java String[] to a platform specific representation. + * Each String in the array contains the absolute path for a single + * file or directory. For additional information see + * Transfer#javaToNative. + * + * @param object a java String[] containing the file names to be + * converted + * @param transferData an empty TransferData 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[] fileNames = (String[]) object; + StringBuffer allFiles = new StringBuffer(); + for (int i = 0; i < fileNames.length; i++) { + allFiles.append(fileNames[i]); + allFiles.append(CF_HDROP_SEPARATOR); // each name is null terminated + } + TCHAR buffer = new TCHAR(0, allFiles.toString(), true); // there is an extra null terminator at the very end + DROPFILES dropfiles = new DROPFILES(); + dropfiles.pFiles = DROPFILES.sizeof; + dropfiles.pt_x = dropfiles.pt_y = 0; + dropfiles.fNC = 0; + dropfiles.fWide = OS.IsUnicode ? 1 : 0; + // Allocate the memory because the caller (DropTarget) has not handed it in + // The caller of this method must release the data when it is done with it. + int byteCount = buffer.length() * TCHAR.sizeof; + int newPtr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, DROPFILES.sizeof + byteCount); + OS.MoveMemory(newPtr, dropfiles, DROPFILES.sizeof); + OS.MoveMemory(newPtr + DROPFILES.sizeof, buffer, byteCount); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = newPtr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; +} + +/** + * This implementation of nativeToJava converts a platform specific + * representation of a list of file names to a java String[]. + * Each String in the array contains the absolute path for a single file or directory. + * For additional information see Transfer#nativeToJava. + * + * @param transferData the platform specific representation of the data to be + * been converted + * @return a java String[] containing a list of file names if the + * conversion was successful; otherwise null + */ +public Object nativeToJava(TransferData transferData) { + if (!isSupportedType(transferData) || transferData.pIDataObject is 0) return null; + + // get file names from IDataObject + IDataObject dataObject = new IDataObject(transferData.pIDataObject); + dataObject.AddRef(); + FORMATETC formatetc = new FORMATETC(); + formatetc.cfFormat = COM.CF_HDROP; + formatetc.ptd = 0; + formatetc.dwAspect = COM.DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.tymed = COM.TYMED_HGLOBAL; + STGMEDIUM stgmedium = new STGMEDIUM(); + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = dataObject.GetData(formatetc, stgmedium); + dataObject.Release(); + if (transferData.result !is COM.S_OK) return null; + // How many files are there? + int count = OS.DragQueryFile(stgmedium.unionField, 0xFFFFFFFF, null, 0); + String[] fileNames = new String[count]; + for (int i = 0; i < count; i++) { + // How long is the name ? + int size = OS.DragQueryFile(stgmedium.unionField, i, null, 0) + 1; + TCHAR lpszFile = new TCHAR(0, size); + // Get file name and append it to string + OS.DragQueryFile(stgmedium.unionField, i, lpszFile, size); + fileNames[i] = lpszFile.toString(0, lpszFile.strlen()); + } + OS.DragFinish(stgmedium.unionField); // frees data associated with HDROP data + return fileNames; +} + +protected int[] getTypeIds(){ + return new int[] {CF_HDROPID}; +} + +protected String[] getTypeNames(){ + return new String[] {CF_HDROP}; +} +bool checkFile(Object object) { + if (object is null || !(object instanceof String[]) || ((String[])object).length is 0) return false; + String[] strings = (String[])object; + for (int i = 0; i < strings.length; i++) { + if (strings[i] is null || strings[i].length() is 0) return false; + } + return true; +} + +protected bool validate(Object object) { + return checkFile(object); +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/HTMLTransfer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/HTMLTransfer.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,210 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.HTMLTransfer; + +import dwt.internal.ole.win32.COM; +import dwt.internal.win32.OS; + +/** + * The class HTMLTransfer provides a platform specific mechanism + * for converting text in HTML format represented as a java String + * to a platform specific representation of the data and vice versa. See + * Transfer for additional information. + * + *

An example of a java String containing HTML text is shown + * below:

+ * + *
+ *     String htmlData = "

This is a paragraph of text.

"; + *
+ */ +public class HTMLTransfer : ByteArrayTransfer { + + static HTMLTransfer _instance = new HTMLTransfer(); + static final String HTML_FORMAT = "HTML Format"; //$NON-NLS-1$ + static final int HTML_FORMATID = registerType(HTML_FORMAT); + static final String NUMBER = "00000000"; //$NON-NLS-1$ + static final String HEADER = "Version:0.9\r\nStartHTML:"+NUMBER+"\r\nEndHTML:"+NUMBER+"\r\nStartFragment:"+NUMBER+"\r\nEndFragment:"+NUMBER+"\r\n"; + static final String PREFIX = ""; //$NON-NLS-1$ + static final String SUFFIX = ""; //$NON-NLS-1$ + static final String StartFragment = "StartFragment:"; //$NON-NLS-1$ + static final String EndFragment = "EndFragment:"; //$NON-NLS-1$ + +private this() {} + +/** + * 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 javaToNative converts HTML-formatted text + * represented by a java String to a platform specific representation. + * For additional information see Transfer#javaToNative. + * + * @param object a java String containing HTML text + * @param transferData an empty TransferData 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 + 1]; + string.getChars(0, count, chars, 0); + int codePage = OS.GetACP(); + int cchMultiByte = OS.WideCharToMultiByte(codePage, 0, chars, -1, null, 0, null, null); + if (cchMultiByte is 0) { + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.DV_E_STGMEDIUM; + return; + } + int startHTML = HEADER.length(); + int startFragment = startHTML + PREFIX.length(); + int endFragment = startFragment + cchMultiByte - 1; + int endHTML = endFragment + SUFFIX.length(); + + StringBuffer buffer = new StringBuffer(HEADER); + int maxLength = NUMBER.length(); + //startHTML + int start = buffer.toString().indexOf(NUMBER); + String temp = Integer.toString(startHTML); + buffer.replace(start + maxLength-temp.length(), start + maxLength, temp); + //endHTML + start = buffer.toString().indexOf(NUMBER, start); + temp = Integer.toString(endHTML); + buffer.replace(start + maxLength-temp.length(), start + maxLength, temp); + //startFragment + start = buffer.toString().indexOf(NUMBER, start); + temp = Integer.toString(startFragment); + buffer.replace(start + maxLength-temp.length(), start + maxLength, temp); + //endFragment + start = buffer.toString().indexOf(NUMBER, start); + temp = Integer.toString(endFragment); + buffer.replace(start + maxLength-temp.length(), start + maxLength, temp); + + buffer.append(PREFIX); + buffer.append(string); + buffer.append(SUFFIX); + + count = buffer.length(); + chars = new char[count + 1]; + buffer.getChars(0, count, chars, 0); + cchMultiByte = OS.WideCharToMultiByte(codePage, 0, chars, -1, null, 0, null, null); + int lpMultiByteStr = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, cchMultiByte); + OS.WideCharToMultiByte(codePage, 0, chars, -1, lpMultiByteStr, cchMultiByte, null, null); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = lpMultiByteStr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + return; +} + +/** + * This implementation of nativeToJava converts a platform specific + * representation of HTML text to a java String. + * For additional information see Transfer#nativeToJava. + * + * @param transferData the platform specific representation of the data to be + * been converted + * @return a java String containing HTML text if the + * conversion was successful; otherwise null + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.pIDataObject is 0) return null; + IDataObject data = new IDataObject(transferData.pIDataObject); + data.AddRef(); + STGMEDIUM stgmedium = new STGMEDIUM(); + FORMATETC formatetc = transferData.formatetc; + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = data.GetData(formatetc, stgmedium); + data.Release(); + if (transferData.result !is COM.S_OK) return null; + int hMem = stgmedium.unionField; + + try { + int lpMultiByteStr = OS.GlobalLock(hMem); + if (lpMultiByteStr is 0) return null; + try { + int codePage = OS.GetACP(); + int cchWideChar = OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, null, 0); + if (cchWideChar is 0) return null; + char[] lpWideCharStr = new char [cchWideChar - 1]; + OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, lpWideCharStr, lpWideCharStr.length); + String string = new String(lpWideCharStr); + int fragmentStart = 0, fragmentEnd = 0; + int start = string.indexOf(StartFragment) + StartFragment.length(); + int end = start + 1; + while (end < string.length()) { + String s = string.substring(start, end); + try { + fragmentStart = Integer.parseInt(s); + end++; + } catch (NumberFormatException e) { + break; + } + } + start = string.indexOf(EndFragment) + EndFragment.length(); + end = start + 1; + while (end < string.length()) { + String s = string.substring(start, end); + try { + fragmentEnd = Integer.parseInt(s); + end++; + } catch (NumberFormatException e) { + break; + } + } + if (fragmentEnd <= fragmentStart || fragmentEnd > lpWideCharStr.length) return null; + /* TO DO: + * FragmentStart and FragmentEnd are offsets in original byte stream, not + * the wide char version of the byte stream. + */ + String s = string.substring(fragmentStart, fragmentEnd); + /* + * Firefox includes in the fragment, so remove it. + */ + String foxStart = "\r\n"; //$NON-NLS-1$ + int prefix = s.indexOf(foxStart); + if (prefix !is -1) { + prefix += foxStart.length(); + s = s.substring(prefix); + } + return s; + } finally { + OS.GlobalUnlock(hMem); + } + } finally { + OS.GlobalFree(hMem); + } +} +protected int[] getTypeIds(){ + return new int[] {HTML_FORMATID}; +} +protected String[] getTypeNames(){ + return new String[] {HTML_FORMAT}; +} +bool checkHTML(Object object) { + return (object !is null && object instanceof String && ((String)object).length() > 0); +} +protected bool validate(Object object) { + return checkHTML(object); +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/OleEnumFORMATETC.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/OleEnumFORMATETC.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.OleEnumFORMATETC; + +import dwt.internal.ole.win32.COM; +import dwt.internal.ole.win32.OBJIDL; +import dwt.internal.ole.win32.extras; + +final class OleEnumFORMATETC { + + private _IEnumFORMATETCImpl iEnumFORMATETC; + + private int refCount; + private int index; + + private FORMATETC*[] formats; + +this() { + + createCOMInterfaces(); + +} +int AddRef() { + refCount++; + return refCount; +} +private void createCOMInterfaces() { + // register each of the interfaces that this object implements + iEnumFORMATETC = new _IEnumFORMATETCImpl( this ); +} +private void disposeCOMInterfaces() { + iEnumFORMATETC = null; +} +IEnumFORMATETC getAddress() { + return iEnumFORMATETC; +} +private FORMATETC*[] getNextItems(int numItems){ + + if (formats is null || numItems < 1) return null; + + int endIndex = index + numItems - 1; + if (endIndex > (formats.length - 1)) endIndex = formats.length - 1; + if (index > endIndex) return null; + + FORMATETC*[] items = new FORMATETC*[endIndex - index + 1]; + for (int i = 0; i < items.length; i++){ + items[i] = formats[index]; + index++; + } + + return items; +} + +package HRESULT Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) { + /* Retrieves the next celt items in the enumeration sequence. + If there are fewer than the requested number of elements left in the sequence, + it retrieves the remaining elements. + The number of elements actually retrieved is returned through pceltFetched + (unless the caller passed in NULL for that parameter). + */ + + if (rgelt is null) return COM.E_INVALIDARG; + if (pceltFetched is null && celt !is 1) return COM.E_INVALIDARG; + + FORMATETC*[] nextItems = getNextItems(celt); + if (nextItems !is null) { + for (int i = 0; i < nextItems.length; i++) { + rgelt[i] = nextItems[i]; + } + + if (pceltFetched !is 0) + *pceltFetched = nextItems.length; + + if (nextItems.length is celt) return COM.S_OK; + + } else { + if (pceltFetched !is 0) + *pceltFetched = 0; + COM.MoveMemory(rgelt, & FORMATETC.init, FORMATETC.sizeof); + + } + return COM.S_FALSE; +} +private int QueryInterface(int riid, int ppvObject) { + + if (riid is null || ppvObject is null) return COM.E_NOINTERFACE; + + if (COM.IsEqualGUID(riid, &COM.IIDIUnknown)) { + *ppvObject = cast(void*)cast(IUnknown)iUnknown; + AddRef(); + return COM.S_OK; + } + if (COM.IsEqualGUID(riid, &COM.IIDIEnumFORMATETC)) { + *ppvObject = cast(void*)cast(IEnumFORMATETC)iEnumFORMATETC; + AddRef(); + return COM.S_OK; + } + *ppvObject = null; + return COM.E_NOINTERFACE; +} +int Release() { + refCount--; + + if (refCount is 0) { + disposeCOMInterfaces(); + COM.CoFreeUnusedLibraries(); + } + + return refCount; +} +private int Reset() { + //Resets the enumeration sequence to the beginning. + index = 0; + return COM.S_OK; +} +void setFormats(FORMATETC*[] newFormats) { + formats = newFormats; + index = 0; +} +private int Skip(int celt) { + //Skips over the next specified number of elements in the enumeration sequence. + if (celt < 1 ) return COM.E_INVALIDARG; + + index += celt; + if (index > (formats.length - 1)){ + index = formats.length - 1; + return COM.S_FALSE; + } + return COM.S_OK; +} +} + +class _IEnumFORMATETCImpl : IEnumFORMATETC { + + + OleEnumFORMATETC parent; + this(OleEnumFORMATETC p) { parent = p; } +extern (Windows): + // interface of IUnknown + HRESULT QueryInterface(REFIID riid, void ** ppvObject) { return parent.QueryInterface(riid, ppvObject); } + ULONG AddRef() { return parent.AddRef(); } + ULONG Release() { return parent.Release(); } + + // interface of IEnumFORMATETC + HRESULT Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) { + return parent.Next(celt, rgelt, pceltFetched); + } + HRESULT Skip(ULONG celt) { return parent.Skip(celt); } + HRESULT Reset() { return parent.Reset(); } + HRESULT Clone(IEnumFORMATETC * ppenum) { return COM.E_NOTIMPL;} +} + + diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/RTFTransfer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/RTFTransfer.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.RTFTransfer; + +import dwt.internal.ole.win32.COM; +import dwt.internal.win32.OS; + +/** + * The class RTFTransfer provides a platform specific mechanism + * for converting text in RTF format represented as a java String + * to a platform specific representation of the data and vice versa. See + * Transfer for additional information. + * + *

An example of a java String containing RTF text is shown + * below:

+ * + *
+ *     String rtfData = "{\\rtf1{\\colortbl;\\red255\\green0\\blue0;}\\uc1\\b\\i Hello World}";
+ * 
+ */ +public class RTFTransfer : ByteArrayTransfer { + + private static RTFTransfer _instance = new RTFTransfer(); + private static final String CF_RTF = "Rich Text Format"; //$NON-NLS-1$ + private static final int CF_RTFID = registerType(CF_RTF); + +private this() {} + +/** + * 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 javaToNative converts RTF-formatted text + * represented by a java String to a platform specific representation. + * For additional information see Transfer#javaToNative. + * + * @param object a java String containing RTF text + * @param transferData an empty TransferData 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); + } + // CF_RTF is stored as a null terminated byte array + String string = (String)object; + int count = string.length(); + char[] chars = new char[count + 1]; + string.getChars(0, count, chars, 0); + int codePage = OS.GetACP(); + int cchMultiByte = OS.WideCharToMultiByte(codePage, 0, chars, -1, null, 0, null, null); + if (cchMultiByte is 0) { + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.DV_E_STGMEDIUM; + return; + } + int lpMultiByteStr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, cchMultiByte); + OS.WideCharToMultiByte(codePage, 0, chars, -1, lpMultiByteStr, cchMultiByte, null, null); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = lpMultiByteStr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + return; +} + +/** + * This implementation of nativeToJava converts a platform specific + * representation of RTF text to a java String. + * For additional information see Transfer#nativeToJava. + * + * @param transferData the platform specific representation of the data to be + * been converted + * @return a java String containing RTF text if the + * conversion was successful; otherwise null + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.pIDataObject is 0) return null; + IDataObject data = new IDataObject(transferData.pIDataObject); + data.AddRef(); + STGMEDIUM stgmedium = new STGMEDIUM(); + FORMATETC formatetc = transferData.formatetc; + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = data.GetData(formatetc, stgmedium); + data.Release(); + if (transferData.result !is COM.S_OK) return null; + int hMem = stgmedium.unionField; + try { + int lpMultiByteStr = OS.GlobalLock(hMem); + if (lpMultiByteStr is 0) return null; + try { + int codePage = OS.GetACP(); + int cchWideChar = OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, null, 0); + if (cchWideChar is 0) return null; + char[] lpWideCharStr = new char [cchWideChar - 1]; + OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, lpWideCharStr, lpWideCharStr.length); + return new String(lpWideCharStr); + } finally { + OS.GlobalUnlock(hMem); + } + } finally { + OS.GlobalFree(hMem); + } +} + +protected int[] getTypeIds(){ + return new int[] {CF_RTFID}; +} + +protected String[] getTypeNames(){ + return new String[] {CF_RTF}; +} + +bool checkRTF(Object object) { + return (object !is null && object instanceof String && ((String)object).length() > 0); +} + +protected bool validate(Object object) { + return checkRTF(object); +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/TableDragSourceEffect.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/TableDragSourceEffect.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,121 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.TableDragSourceEffect; + +import dwt.DWT; +import dwt.graphics.Image; +import dwt.graphics.Rectangle; +import dwt.internal.win32.OS; +import dwt.widgets.Display; +import dwt.widgets.Table; +import dwt.widgets.TableItem; + +/** + * This class provides default implementations to display a source image + * when a drag is initiated from a Table. + * + *

Classes that wish to provide their own source image for a Table can + * extend the TableDragSourceEffect class, override the + * TableDragSourceEffect.dragStart method and set the field + * DragSourceEvent.image with their own image.

+ * + * Subclasses that override any methods of this class must call the corresponding + * super method to get the default drag source effect implementation. + * + * @see DragSourceEffect + * @see DragSourceEvent + * + * @since 3.3 + */ +public class TableDragSourceEffect : DragSourceEffect { + Image dragSourceImage = null; + + /** + * Creates a new TableDragSourceEffect to handle drag effect + * from the specified Table. + * + * @param table the Table that the user clicks on to initiate the drag + */ + public this(Table table) { + super(table); + } + + /** + * This implementation of dragFinished disposes the image + * that was created in TableDragSourceEffect.dragStart. + * + * Subclasses that override this method should call super.dragFinished(event) + * to dispose the image in the default implementation. + * + * @param event the information associated with the drag finished event + */ + public void dragFinished(DragSourceEvent event) { + if (dragSourceImage !is null) dragSourceImage.dispose(); + dragSourceImage = null; + } + + /** + * This implementation of dragStart will create a default + * image that will be used during the drag. The image should be disposed + * when the drag is completed in the TableDragSourceEffect.dragFinished + * method. + * + * Subclasses that override this method should call super.dragStart(event) + * to use the image from the default implementation. + * + * @param event the information associated with the drag start event + */ + public void dragStart(DragSourceEvent event) { + event.image = getDragSourceImage(event); + } + + Image getDragSourceImage(DragSourceEvent event) { + if (dragSourceImage !is null) dragSourceImage.dispose(); + dragSourceImage = null; + Table table = (Table) control; + TableItem[] selection = table.getSelection(); + if (selection.length is 0) return null; + int tableImageList = OS.SendMessage (table.handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0); + if (tableImageList !is 0) { + int count = Math.min(selection.length, 10); + Rectangle bounds = selection[0].getBounds(0); + for (int i = 1; i < count; i++) { + bounds = bounds.union(selection[i].getBounds(0)); + } + int hDC = OS.GetDC(0); + int hDC1 = OS.CreateCompatibleDC(hDC); + int bitmap = OS.CreateCompatibleBitmap(hDC, bounds.width, bounds.height); + int hOldBitmap = OS.SelectObject(hDC1, bitmap); + RECT rect = new RECT(); + rect.right = bounds.width; + rect.bottom = bounds.height; + int hBrush = OS.GetStockObject(OS.WHITE_BRUSH); + OS.FillRect(hDC1, rect, hBrush); + for (int i = 0; i < count; i++) { + TableItem selected = selection[i]; + Rectangle cell = selected.getBounds(0); + POINT pt = new POINT(); + int imageList = OS.SendMessage (table.handle, OS.LVM_CREATEDRAGIMAGE, table.indexOf(selected), pt); + OS.ImageList_Draw(imageList, 0, hDC1, cell.x - bounds.x, cell.y - bounds.y, OS.ILD_SELECTED); + OS.ImageList_Destroy(imageList); + } + OS.SelectObject(hDC1, hOldBitmap); + OS.DeleteDC (hDC1); + OS.ReleaseDC (0, hDC); + Display display = table.getDisplay(); + dragSourceImage = Image.win32_new(display, DWT.BITMAP, bitmap); + return dragSourceImage; + } + return null; + } +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/TableDropTargetEffect.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/TableDropTargetEffect.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,206 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.TableDropTargetEffect; + +import dwt.graphics.Point; +import dwt.internal.win32.OS; +import dwt.widgets.Table; +import dwt.widgets.TableItem; + +/** + * This class provides a default drag under effect (eg. select, insert and scroll) + * when a drag occurs over a Table. + * + *

Classes that wish to provide their own drag under effect for a Table + * can extend the TableDropTargetEffect and override any applicable methods + * in TableDropTargetEffect to display their own drag under effect.

+ * + * Subclasses that override any methods of this class must call the corresponding + * super method to get the default drag under effect implementation. + * + *

The feedback value is either one of the FEEDBACK constants defined in + * class DND which is applicable to instances of this class, + * or it must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DND effect constants. + *

+ *

+ *

+ *
Feedback:
+ *
FEEDBACK_SELECT, FEEDBACK_SCROLL
+ *
+ *

+ * + * @see DropTargetAdapter + * @see DropTargetEvent + * + * @since 3.3 + */ +public class TableDropTargetEffect : DropTargetEffect { + static final int SCROLL_HYSTERESIS = 200; // milli seconds + + int scrollIndex = -1; + long scrollBeginTime; + TableItem dropHighlight; + + /** + * Creates a new TableDropTargetEffect to handle the drag under effect on the specified + * Table. + * + * @param table the Table over which the user positions the cursor to drop the data + */ + public this(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) !is 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER & ~DND.FEEDBACK_INSERT_BEFORE; + if ((effect & DND.FEEDBACK_INSERT_BEFORE) !is 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER; + return effect; + } + + /** + * This implementation of dragEnter provides a default drag under effect + * for the feedback specified in event.feedback. + * + * For additional information see DropTargetAdapter.dragEnter. + * + * Subclasses that override this method should call super.dragEnter(event) + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag enter event + * + * @see DropTargetAdapter + * @see DropTargetEvent + */ + public void dragEnter(DropTargetEvent event) { + scrollBeginTime = 0; + scrollIndex = -1; + dropHighlight = null; + } + + /** + * This implementation of dragLeave provides a default drag under effect + * for the feedback specified in event.feedback. + * + * For additional information see DropTargetAdapter.dragLeave. + * + * Subclasses that override this method should call super.dragLeave(event) + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag leave event + * + * @see DropTargetAdapter + * @see DropTargetEvent + */ + public void dragLeave(DropTargetEvent event) { + Table table = (Table) control; + int handle = table.handle; + if (dropHighlight !is null) { + LVITEM lvItem = new LVITEM (); + lvItem.stateMask = OS.LVIS_DROPHILITED; + OS.SendMessage(handle, OS.LVM_SETITEMSTATE, -1, lvItem); + dropHighlight = null; + } + scrollBeginTime = 0; + scrollIndex = -1; + } + + /** + * This implementation of dragOver provides a default drag under effect + * for the feedback specified in event.feedback. The class description + * lists the FEEDBACK constants that are applicable to the class. + * + * For additional information see DropTargetAdapter.dragOver. + * + * Subclasses that override this method should call super.dragOver(event) + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag over event + * + * @see DropTargetAdapter + * @see DropTargetEvent + * @see DND#FEEDBACK_SELECT + * @see DND#FEEDBACK_SCROLL + */ + public void dragOver(DropTargetEvent event) { + Table table = (Table) getControl(); + int effect = checkEffect(event.feedback); + int handle = table.handle; + Point coordinates = new Point(event.x, event.y); + coordinates = table.toControl(coordinates); + LVHITTESTINFO pinfo = new LVHITTESTINFO(); + pinfo.x = coordinates.x; + pinfo.y = coordinates.y; + OS.SendMessage(handle, OS.LVM_HITTEST, 0, pinfo); + if ((effect & DND.FEEDBACK_SCROLL) is 0) { + scrollBeginTime = 0; + scrollIndex = -1; + } else { + if (pinfo.iItem !is -1 && scrollIndex is pinfo.iItem && scrollBeginTime !is 0) { + if (System.currentTimeMillis() >= scrollBeginTime) { + int top = Math.max (0, OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)); + int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0); + int index = (scrollIndex - 1 < top) ? Math.max(0, scrollIndex - 1) : Math.min(count - 1, scrollIndex + 1); + bool scroll = true; + if (pinfo.iItem is top) { + scroll = pinfo.iItem !is index; + } else { + RECT itemRect = new RECT (); + itemRect.left = OS.LVIR_BOUNDS; + if (OS.SendMessage (handle, OS.LVM_GETITEMRECT, pinfo.iItem, itemRect) !is 0) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + POINT pt = new POINT (); + pt.x = itemRect.left; + pt.y = itemRect.top; + if (OS.PtInRect (rect, pt)) { + pt.y = itemRect.bottom; + if (OS.PtInRect (rect, pt)) scroll = false; + } + } + } + if (scroll) { + OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 0); + table.redraw(); + } + scrollBeginTime = 0; + scrollIndex = -1; + } + } else { + scrollBeginTime = System.currentTimeMillis() + SCROLL_HYSTERESIS; + scrollIndex = pinfo.iItem; + } + } + + if (pinfo.iItem !is -1 && (effect & DND.FEEDBACK_SELECT) !is 0) { + TableItem item = table.getItem(pinfo.iItem); + if (dropHighlight !is item) { + LVITEM lvItem = new LVITEM(); + lvItem.stateMask = OS.LVIS_DROPHILITED; + OS.SendMessage(handle, OS.LVM_SETITEMSTATE, -1, lvItem); + lvItem.state = OS.LVIS_DROPHILITED; + OS.SendMessage(handle, OS.LVM_SETITEMSTATE, pinfo.iItem, lvItem); + dropHighlight = item; + } + } else { + if (dropHighlight !is null) { + LVITEM lvItem = new LVITEM (); + lvItem.stateMask = OS.LVIS_DROPHILITED; + OS.SendMessage(handle, OS.LVM_SETITEMSTATE, -1, lvItem); + dropHighlight = null; + } + } + } +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/TextTransfer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/TextTransfer.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,186 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.TextTransfer; + +import dwt.internal.ole.win32.COM; +import dwt.internal.win32.OS; + +/** + * The class TextTransfer provides a platform specific mechanism + * for converting plain text represented as a java String + * to a platform specific representation of the data and vice versa. + * + *

An example of a java String containing plain text is shown + * below:

+ * + *
+ *     String textData = "Hello World";
+ * 
+ * + * @see Transfer + */ +public class TextTransfer : ByteArrayTransfer { + + private static TextTransfer _instance = new TextTransfer(); + private static final String CF_UNICODETEXT = "CF_UNICODETEXT"; //$NON-NLS-1$ + private static final String CF_TEXT = "CF_TEXT"; //$NON-NLS-1$ + private static final int CF_UNICODETEXTID = COM.CF_UNICODETEXT; + private static final int CF_TEXTID = COM.CF_TEXT; + +private this() {} + +/** + * 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 javaToNative converts plain text + * represented by a java String to a platform specific representation. + * + * @param object a java String containing text + * @param transferData an empty TransferData 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); + } + transferData.result = COM.E_FAIL; + String string = (String)object; + switch (transferData.type) { + case COM.CF_UNICODETEXT: { + int charCount = string.length (); + char[] chars = new char[charCount+1]; + string.getChars (0, charCount, chars, 0); + int byteCount = chars.length * 2; + int newPtr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, byteCount); + OS.MoveMemory(newPtr, chars, byteCount); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = newPtr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + break; + } + case COM.CF_TEXT: { + int count = string.length(); + char[] chars = new char[count + 1]; + string.getChars(0, count, chars, 0); + int codePage = OS.GetACP(); + int cchMultiByte = OS.WideCharToMultiByte(codePage, 0, chars, -1, null, 0, null, null); + if (cchMultiByte is 0) { + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.DV_E_STGMEDIUM; + return; + } + int lpMultiByteStr = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, cchMultiByte); + OS.WideCharToMultiByte(codePage, 0, chars, -1, lpMultiByteStr, cchMultiByte, null, null); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = lpMultiByteStr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + break; + } + } + return; +} + +/** + * This implementation of nativeToJava converts a platform specific + * representation of plain text to a java String. + * + * @param transferData the platform specific representation of the data to be converted + * @return a java String containing text if the conversion was successful; otherwise null + * + * @see Transfer#nativeToJava + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.pIDataObject is 0) return null; + + IDataObject data = new IDataObject(transferData.pIDataObject); + data.AddRef(); + FORMATETC formatetc = transferData.formatetc; + STGMEDIUM stgmedium = new STGMEDIUM(); + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = data.GetData(formatetc, stgmedium); + data.Release(); + if (transferData.result !is COM.S_OK) return null; + int hMem = stgmedium.unionField; + try { + switch (transferData.type) { + case CF_UNICODETEXTID: { + /* Ensure byteCount is a multiple of 2 bytes */ + int size = OS.GlobalSize(hMem) / 2 * 2; + if (size is 0) return null; + char[] chars = new char[size/2]; + int ptr = OS.GlobalLock(hMem); + if (ptr is 0) return null; + try { + OS.MoveMemory(chars, ptr, size); + int length = chars.length; + for (int i=0; i 0); +} + +protected bool validate(Object object) { + return checkText(object); +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/Transfer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/Transfer.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,156 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.Transfer; + + +import dwt.internal.win32.OS; + +import dwt.dnd.TransferData; + +/** + * Transfer 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. + * + *

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.

+ * + * @see ByteArrayTransfer + */ +public abstract class Transfer { + +/** + * Returns a list of the platform specific data types that can be converted using + * this transfer agent. + * + *

Only the data type fields of the TransferData objects are filled + * in.

+ * + * @return a list of the data types that can be converted using this transfer agent + */ +abstract public TransferData[] getSupportedTypes(); + +/** + * Returns true if the TransferData data type can be converted + * using this transfer agent, or false otherwise (including if transferData is + * null). + * + * @param transferData a platform specific description of a data type; only the data + * type fields of the TransferData object need to be filled in + * + * @return true if the transferData data type can be converted using this transfer + * agent + */ +abstract public bool 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 char[][] getTypeNames(); + +/** + * Converts a java representation of data to a platform specific representation of + * the data. + * + *

On a successful conversion, the transferData.result field will be set as follows: + *

    + *
  • Windows: COM.S_OK + *
  • Motif: 1 + *
  • GTK: 1 + *
  • Photon: 1 + *

+ * + *

If this transfer agent is unable to perform the conversion, the transferData.result + * field will be set to a failure value as follows: + *

    + *
  • Windows: COM.DV_E_TYMED or COM.E_FAIL + *
  • Motif: 0 + *
  • GTK: 0 + *
  • Photon: 0 + *

+ * + * @param object a java representation of the data to be converted; the type of + * Object that is passed in is dependent on the Transfer subclass. + * + * @param transferData an empty TransferData object; this object will be + * filled in on return with the platform specific representation of the data + * + * @exception dwt.DWTException
    + *
  • ERROR_INVALID_DATA - if object does not contain data in a valid format or is null
  • + *
+ */ +abstract public 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 null then + * null is returned. The type of Object that is returned is + * dependent on the Transfer subclass. + */ +abstract public Object nativeToJava(TransferData transferData); + +/** + * Registers a name for a data type and returns the associated unique identifier. + * + *

You may register the same type more than once, the same unique identifier + * will be returned if the type has been previously registered.

+ * + *

Note: On windows, do not 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

+ * + * @param formatName the name of a data type + * + * @return the unique identifier associated with this data type + */ +public static int registerType(char[] formatName) { + // Look name up in the registry + // If name is not in registry, add it and return assigned value. + // If name already exists in registry, return its assigned value + TCHAR chFormatName = new TCHAR(0, formatName, true); + return OS.RegisterClipboardFormat(chFormatName); +} + +/** + * Test that the object is of the correct format for this Transfer class. + * + * @param object a java representation of the data to be converted + * + * @return true if object is of the correct form for this transfer type + * + * @since 3.1 + */ +public bool validate(Object object) { + return true; +} +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/TransferData.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/TransferData.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.TransferData; + +import dwt.internal.ole.win32.COM; +import dwt.internal.ole.win32.OBJIDL; + +/** + * The TransferData class is a platform specific data structure for + * describing the type and the contents of data being converted by a transfer agent. + * + *

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

+ * + *

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.

+ */ +public class TransferData { + /** + * The type is a unique identifier of a system format or user defined format. + * (Warning: This field is platform dependent) + *

+ * IMPORTANT: This field is not part of the DWT + * public API. It is marked public only so that it can be shared + * within the packages provided by DWT. It is not available on all + * platforms and should never be accessed from application code. + *

+ */ + public int type; + + /** + * The formatetc structure is a generalized data transfer format, enhanced to + * encompass a target device, the aspect, or view of the data, and + * a storage medium. + * (Warning: This field is platform dependent) + *

+ * IMPORTANT: This field is not part of the DWT + * public API. It is marked public only so that it can be shared + * within the packages provided by DWT. It is not available on all + * platforms and should never be accessed from application code. + *

+ */ + public FORMATETC* formatetc; + + /** + * The stgmedium structure is a generalized global memory handle used for + * data transfer operations. + * (Warning: This field is platform dependent) + *

+ * IMPORTANT: This field is not part of the DWT + * public API. It is marked public only so that it can be shared + * within the packages provided by DWT. It is not available on all + * platforms and should never be accessed from application code. + *

+ */ + public STGMEDIUM* stgmedium; + + /** + * The result field contains the result of converting a + * java data type into a platform specific value. + * (Warning: This field is platform dependent) + *

+ * IMPORTANT: This field is not part of the DWT + * public API. It is marked public only so that it can be shared + * within the packages provided by DWT. It is not available on all + * platforms and should never be accessed from application code. + *

+ *

The value of result is 1 if the conversion was successful. + * The value of result is 0 if the conversion failed.

+ */ + public int result = COM.E_FAIL; + + /** + * The pIDataObject is the address of an IDataObject OLE Interface which + * provides access to the data associated with the transfer. + * (Warning: This field is platform dependent) + *

+ * IMPORTANT: This field is not part of the DWT + * public API. It is marked public only so that it can be shared + * within the packages provided by DWT. It is not available on all + * platforms and should never be accessed from application code. + *

+ */ + public IDataObject pIDataObject; + + static bool sameType(TransferData data1, TransferData data2) { + if (data1 is data2) return true; + if (data1 is null || data2 is null) return false; + return (data1.type is data2.type && + data1.formatetc.cfFormat is data2.formatetc.cfFormat && + data1.formatetc.dwAspect is data2.formatetc.dwAspect && + data1.formatetc.tymed is data2.formatetc.tymed); + } + +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/TreeDragSourceEffect.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/TreeDragSourceEffect.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,119 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.TreeDragSourceEffect; + +import dwt.DWT; +import dwt.graphics.Image; +import dwt.graphics.Rectangle; +import dwt.internal.win32.OS; +import dwt.widgets.Display; +import dwt.widgets.Tree; +import dwt.widgets.TreeItem; + +/** + * This class provides default implementations to display a source image + * when a drag is initiated from a Tree. + * + *

Classes that wish to provide their own source image for a Tree can + * extend TreeDragSourceEffect class and override the TreeDragSourceEffect.dragStart + * method and set the field DragSourceEvent.image with their own image.

+ * + * Subclasses that override any methods of this class must call the corresponding + * super method to get the default drag under effect implementation. + * + * @see DragSourceEffect + * @see DragSourceEvent + * + * @since 3.3 + */ +public class TreeDragSourceEffect : DragSourceEffect { + Image dragSourceImage = null; + + /** + * Creates a new TreeDragSourceEffect to handle drag effect + * from the specified Tree. + * + * @param tree the Tree that the user clicks on to initiate the drag + */ + public this(Tree tree) { + super(tree); + } + + /** + * This implementation of dragFinished disposes the image + * that was created in TreeDragSourceEffect.dragStart. + * + * Subclasses that override this method should call super.dragFinished(event) + * to dispose the image in the default implementation. + * + * @param event the information associated with the drag finished event + */ + public void dragFinished(DragSourceEvent event) { + if (dragSourceImage !is null) dragSourceImage.dispose(); + dragSourceImage = null; + } + + /** + * This implementation of dragStart will create a default + * image that will be used during the drag. The image should be disposed + * when the drag is completed in the TreeDragSourceEffect.dragFinished + * method. + * + * Subclasses that override this method should call super.dragStart(event) + * to use the image from the default implementation. + * + * @param event the information associated with the drag start event + */ + public void dragStart(DragSourceEvent event) { + event.image = getDragSourceImage(event); + } + + Image getDragSourceImage(DragSourceEvent event) { + if (dragSourceImage !is null) dragSourceImage.dispose(); + dragSourceImage = null; + Tree tree = (Tree) control; + TreeItem[] selection = tree.getSelection(); + if (selection.length is 0) return null; + int treeImageList = OS.SendMessage (tree.handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0); + if (treeImageList !is 0) { + int count = Math.min(selection.length, 10); + Rectangle bounds = selection[0].getBounds(0); + for (int i = 1; i < count; i++) { + bounds = bounds.union(selection[i].getBounds(0)); + } + int hDC = OS.GetDC(0); + int hDC1 = OS.CreateCompatibleDC(hDC); + int bitmap = OS.CreateCompatibleBitmap(hDC, bounds.width, bounds.height); + int hOldBitmap = OS.SelectObject(hDC1, bitmap); + RECT rect = new RECT(); + rect.right = bounds.width; + rect.bottom = bounds.height; + int hBrush = OS.GetStockObject(OS.WHITE_BRUSH); + OS.FillRect(hDC1, rect, hBrush); + for (int i = 0; i < count; i++) { + TreeItem selected = selection[i]; + Rectangle cell = selected.getBounds(0); + int imageList = OS.SendMessage(tree.handle, OS.TVM_CREATEDRAGIMAGE, 0, selected.handle); + OS.ImageList_Draw(imageList, 0, hDC1, cell.x - bounds.x, cell.y - bounds.y, OS.ILD_SELECTED); + OS.ImageList_Destroy(imageList); + } + OS.SelectObject(hDC1, hOldBitmap); + OS.DeleteDC (hDC1); + OS.ReleaseDC (0, hDC); + Display display = tree.getDisplay(); + dragSourceImage = Image.win32_new(display, DWT.BITMAP, bitmap); + return dragSourceImage; + } + return null; + } +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/TreeDropTargetEffect.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/TreeDropTargetEffect.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,280 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.TreeDropTargetEffect; + +import dwt.graphics.Point; +import dwt.internal.win32.OS; +import dwt.widgets.Tree; +import dwt.widgets.TreeItem; + +/** + * This class provides a default drag under effect (eg. select, insert, scroll and expand) + * when a drag occurs over a Tree. + * + *

Classes that wish to provide their own drag under effect for a Tree + * can extend the TreeDropTargetEffect class and override any applicable methods + * in TreeDropTargetEffect to display their own drag under effect.

+ * + * Subclasses that override any methods of this class must call the corresponding + * super method to get the default drag under effect implementation. + * + *

The feedback value is either one of the FEEDBACK constants defined in + * class DND which is applicable to instances of this class, + * or it must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those DND effect constants. + *

+ *

+ *

+ *
Feedback:
+ *
FEEDBACK_SELECT, FEEDBACK_INSERT_BEFORE, FEEDBACK_INSERT_AFTER, FEEDBACK_EXPAND, FEEDBACK_SCROLL
+ *
+ *

+ * Note: Only one of the styles FEEDBACK_SELECT, FEEDBACK_INSERT_BEFORE or + * FEEDBACK_INSERT_AFTER may be specified. + *

+ * + * @see DropTargetAdapter + * @see DropTargetEvent + * + * @since 3.3 + */ +public class TreeDropTargetEffect : DropTargetEffect { + static final int SCROLL_HYSTERESIS = 200; // milli seconds + static final int EXPAND_HYSTERESIS = 1000; // milli seconds + + int dropIndex; + int scrollIndex; + long scrollBeginTime; + int expandIndex; + long expandBeginTime; + TreeItem insertItem; + bool insertBefore; + + /** + * Creates a new TreeDropTargetEffect to handle the drag under effect on the specified + * Tree. + * + * @param tree the Tree over which the user positions the cursor to drop the data + */ + public this(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) !is 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER & ~DND.FEEDBACK_INSERT_BEFORE; + if ((effect & DND.FEEDBACK_INSERT_BEFORE) !is 0) effect = effect & ~DND.FEEDBACK_INSERT_AFTER; + return effect; + } + + /** + * This implementation of dragEnter provides a default drag under effect + * for the feedback specified in event.feedback. + * + * For additional information see DropTargetAdapter.dragEnter. + * + * Subclasses that override this method should call super.dragEnter(event) + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag enter event + * + * @see DropTargetAdapter + * @see DropTargetEvent + */ + public void dragEnter(DropTargetEvent event) { + dropIndex = -1; + insertItem = null; + expandBeginTime = 0; + expandIndex = -1; + scrollBeginTime = 0; + scrollIndex = -1; + } + + /** + * This implementation of dragLeave provides a default drag under effect + * for the feedback specified in event.feedback. + * + * For additional information see DropTargetAdapter.dragLeave. + * + * Subclasses that override this method should call super.dragLeave(event) + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag leave event + * + * @see DropTargetAdapter + * @see DropTargetEvent + */ + public void dragLeave(DropTargetEvent event) { + Tree tree = (Tree) control; + int handle = tree.handle; + if (dropIndex !is -1) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = dropIndex; + tvItem.mask = OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_DROPHILITED; + tvItem.state = 0; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + dropIndex = -1; + } + if (insertItem !is null) { + tree.setInsertMark(null, false); + insertItem = null; + } + expandBeginTime = 0; + expandIndex = -1; + scrollBeginTime = 0; + scrollIndex = -1; + } + + /** + * This implementation of dragOver provides a default drag under effect + * for the feedback specified in event.feedback. + * + * For additional information see DropTargetAdapter.dragOver. + * + * Subclasses that override this method should call super.dragOver(event) + * to get the default drag under effect implementation. + * + * @param event the information associated with the drag over event + * + * @see DropTargetAdapter + * @see DropTargetEvent + * @see DND#FEEDBACK_SELECT + * @see DND#FEEDBACK_INSERT_BEFORE + * @see DND#FEEDBACK_INSERT_AFTER + * @see DND#FEEDBACK_SCROLL + */ + public void dragOver(DropTargetEvent event) { + Tree tree = (Tree) getControl(); + int effect = checkEffect(event.feedback); + int handle = tree.handle; + Point coordinates = new Point(event.x, event.y); + coordinates = tree.toControl(coordinates); + TVHITTESTINFO lpht = new TVHITTESTINFO (); + lpht.x = coordinates.x; + lpht.y = coordinates.y; + OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); + int hItem = lpht.hItem; + if ((effect & DND.FEEDBACK_SCROLL) is 0) { + scrollBeginTime = 0; + scrollIndex = -1; + } else { + if (hItem !is -1 && scrollIndex is hItem && scrollBeginTime !is 0) { + if (System.currentTimeMillis() >= scrollBeginTime) { + int topItem = OS.SendMessage(handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); + int nextItem = OS.SendMessage(handle, OS.TVM_GETNEXTITEM, hItem is topItem ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE, hItem); + bool scroll = true; + if (hItem is topItem) { + scroll = nextItem !is 0; + } else { + RECT itemRect = new RECT (); + itemRect.left = nextItem; + if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, itemRect) !is 0) { + RECT rect = new RECT (); + OS.GetClientRect (handle, rect); + POINT pt = new POINT (); + pt.x = itemRect.left; + pt.y = itemRect.top; + if (OS.PtInRect (rect, pt)) { + pt.y = itemRect.bottom; + if (OS.PtInRect (rect, pt)) scroll = false; + } + } + } + if (scroll) { + OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, nextItem); + tree.redraw(); + } + scrollBeginTime = 0; + scrollIndex = -1; + } + } else { + scrollBeginTime = System.currentTimeMillis() + SCROLL_HYSTERESIS; + scrollIndex = hItem; + } + } + if ((effect & DND.FEEDBACK_EXPAND) is 0) { + expandBeginTime = 0; + expandIndex = -1; + } else { + if (hItem !is -1 && expandIndex is hItem && expandBeginTime !is 0) { + if (System.currentTimeMillis() >= expandBeginTime) { + if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem) !is 0) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = hItem; + tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; + OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); + if ((tvItem.state & OS.TVIS_EXPANDED) is 0) { + OS.SendMessage (handle, OS.TVM_EXPAND, OS.TVE_EXPAND, hItem); + tree.redraw(); + } + } + expandBeginTime = 0; + expandIndex = -1; + } + } else { + expandBeginTime = System.currentTimeMillis() + EXPAND_HYSTERESIS; + expandIndex = hItem; + } + } + if (dropIndex !is -1 && (dropIndex !is hItem || (effect & DND.FEEDBACK_SELECT) is 0)) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = dropIndex; + tvItem.mask = OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_DROPHILITED; + tvItem.state = 0; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + dropIndex = -1; + } + if (hItem !is -1 && hItem !is dropIndex && (effect & DND.FEEDBACK_SELECT) !is 0) { + TVITEM tvItem = new TVITEM (); + tvItem.hItem = hItem; + tvItem.mask = OS.TVIF_STATE; + tvItem.stateMask = OS.TVIS_DROPHILITED; + tvItem.state = OS.TVIS_DROPHILITED; + OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); + dropIndex = hItem; + } + if ((effect & DND.FEEDBACK_INSERT_BEFORE) !is 0 || (effect & DND.FEEDBACK_INSERT_AFTER) !is 0) { + bool before = (effect & DND.FEEDBACK_INSERT_BEFORE) !is 0; + /* + * Bug in Windows. When TVM_SETINSERTMARK is used to set + * an insert mark for a tree and an item is expanded or + * collapsed near the insert mark, the tree does not redraw + * the insert mark properly. The fix is to hide and show + * the insert mark whenever an item is expanded or collapsed. + * Since the insert mark can not be queried from the tree, + * use the Tree API rather than calling the OS directly. + */ + TreeItem item = (TreeItem)tree.getDisplay().findWidget(tree.handle, hItem); + if (item !is null) { + if (item !is insertItem || before !is insertBefore) { + tree.setInsertMark(item, before); + } + insertItem = item; + insertBefore = before; + } else { + if (insertItem !is null) { + tree.setInsertMark(null, false); + } + insertItem = null; + } + } else { + if (insertItem !is null) { + tree.setInsertMark(null, false); + } + insertItem = null; + } + } +} diff -r fa7d7d66b9ed -r 242e33c0e383 dwt/dnd/URLTransfer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/dnd/URLTransfer.d Wed Feb 13 04:51:22 2008 +0100 @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 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 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwt.dnd.URLTransfer; + +import java.net.URL; + +import dwt.internal.ole.win32.COM; +import dwt.internal.win32.OS; + +/** + * The class URLTransfer provides a platform specific mechanism + * for converting text in URL format represented as a java String[] + * to a platform specific representation of the data and vice versa. See + * Transfer for additional information. The first string in the + * array is mandatory and must contain the fully specified url. The second + * string in the array is optional and if present contains the title for the + * page. + * + *

An example of a java String[] containing a URL is shown + * below:

+ * + *
+ *     String[] urlData = new String[] {"http://www.eclipse.org", "Eclipse.org Main Page"};
+ * 
+ */ +/*public*/ class URLTransfer : ByteArrayTransfer { + + static URLTransfer _instance = new URLTransfer(); + static final String CFSTR_INETURL = "UniformResourceLocator"; //$NON-NLS-1$ + static final int CFSTR_INETURLID = registerType(CFSTR_INETURL); + +private this() {} + +/** + * 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 javaToNative converts a URL and optionally a title + * represented by a java String[] to a platform specific representation. + * For additional information see Transfer#javaToNative. + * + * @param object a java String[] containing a URL and optionally, a title + * @param transferData an empty TransferData 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 = COM.E_FAIL; + // URL is stored as a null terminated byte array + String url = ((String[])object)[0]; + int count = url.length(); + char[] chars = new char[count + 1]; + url.getChars(0, count, chars, 0); + int codePage = OS.GetACP(); + int cchMultiByte = OS.WideCharToMultiByte(codePage, 0, chars, -1, null, 0, null, null); + if (cchMultiByte is 0) { + transferData.stgmedium = new STGMEDIUM(); + transferData.result = COM.DV_E_STGMEDIUM; + return; + } + int lpMultiByteStr = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, cchMultiByte); + OS.WideCharToMultiByte(codePage, 0, chars, -1, lpMultiByteStr, cchMultiByte, null, null); + transferData.stgmedium = new STGMEDIUM(); + transferData.stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.stgmedium.unionField = lpMultiByteStr; + transferData.stgmedium.pUnkForRelease = 0; + transferData.result = COM.S_OK; + return; +} + +/** + * This implementation of nativeToJava converts a platform specific + * representation of a URL and optionally, a title to a java String[]. + * For additional information see Transfer#nativeToJava. + * + * @param transferData the platform specific representation of the data to be + * been converted + * @return a java String[] containing a URL and optionally a title if the + * conversion was successful; otherwise null + */ +public Object nativeToJava(TransferData transferData){ + if (!isSupportedType(transferData) || transferData.pIDataObject is 0) return null; + IDataObject data = new IDataObject(transferData.pIDataObject); + data.AddRef(); + STGMEDIUM stgmedium = new STGMEDIUM(); + FORMATETC formatetc = transferData.formatetc; + stgmedium.tymed = COM.TYMED_HGLOBAL; + transferData.result = data.GetData(formatetc, stgmedium); + data.Release(); + if (transferData.result !is COM.S_OK) return null; + int hMem = stgmedium.unionField; + try { + int lpMultiByteStr = OS.GlobalLock(hMem); + if (lpMultiByteStr is 0) return null; + try { + int codePage = OS.GetACP(); + int cchWideChar = OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, null, 0); + if (cchWideChar is 0) return null; + char[] lpWideCharStr = new char [cchWideChar - 1]; + OS.MultiByteToWideChar (codePage, OS.MB_PRECOMPOSED, lpMultiByteStr, -1, lpWideCharStr, lpWideCharStr.length); + return new String[]{new String(lpWideCharStr)}; + } finally { + OS.GlobalUnlock(hMem); + } + } finally { + OS.GlobalFree(hMem); + } +} + +protected int[] getTypeIds(){ + return new int[] {CFSTR_INETURLID}; +} + +protected String[] getTypeNames(){ + return new String[] {CFSTR_INETURL}; +} + +bool checkURL(Object object) { + if (object is null || !(object instanceof String[]) || ((String[])object).length is 0) return false; + String[] strings = (String[])object; + if (strings[0] is null || strings[0].length() is 0) return false; + try { + new URL(strings[0]); + } catch (java.net.MalformedURLException e) { + return false; + } + return true; +} + +protected bool validate(Object object) { + return checkURL(object); +} +}