changeset 28:b868bfa989cd

Cursor
author Frank Benoit <benoit@tionex.de>
date Mon, 28 Jan 2008 00:22:22 +0100
parents 39834037712c
children 16332d261df7
files dwt/dwthelper/utils.d dwt/graphics/Cursor.d dwt/internal/win32/OS.d dwt/internal/win32/WINAPI.d
diffstat 4 files changed, 491 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/dwt/dwthelper/utils.d	Sun Jan 27 23:42:27 2008 +0100
+++ b/dwt/dwthelper/utils.d	Mon Jan 28 00:22:22 2008 +0100
@@ -92,6 +92,10 @@
     dst[ dstBegin .. dstBegin + srcEnd - srcBegin ] = src[ srcBegin .. srcEnd ];
 }
 
+public wchar[] toCharArray( char[] str ){
+    return StrToWCHARs( str );
+}
+
 public bool endsWith( char[] src, char[] pattern ){
     if( src.length < pattern.length ){
         return false;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/graphics/Cursor.d	Mon Jan 28 00:22:22 2008 +0100
@@ -0,0 +1,484 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.graphics.Cursor;
+
+
+import dwt.DWT;
+import dwt.DWTError;
+import dwt.internal.win32.OS;
+
+import dwt.graphics.Resource;
+import dwt.graphics.Device;
+import dwt.graphics.ImageData;
+import dwt.graphics.RGB;
+import dwt.graphics.PaletteData;
+import dwt.graphics.Image;
+
+import tango.text.convert.Format;
+
+/**
+ * Instances of this class manage operating system resources that
+ * specify the appearance of the on-screen pointer. To create a
+ * cursor you specify the device and either a simple cursor style
+ * describing one of the standard operating system provided cursors
+ * or the image and mask data for the desired appearance.
+ * <p>
+ * Application code must explicitly invoke the <code>Cursor.dispose()</code>
+ * method to release the operating system resources managed by each instance
+ * when those instances are no longer required.
+ * </p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>
+ *   CURSOR_ARROW, CURSOR_WAIT, CURSOR_CROSS, CURSOR_APPSTARTING, CURSOR_HELP,
+ *   CURSOR_SIZEALL, CURSOR_SIZENESW, CURSOR_SIZENS, CURSOR_SIZENWSE, CURSOR_SIZEWE,
+ *   CURSOR_SIZEN, CURSOR_SIZES, CURSOR_SIZEE, CURSOR_SIZEW, CURSOR_SIZENE, CURSOR_SIZESE,
+ *   CURSOR_SIZESW, CURSOR_SIZENW, CURSOR_UPARROW, CURSOR_IBEAM, CURSOR_NO, CURSOR_HAND
+ * </dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the above styles may be specified.
+ * </p>
+ */
+
+public final class Cursor : Resource {
+
+    /**
+     * the handle to the OS cursor resource
+     * (Warning: This field is platform dependent)
+     * <p>
+     * <b>IMPORTANT:</b> This field is <em>not</em> 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.
+     * </p>
+     */
+    public HCURSOR handle;
+
+    bool isIcon;
+
+    /**
+     * data used to create a HAND cursor.
+     */
+    static const byte[] HAND_SOURCE = [
+        cast(byte)0xf9,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xf0,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xf0,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xf0,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xf0,cast(byte)0x3f,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xf0,cast(byte)0x07,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xf0,cast(byte)0x03,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xf0,cast(byte)0x00,cast(byte)0xff,cast(byte)0xff,
+
+        cast(byte)0x10,cast(byte)0x00,cast(byte)0x7f,cast(byte)0xff,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x7f,cast(byte)0xff,
+        cast(byte)0x80,cast(byte)0x00,cast(byte)0x7f,cast(byte)0xff,
+        cast(byte)0xc0,cast(byte)0x00,cast(byte)0x7f,cast(byte)0xff,
+        cast(byte)0xe0,cast(byte)0x00,cast(byte)0x7f,cast(byte)0xff,
+        cast(byte)0xf0,cast(byte)0x00,cast(byte)0x7f,cast(byte)0xff,
+        cast(byte)0xf8,cast(byte)0x00,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xfc,cast(byte)0x01,cast(byte)0xff,cast(byte)0xff,
+
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,
+        cast(byte)0xff,cast(byte)0xff,cast(byte)0xff,cast(byte)0xff
+    ];
+    static const byte[] HAND_MASK = [
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x06,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x06,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x06,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x06,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x06,cast(byte)0xc0,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x06,cast(byte)0xd8,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x06,cast(byte)0xd8,cast(byte)0x00,cast(byte)0x00,
+
+        cast(byte)0x07,cast(byte)0xdb,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x67,cast(byte)0xfb,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x3f,cast(byte)0xff,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x1f,cast(byte)0xff,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x0f,cast(byte)0xff,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x07,cast(byte)0xff,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x03,cast(byte)0xfe,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,
+        cast(byte)0x00,cast(byte)0x00,cast(byte)0x00,cast(byte)0x00
+    ];
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+this() {
+}
+
+/**
+ * Constructs a new cursor given a device and a style
+ * constant describing the desired cursor appearance.
+ * <p>
+ * You must dispose the cursor when it is no longer required.
+ * </p>
+ *
+ * @param device the device on which to allocate the cursor
+ * @param style the style of cursor to allocate
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_INVALID_ARGUMENT - when an unknown style is specified</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
+ * </ul>
+ *
+ * @see DWT#CURSOR_ARROW
+ * @see DWT#CURSOR_WAIT
+ * @see DWT#CURSOR_CROSS
+ * @see DWT#CURSOR_APPSTARTING
+ * @see DWT#CURSOR_HELP
+ * @see DWT#CURSOR_SIZEALL
+ * @see DWT#CURSOR_SIZENESW
+ * @see DWT#CURSOR_SIZENS
+ * @see DWT#CURSOR_SIZENWSE
+ * @see DWT#CURSOR_SIZEWE
+ * @see DWT#CURSOR_SIZEN
+ * @see DWT#CURSOR_SIZES
+ * @see DWT#CURSOR_SIZEE
+ * @see DWT#CURSOR_SIZEW
+ * @see DWT#CURSOR_SIZENE
+ * @see DWT#CURSOR_SIZESE
+ * @see DWT#CURSOR_SIZESW
+ * @see DWT#CURSOR_SIZENW
+ * @see DWT#CURSOR_UPARROW
+ * @see DWT#CURSOR_IBEAM
+ * @see DWT#CURSOR_NO
+ * @see DWT#CURSOR_HAND
+ */
+public this(Device device, int style) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    this.device = device;
+    int lpCursorName = 0;
+    switch (style) {
+        case DWT.CURSOR_HAND:       lpCursorName = OS.IDC_HAND; break;
+        case DWT.CURSOR_ARROW:      lpCursorName = OS.IDC_ARROW; break;
+        case DWT.CURSOR_WAIT:       lpCursorName = OS.IDC_WAIT; break;
+        case DWT.CURSOR_CROSS:      lpCursorName = OS.IDC_CROSS; break;
+        case DWT.CURSOR_APPSTARTING:    lpCursorName = OS.IDC_APPSTARTING; break;
+        case DWT.CURSOR_HELP:       lpCursorName = OS.IDC_HELP; break;
+        case DWT.CURSOR_SIZEALL:    lpCursorName = OS.IDC_SIZEALL; break;
+        case DWT.CURSOR_SIZENESW:   lpCursorName = OS.IDC_SIZENESW; break;
+        case DWT.CURSOR_SIZENS:     lpCursorName = OS.IDC_SIZENS; break;
+        case DWT.CURSOR_SIZENWSE:   lpCursorName = OS.IDC_SIZENWSE; break;
+        case DWT.CURSOR_SIZEWE:     lpCursorName = OS.IDC_SIZEWE; break;
+        case DWT.CURSOR_SIZEN:      lpCursorName = OS.IDC_SIZENS; break;
+        case DWT.CURSOR_SIZES:      lpCursorName = OS.IDC_SIZENS; break;
+        case DWT.CURSOR_SIZEE:      lpCursorName = OS.IDC_SIZEWE; break;
+        case DWT.CURSOR_SIZEW:      lpCursorName = OS.IDC_SIZEWE; break;
+        case DWT.CURSOR_SIZENE:     lpCursorName = OS.IDC_SIZENESW; break;
+        case DWT.CURSOR_SIZESE:     lpCursorName = OS.IDC_SIZENWSE; break;
+        case DWT.CURSOR_SIZESW:     lpCursorName = OS.IDC_SIZENESW; break;
+        case DWT.CURSOR_SIZENW:     lpCursorName = OS.IDC_SIZENWSE; break;
+        case DWT.CURSOR_UPARROW:    lpCursorName = OS.IDC_UPARROW; break;
+        case DWT.CURSOR_IBEAM:      lpCursorName = OS.IDC_IBEAM; break;
+        case DWT.CURSOR_NO:         lpCursorName = OS.IDC_NO; break;
+        default:
+            DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    handle = OS.LoadCursor(null, cast(wchar*)lpCursorName);
+    /*
+    * IDC_HAND is supported only on Windows 2000 and Windows 98.
+    * Create a hand cursor if running in other Windows platforms.
+    */
+    if (handle is null && style is DWT.CURSOR_HAND) {
+        int width = OS.GetSystemMetrics(OS.SM_CXCURSOR);
+        int height = OS.GetSystemMetrics(OS.SM_CYCURSOR);
+        if (width is 32 && height is 32) {
+            auto hInst = OS.GetModuleHandle(null);
+            if (OS.IsWinCE) DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
+            handle = OS.CreateCursor(hInst, 5, 0, 32, 32, HAND_SOURCE.ptr, HAND_MASK.ptr);
+
+        }
+    }
+    if (handle is null) DWT.error(DWT.ERROR_NO_HANDLES);
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Constructs a new cursor given a device, image and mask
+ * data describing the desired cursor appearance, and the x
+ * and y coordinates of the <em>hotspot</em> (that is, the point
+ * within the area covered by the cursor which is considered
+ * to be where the on-screen pointer is "pointing").
+ * <p>
+ * The mask data is allowed to be null, but in this case the source
+ * must be an ImageData representing an icon that specifies both
+ * color data and mask data.
+ * <p>
+ * You must dispose the cursor when it is no longer required.
+ * </p>
+ *
+ * @param device the device on which to allocate the cursor
+ * @param source the color data for the cursor
+ * @param mask the mask data for the cursor (or null)
+ * @param hotspotX the x coordinate of the cursor's hotspot
+ * @param hotspotY the y coordinate of the cursor's hotspot
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the source is null</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the mask is null and the source does not have a mask</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the source and the mask are not the same
+ *          size, or if the hotspot is outside the bounds of the image</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
+ * </ul>
+ */
+public this(Device device, ImageData source, ImageData mask, int hotspotX, int hotspotY) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    this.device = device;
+    if (source is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (mask is null) {
+        if (source.getTransparencyType() !is DWT.TRANSPARENCY_MASK) {
+            DWT.error(DWT.ERROR_NULL_ARGUMENT);
+        }
+        mask = source.getTransparencyMask();
+    }
+    /* Check the bounds. Mask must be the same size as source */
+    if (mask.width !is source.width || mask.height !is source.height) {
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    /* Check the hotspots */
+    if (hotspotX >= source.width || hotspotX < 0 ||
+        hotspotY >= source.height || hotspotY < 0) {
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    /* Convert depth to 1 */
+    mask = ImageData.convertMask(mask);
+    source = ImageData.convertMask(source);
+
+    /* Make sure source and mask scanline pad is 2 */
+    byte[] sourceData = ImageData.convertPad(source.data, source.width, source.height, source.depth, source.scanlinePad, 2);
+    byte[] maskData = ImageData.convertPad(mask.data, mask.width, mask.height, mask.depth, mask.scanlinePad, 2);
+
+    /* Create the cursor */
+    auto hInst = OS.GetModuleHandle(null);
+    if (OS.IsWinCE) DWT.error (DWT.ERROR_NOT_IMPLEMENTED);
+    handle = OS.CreateCursor(hInst, hotspotX, hotspotY, source.width, source.height, sourceData.ptr, maskData.ptr);
+    if (handle is null) DWT.error(DWT.ERROR_NO_HANDLES);
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Constructs a new cursor given a device, image data describing
+ * the desired cursor appearance, and the x and y coordinates of
+ * the <em>hotspot</em> (that is, the point within the area
+ * covered by the cursor which is considered to be where the
+ * on-screen pointer is "pointing").
+ * <p>
+ * You must dispose the cursor when it is no longer required.
+ * </p>
+ *
+ * @param device the device on which to allocate the cursor
+ * @param source the image data for the cursor
+ * @param hotspotX the x coordinate of the cursor's hotspot
+ * @param hotspotY the y coordinate of the cursor's hotspot
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the image is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the hotspot is outside the bounds of the
+ *       image</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES - if a handle could not be obtained for cursor creation</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public this(Device device, ImageData source, int hotspotX, int hotspotY) {
+    if (device is null) device = Device.getDevice();
+    if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    this.device = device;
+    if (source is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    /* Check the hotspots */
+    if (hotspotX >= source.width || hotspotX < 0 ||
+        hotspotY >= source.height || hotspotY < 0) {
+        DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    ImageData mask = source.getTransparencyMask();
+    int[] result = Image.init(device, null, source, mask);
+    auto hBitmap = cast(HBITMAP)result[0];
+    auto hMask = cast(HBITMAP)result[1];
+    /* Create the icon */
+    ICONINFO info;
+    info.fIcon = false;
+    info.hbmColor = hBitmap;
+    info.hbmMask = hMask;
+    info.xHotspot = hotspotX;
+    info.yHotspot = hotspotY;
+    handle = OS.CreateIconIndirect(&info);
+    if (handle is null) DWT.error(DWT.ERROR_NO_HANDLES);
+    OS.DeleteObject(hBitmap);
+    OS.DeleteObject(hMask);
+    isIcon = true;
+    if (device.tracking) device.new_Object(this);
+}
+
+/**
+ * Disposes of the operating system resources associated with
+ * the cursor. Applications must dispose of all cursors which
+ * they allocate.
+ */
+public void dispose () {
+    if (handle is null) return;
+    if (device.isDisposed()) return;
+
+    /*
+    * It is an error in Windows to destroy the current
+    * cursor.  Check that the cursor that is about to
+    * be destroyed is the current cursor.  If so, set
+    * the current cursor to be IDC_ARROW.  Note that
+    * Windows shares predefined cursors so the call to
+    * LoadCursor() does not leak.
+    */
+    // TEMPORARY CODE
+//  if (OS.GetCursor() is handle) {
+//      OS.SetCursor(OS.LoadCursor(0, OS.IDC_ARROW));
+//  }
+
+    if (isIcon) {
+        OS.DestroyIcon(handle);
+    } else {
+        /*
+        * The MSDN states that one should not destroy a shared
+        * cursor, that is, one obtained from LoadCursor.
+        * However, it does not appear to do any harm, so rather
+        * than keep track of how a cursor was created, we just
+        * destroy them all. If this causes problems in the future,
+        * put the flag back in.
+        */
+        if (!OS.IsWinCE) OS.DestroyCursor(handle);
+    }
+    handle = null;
+    if (device.tracking) device.dispose_Object(this);
+    device = null;
+}
+
+/**
+ * Compares the argument to the receiver, and returns true
+ * if they represent the <em>same</em> object using a class
+ * specific comparison.
+ *
+ * @param object the object to compare with this object
+ * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
+ *
+ * @see #hashCode
+ */
+public override int opEquals (Object object) {
+    if (object is this) return true;
+    if (!(cast(Cursor)object)) return false;
+    Cursor cursor = cast(Cursor) object;
+    return device is cursor.device && handle is cursor.handle;
+}
+
+/**
+ * Returns an integer hash code for the receiver. Any two
+ * objects that return <code>true</code> when passed to
+ * <code>equals</code> must return the same value for this
+ * method.
+ *
+ * @return the receiver's hash
+ *
+ * @see #equals
+ */
+public override hash_t toHash () {
+    return cast(hash_t)handle;
+}
+
+/**
+ * Returns <code>true</code> if the cursor has been disposed,
+ * and <code>false</code> otherwise.
+ * <p>
+ * This method gets the dispose state for the cursor.
+ * When a cursor has been disposed, it is an error to
+ * invoke any other method using the cursor.
+ *
+ * @return <code>true</code> when the cursor is disposed and <code>false</code> otherwise
+ */
+public bool isDisposed() {
+    return handle is null;
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the receiver
+ */
+public char[] toString () {
+    if (isDisposed()) return "Cursor {*DISPOSED*}";
+    return Format( "Cursor {{{}}", handle );
+}
+
+/**
+ * Invokes platform specific functionality to allocate a new cursor.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Cursor</code>. 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 called from
+ * application code.
+ * </p>
+ *
+ * @param device the device on which to allocate the color
+ * @param handle the handle for the cursor
+ * @return a new cursor object containing the specified device and handle
+ */
+public static Cursor win32_new(Device device, HCURSOR handle) {
+    if (device is null) device = Device.getDevice();
+    Cursor cursor = new Cursor();
+    cursor.handle = handle;
+    cursor.device = device;
+    return cursor;
+}
+
+}
--- a/dwt/internal/win32/OS.d	Sun Jan 27 23:42:27 2008 +0100
+++ b/dwt/internal/win32/OS.d	Mon Jan 28 00:22:22 2008 +0100
@@ -4264,6 +4264,7 @@
 alias WINAPI.HeapAlloc HeapAlloc;
 alias WINAPI.HeapFree HeapFree;
 alias WINAPI.HideCaret HideCaret;
+alias WINAPI.IIDFromString IIDFromString;
 alias WINAPI.ImageList_Add ImageList_Add;
 alias WINAPI.ImageList_AddMasked ImageList_AddMasked;
 alias WINAPI.ImageList_Create ImageList_Create;
--- a/dwt/internal/win32/WINAPI.d	Sun Jan 27 23:42:27 2008 +0100
+++ b/dwt/internal/win32/WINAPI.d	Mon Jan 28 00:22:22 2008 +0100
@@ -40,6 +40,8 @@
   int nHeightSrc,     // height of source rectangle
   UINT crTransparent  // color to make transparent
 );
+int IIDFromString (wchar* lpsz, byte* lpiid);
+
 }