changeset 31:92c102dd64a3

Added all widgets modules as dummy. Most modules of accessible are equal to the linux version, except Accessible.
author Frank Benoit <benoit@tionex.de>
date Mon, 28 Jan 2008 04:47:28 +0100
parents 1e14cb29290a
children 2985239119a3
files dsss.conf dwt/accessible/ACC.d dwt/accessible/Accessible.d dwt/accessible/AccessibleAdapter.d dwt/accessible/AccessibleControlAdapter.d dwt/accessible/AccessibleControlEvent.d dwt/accessible/AccessibleControlListener.d dwt/accessible/AccessibleEvent.d dwt/accessible/AccessibleListener.d dwt/accessible/AccessibleTextAdapter.d dwt/accessible/AccessibleTextEvent.d dwt/accessible/AccessibleTextListener.d dwt/graphics/TextLayout.d dwt/internal/image/GIFFileFormat.d dwt/internal/image/JPEGDecoder.d dwt/internal/image/JPEGFileFormat.d dwt/internal/image/JPEGFrameHeader.d dwt/internal/image/JPEGScanHeader.d dwt/internal/image/LZWCodec.d dwt/internal/image/OS2BMPFileFormat.d dwt/internal/image/PNGFileFormat.d dwt/internal/image/PngChunk.d dwt/internal/image/PngIdatChunk.d dwt/internal/image/TIFFDirectory.d dwt/internal/image/WinBMPFileFormat.d dwt/internal/image/WinICOFileFormat.d dwt/layout/FillLayout.d dwt/widgets/Button.d dwt/widgets/Canvas.d dwt/widgets/Caret.d dwt/widgets/ColorDialog.d dwt/widgets/Combo.d dwt/widgets/Composite.d dwt/widgets/Control.d dwt/widgets/CoolBar.d dwt/widgets/CoolItem.d dwt/widgets/DateTime.d dwt/widgets/Decorations.d dwt/widgets/Dialog.d dwt/widgets/DirectoryDialog.d dwt/widgets/Display.d dwt/widgets/Event.d dwt/widgets/EventTable.d dwt/widgets/ExpandBar.d dwt/widgets/ExpandItem.d dwt/widgets/FileDialog.d dwt/widgets/FontDialog.d dwt/widgets/Group.d dwt/widgets/Item.d dwt/widgets/Label.d dwt/widgets/Layout.d dwt/widgets/Link.d dwt/widgets/List.d dwt/widgets/Listener.d dwt/widgets/Menu.d dwt/widgets/MenuItem.d dwt/widgets/MessageBox.d dwt/widgets/Monitor.d dwt/widgets/ProgressBar.d dwt/widgets/RunnableLock.d dwt/widgets/Sash.d dwt/widgets/Scale.d dwt/widgets/ScrollBar.d dwt/widgets/Scrollable.d dwt/widgets/Shell.d dwt/widgets/Slider.d dwt/widgets/Spinner.d dwt/widgets/Synchronizer.d dwt/widgets/TabFolder.d dwt/widgets/TabItem.d dwt/widgets/Table.d dwt/widgets/TableColumn.d dwt/widgets/TableItem.d dwt/widgets/Text.d dwt/widgets/ToolBar.d dwt/widgets/ToolItem.d dwt/widgets/ToolTip.d dwt/widgets/Tracker.d dwt/widgets/Tray.d dwt/widgets/TrayItem.d dwt/widgets/Tree.d dwt/widgets/TreeColumn.d dwt/widgets/TreeItem.d dwt/widgets/TypedListener.d dwt/widgets/Widget.d
diffstat 85 files changed, 69855 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/dsss.conf	Mon Jan 28 02:37:23 2008 +0100
+++ b/dsss.conf	Mon Jan 28 04:47:28 2008 +0100
@@ -2,7 +2,4 @@
 
 [dwt]
 type=library
-exclude=dwt/events
-exclude+=dwt/internal/image
-exclude+=dwt/layout
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/ACC.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.ACC;
+
+
+/**
+ * Class ACC contains all the constants used in defining an
+ * Accessible object.
+ *
+ * @since 2.0
+ */
+public class ACC {
+    public static const int STATE_NORMAL = 0x00000000;
+    public static const int STATE_SELECTED = 0x00000002;
+    public static const int STATE_SELECTABLE = 0x00200000;
+    public static const int STATE_MULTISELECTABLE = 0x1000000;
+    public static const int STATE_FOCUSED = 0x00000004;
+    public static const int STATE_FOCUSABLE = 0x00100000;
+    public static const int STATE_PRESSED = 0x8;
+    public static const int STATE_CHECKED = 0x10;
+    public static const int STATE_EXPANDED = 0x200;
+    public static const int STATE_COLLAPSED = 0x400;
+    public static const int STATE_HOTTRACKED = 0x80;
+    public static const int STATE_BUSY = 0x800;
+    public static const int STATE_READONLY = 0x40;
+    public static const int STATE_INVISIBLE = 0x8000;
+    public static const int STATE_OFFSCREEN = 0x10000;
+    public static const int STATE_SIZEABLE = 0x20000;
+    public static const int STATE_LINKED = 0x400000;
+
+    public static const int ROLE_CLIENT_AREA = 0xa;
+    public static const int ROLE_WINDOW = 0x9;
+    public static const int ROLE_MENUBAR = 0x2;
+    public static const int ROLE_MENU = 0xb;
+    public static const int ROLE_MENUITEM = 0xc;
+    public static const int ROLE_SEPARATOR = 0x15;
+    public static const int ROLE_TOOLTIP = 0xd;
+    public static const int ROLE_SCROLLBAR = 0x3;
+    public static const int ROLE_DIALOG = 0x12;
+    public static const int ROLE_LABEL = 0x29;
+    public static const int ROLE_PUSHBUTTON = 0x2b;
+    public static const int ROLE_CHECKBUTTON = 0x2c;
+    public static const int ROLE_RADIOBUTTON = 0x2d;
+    public static const int ROLE_COMBOBOX = 0x2e;
+    public static const int ROLE_TEXT = 0x2a;
+    public static const int ROLE_TOOLBAR = 0x16;
+    public static const int ROLE_LIST = 0x21;
+    public static const int ROLE_LISTITEM = 0x22;
+    public static const int ROLE_TABLE = 0x18;
+    public static const int ROLE_TABLECELL = 0x1d;
+    public static const int ROLE_TABLECOLUMNHEADER = 0x19;
+    /** @deprecated use ROLE_TABLECOLUMNHEADER */
+    public static const int ROLE_TABLECOLUMN = ROLE_TABLECOLUMNHEADER;
+    public static const int ROLE_TABLEROWHEADER = 0x1a;
+    public static const int ROLE_TREE = 0x23;
+    public static const int ROLE_TREEITEM = 0x24;
+    public static const int ROLE_TABFOLDER = 0x3c;
+    public static const int ROLE_TABITEM = 0x25;
+    public static const int ROLE_PROGRESSBAR = 0x30;
+    public static const int ROLE_SLIDER = 0x33;
+    public static const int ROLE_LINK = 0x1e;
+
+    public static const int CHILDID_SELF = -1;
+    public static const int CHILDID_NONE = -2;
+    public static const int CHILDID_MULTIPLE = -3;
+
+    public static const int TEXT_INSERT = 0;
+    public static const int TEXT_DELETE = 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/Accessible.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1458 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.Accessible;
+
+//PORTING_TYPE
+public class Accessible {
+}
+
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.internal.ole.win32.COM;
+import dwt.internal.ole.win32.COMObject;
+import dwt.internal.ole.win32.GUID;
+import dwt.internal.ole.win32.IAccessible;
+import dwt.internal.ole.win32.IEnumVARIANT;
+import dwt.internal.ole.win32.VARIANT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.TVITEM;
+import dwt.ole.win32.OLE;
+import dwt.widgets.Control;
+import dwt.widgets.Table;
+import dwt.widgets.TableItem;
+import dwt.widgets.Tree;
+import dwt.widgets.TreeItem;
+import dwt.widgets.Widget;
+
+/**
+ * Instances of this class provide a bridge between application
+ * code and assistive technology clients. Many platforms provide
+ * default accessible behavior for most widgets, and this class
+ * allows that default behavior to be overridden. Applications
+ * can get the default Accessible object for a control by sending
+ * it <code>getAccessible</code>, and then add an accessible listener
+ * to override simple items like the name and help string, or they
+ * can add an accessible control listener to override complex items.
+ * As a rule of thumb, an application would only want to use the
+ * accessible control listener to implement accessibility for a
+ * custom control.
+ *
+ * @see Control#getAccessible
+ * @see AccessibleListener
+ * @see AccessibleEvent
+ * @see AccessibleControlListener
+ * @see AccessibleControlEvent
+ *
+ * @since 2.0
+ */
+public class Accessible {
+    int refCount = 0, enumIndex = 0;
+    COMObject objIAccessible, objIEnumVARIANT;
+    IAccessible iaccessible;
+    Vector accessibleListeners = new Vector();
+    Vector accessibleControlListeners = new Vector();
+    Vector textListeners = new Vector ();
+    Object[] variants;
+    Control control;
+
+    Accessible(Control control) {
+        this.control = control;
+        int /*long*/[] ppvObject = new int /*long*/[1];
+        /* CreateStdAccessibleObject([in] hwnd, [in] idObject, [in] riidInterface, [out] ppvObject).
+         * AddRef has already been called on ppvObject by the callee and must be released by the caller.
+         */
+        int result = (int)/*64*/COM.CreateStdAccessibleObject(control.handle, COM.OBJID_CLIENT, COM.IIDIAccessible, ppvObject);
+        /* The object needs to be checked, because if the CreateStdAccessibleObject()
+         * symbol is not found, the return value is S_OK.
+         */
+        if (ppvObject[0] is 0) return;
+        if (result !is COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result);
+        iaccessible = new IAccessible(ppvObject[0]);
+
+        objIAccessible = new COMObject(new int[] {2,0,0,1,3,5,8,1,1,2,2,2,2,2,2,2,3,2,1,1,2,2,5,3,3,1,2,2}) {
+            public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);}
+            public int /*long*/ method1(int /*long*/[] args) {return AddRef();}
+            public int /*long*/ method2(int /*long*/[] args) {return Release();}
+            // method3 GetTypeInfoCount - not implemented
+            // method4 GetTypeInfo - not implemented
+            // method5 GetIDsOfNames - not implemented
+            // method6 Invoke - not implemented
+            public int /*long*/ method7(int /*long*/[] args) {return get_accParent(args[0]);}
+            public int /*long*/ method8(int /*long*/[] args) {return get_accChildCount(args[0]);}
+            public int /*long*/ method9(int /*long*/[] args) {return get_accChild(args[0], args[1]);}
+            public int /*long*/ method10(int /*long*/[] args) {return get_accName(args[0], args[1]);}
+            public int /*long*/ method11(int /*long*/[] args) {return get_accValue(args[0], args[1]);}
+            public int /*long*/ method12(int /*long*/[] args) {return get_accDescription(args[0], args[1]);}
+            public int /*long*/ method13(int /*long*/[] args) {return get_accRole(args[0], args[1]);}
+            public int /*long*/ method14(int /*long*/[] args) {return get_accState(args[0], args[1]);}
+            public int /*long*/ method15(int /*long*/[] args) {return get_accHelp(args[0], args[1]);}
+            public int /*long*/ method16(int /*long*/[] args) {return get_accHelpTopic(args[0], args[1], args[2]);}
+            public int /*long*/ method17(int /*long*/[] args) {return get_accKeyboardShortcut(args[0], args[1]);}
+            public int /*long*/ method18(int /*long*/[] args) {return get_accFocus(args[0]);}
+            public int /*long*/ method19(int /*long*/[] args) {return get_accSelection(args[0]);}
+            public int /*long*/ method20(int /*long*/[] args) {return get_accDefaultAction(args[0], args[1]);}
+            public int /*long*/ method21(int /*long*/[] args) {return accSelect((int)/*64*/args[0], args[1]);}
+            public int /*long*/ method22(int /*long*/[] args) {return accLocation(args[0], args[1], args[2], args[3], args[4]);}
+            public int /*long*/ method23(int /*long*/[] args) {return accNavigate((int)/*64*/args[0], args[1], args[2]);}
+            public int /*long*/ method24(int /*long*/[] args) {return accHitTest((int)/*64*/args[0], (int)/*64*/args[1], args[2]);}
+            public int /*long*/ method25(int /*long*/[] args) {return accDoDefaultAction(args[0]);}
+            public int /*long*/ method26(int /*long*/[] args) {return put_accName(args[0], args[1]);}
+            public int /*long*/ method27(int /*long*/[] args) {return put_accValue(args[0], args[1]);}
+        };
+
+        int /*long*/ ppVtable = objIAccessible.ppVtable;
+        int /*long*/[] pVtable = new int /*long*/[1];
+        COM.MoveMemory(pVtable, ppVtable, OS.PTR_SIZEOF);
+        int /*long*/[] funcs = new int /*long*/[28];
+        COM.MoveMemory(funcs, pVtable[0], OS.PTR_SIZEOF * funcs.length);
+        funcs[9] = COM.get_accChild_CALLBACK(funcs[9]);
+        funcs[10] = COM.get_accName_CALLBACK(funcs[10]);
+        funcs[11] = COM.get_accValue_CALLBACK(funcs[11]);
+        funcs[12] = COM.get_accDescription_CALLBACK(funcs[12]);
+        funcs[13] = COM.get_accRole_CALLBACK(funcs[13]);
+        funcs[14] = COM.get_accState_CALLBACK(funcs[14]);
+        funcs[15] = COM.get_accHelp_CALLBACK(funcs[15]);
+        funcs[16] = COM.get_accHelpTopic_CALLBACK(funcs[16]);
+        funcs[17] = COM.get_accKeyboardShortcut_CALLBACK(funcs[17]);
+        funcs[20] = COM.get_accDefaultAction_CALLBACK(funcs[20]);
+        funcs[21] = COM.accSelect_CALLBACK(funcs[21]);
+        funcs[22] = COM.accLocation_CALLBACK(funcs[22]);
+        funcs[23] = COM.accNavigate_CALLBACK(funcs[23]);
+        funcs[25] = COM.accDoDefaultAction_CALLBACK(funcs[25]);
+        funcs[26] = COM.put_accName_CALLBACK(funcs[26]);
+        funcs[27] = COM.put_accValue_CALLBACK(funcs[27]);
+        COM.MoveMemory(pVtable[0], funcs, OS.PTR_SIZEOF * funcs.length);
+
+        objIEnumVARIANT = new COMObject(new int[] {2,0,0,3,1,0,1}) {
+            public int /*long*/ method0(int /*long*/[] args) {return QueryInterface(args[0], args[1]);}
+            public int /*long*/ method1(int /*long*/[] args) {return AddRef();}
+            public int /*long*/ method2(int /*long*/[] args) {return Release();}
+            public int /*long*/ method3(int /*long*/[] args) {return Next((int)args[0], args[1], args[2]);}
+            public int /*long*/ method4(int /*long*/[] args) {return Skip((int)args[0]);}
+            public int /*long*/ method5(int /*long*/[] args) {return Reset();}
+            public int /*long*/ method6(int /*long*/[] args) {return Clone(args[0]);}
+        };
+        AddRef();
+    }
+
+    /**
+     * Invokes platform specific functionality to allocate a new accessible object.
+     * <p>
+     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+     * API for <code>Accessible</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 control the control to get the accessible object for
+     * @return the platform specific accessible object
+     */
+    public static Accessible internal_new_Accessible(Control control) {
+        return new Accessible(control);
+    }
+
+    /**
+     * Adds the listener to the collection of listeners who will
+     * be notified when an accessible client asks for certain strings,
+     * such as name, description, help, or keyboard shortcut. The
+     * listener is notified by sending it one of the messages defined
+     * in the <code>AccessibleListener</code> interface.
+     *
+     * @param listener the listener that should be notified when the receiver
+     * is asked for a name, description, help, or keyboard shortcut string
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleListener
+     * @see #removeAccessibleListener
+     */
+    public void addAccessibleListener(AccessibleListener listener) {
+        checkWidget();
+        if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+        accessibleListeners.addElement(listener);
+    }
+
+    /**
+     * Adds the listener to the collection of listeners who will
+     * be notified when an accessible client asks for custom control
+     * specific information. The listener is notified by sending it
+     * one of the messages defined in the <code>AccessibleControlListener</code>
+     * interface.
+     *
+     * @param listener the listener that should be notified when the receiver
+     * is asked for custom control specific information
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleControlListener
+     * @see #removeAccessibleControlListener
+     */
+    public void addAccessibleControlListener(AccessibleControlListener listener) {
+        checkWidget();
+        if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+        accessibleControlListeners.addElement(listener);
+    }
+
+    /**
+     * Adds the listener to the collection of listeners who will
+     * be notified when an accessible client asks for custom text control
+     * specific information. The listener is notified by sending it
+     * one of the messages defined in the <code>AccessibleTextListener</code>
+     * interface.
+     *
+     * @param listener the listener that should be notified when the receiver
+     * is asked for custom text control specific information
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleTextListener
+     * @see #removeAccessibleTextListener
+     *
+     * @since 3.0
+     */
+    public void addAccessibleTextListener (AccessibleTextListener listener) {
+        checkWidget ();
+        if (listener is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+        textListeners.addElement (listener);
+    }
+
+    /**
+     * Returns the control for this Accessible object.
+     *
+     * @return the receiver's control
+     * @since 3.0
+     */
+    public Control getControl() {
+        return control;
+    }
+
+    /**
+     * Invokes platform specific functionality to dispose an accessible object.
+     * <p>
+     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+     * API for <code>Accessible</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>
+     */
+    public void internal_dispose_Accessible() {
+        if (iaccessible !is null) {
+            iaccessible.Release();
+        }
+        iaccessible = null;
+        Release();
+    }
+
+    /**
+     * Invokes platform specific functionality to handle a window message.
+     * <p>
+     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+     * API for <code>Accessible</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>
+     */
+    public int /*long*/ internal_WM_GETOBJECT (int /*long*/ wParam, int /*long*/ lParam) {
+        if (objIAccessible is null) return 0;
+        if ((int)/*64*/lParam is COM.OBJID_CLIENT) {
+            /* LresultFromObject([in] riid, [in] wParam, [in] pAcc)
+             * The argument pAcc is owned by the caller so reference count does not
+             * need to be incremented.
+             */
+            return COM.LresultFromObject(COM.IIDIAccessible, wParam, objIAccessible.getAddress());
+        }
+        return 0;
+    }
+
+    /**
+     * Removes the listener from the collection of listeners who will
+     * be notified when an accessible client asks for certain strings,
+     * such as name, description, help, or keyboard shortcut.
+     *
+     * @param listener the listener that should no longer be notified when the receiver
+     * is asked for a name, description, help, or keyboard shortcut string
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleListener
+     * @see #addAccessibleListener
+     */
+    public void removeAccessibleListener(AccessibleListener listener) {
+        checkWidget();
+        if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+        accessibleListeners.removeElement(listener);
+    }
+
+    /**
+     * Removes the listener from the collection of listeners who will
+     * be notified when an accessible client asks for custom control
+     * specific information.
+     *
+     * @param listener the listener that should no longer be notified when the receiver
+     * is asked for custom control specific information
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleControlListener
+     * @see #addAccessibleControlListener
+     */
+    public void removeAccessibleControlListener(AccessibleControlListener listener) {
+        checkWidget();
+        if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+        accessibleControlListeners.removeElement(listener);
+    }
+
+    /**
+     * Removes the listener from the collection of listeners who will
+     * be notified when an accessible client asks for custom text control
+     * specific information.
+     *
+     * @param listener the listener that should no longer be notified when the receiver
+     * is asked for custom text control specific information
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleTextListener
+     * @see #addAccessibleTextListener
+     *
+     * @since 3.0
+     */
+    public void removeAccessibleTextListener (AccessibleTextListener listener) {
+        checkWidget ();
+        if (listener is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+        textListeners.removeElement (listener);
+    }
+
+    /**
+     * Sends a message to accessible clients that the child selection
+     * within a custom container control has changed.
+     *
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @since 3.0
+     */
+    public void selectionChanged () {
+        checkWidget();
+        COM.NotifyWinEvent (COM.EVENT_OBJECT_SELECTIONWITHIN, control.handle, COM.OBJID_CLIENT, COM.CHILDID_SELF);
+    }
+
+    /**
+     * Sends a message to accessible clients indicating that the focus
+     * has changed within a custom control.
+     *
+     * @param childID an identifier specifying a child of the control
+     *
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     */
+    public void setFocus(int childID) {
+        checkWidget();
+        COM.NotifyWinEvent (COM.EVENT_OBJECT_FOCUS, control.handle, COM.OBJID_CLIENT, childIDToOs(childID));
+    }
+
+    /**
+     * Sends a message to accessible clients that the text
+     * caret has moved within a custom control.
+     *
+     * @param index the new caret index within the control
+     *
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @since 3.0
+     */
+    public void textCaretMoved (int index) {
+        checkWidget();
+        COM.NotifyWinEvent (COM.EVENT_OBJECT_LOCATIONCHANGE, control.handle, COM.OBJID_CARET, COM.CHILDID_SELF);
+    }
+
+    /**
+     * Sends a message to accessible clients that the text
+     * within a custom control has changed.
+     *
+     * @param type the type of change, one of <code>ACC.NOTIFY_TEXT_INSERT</code>
+     * or <code>ACC.NOTIFY_TEXT_DELETE</code>
+     * @param startIndex the text index within the control where the insertion or deletion begins
+     * @param length the non-negative length in characters of the insertion or deletion
+     *
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see ACC#TEXT_INSERT
+     * @see ACC#TEXT_DELETE
+     *
+     * @since 3.0
+     */
+    public void textChanged (int type, int startIndex, int length) {
+        checkWidget();
+        COM.NotifyWinEvent (COM.EVENT_OBJECT_VALUECHANGE, control.handle, COM.OBJID_CLIENT, COM.CHILDID_SELF);
+    }
+
+    /**
+     * Sends a message to accessible clients that the text
+     * selection has changed within a custom control.
+     *
+     * @exception DWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @since 3.0
+     */
+    public void textSelectionChanged () {
+        checkWidget();
+        // not an MSAA event
+    }
+
+    /* QueryInterface([in] iid, [out] ppvObject)
+     * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject
+     * must be incremented before returning.  Caller is responsible for releasing ppvObject.
+     */
+    int QueryInterface(int /*long*/ iid, int /*long*/ ppvObject) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        GUID guid = new GUID();
+        COM.MoveMemory(guid, iid, GUID.sizeof);
+
+        if (COM.IsEqualGUID(guid, COM.IIDIUnknown)) {
+            COM.MoveMemory(ppvObject, new int /*long*/[] { objIAccessible.getAddress()}, OS.PTR_SIZEOF);
+            AddRef();
+            return COM.S_OK;
+        }
+
+        if (COM.IsEqualGUID(guid, COM.IIDIDispatch)) {
+            COM.MoveMemory(ppvObject, new int /*long*/[] { objIAccessible.getAddress()}, OS.PTR_SIZEOF);
+            AddRef();
+            return COM.S_OK;
+        }
+
+        if (COM.IsEqualGUID(guid, COM.IIDIAccessible)) {
+            COM.MoveMemory(ppvObject, new int /*long*/[] { objIAccessible.getAddress()}, OS.PTR_SIZEOF);
+            AddRef();
+            return COM.S_OK;
+        }
+
+        if (COM.IsEqualGUID(guid, COM.IIDIEnumVARIANT)) {
+            COM.MoveMemory(ppvObject, new int /*long*/[] { objIEnumVARIANT.getAddress()}, OS.PTR_SIZEOF);
+            AddRef();
+            enumIndex = 0;
+            return COM.S_OK;
+        }
+
+        int /*long*/[] ppv = new int /*long*/[1];
+        int result = iaccessible.QueryInterface(guid, ppv);
+        COM.MoveMemory(ppvObject, ppv, OS.PTR_SIZEOF);
+        return result;
+    }
+
+    int AddRef() {
+        refCount++;
+        return refCount;
+    }
+
+    int Release() {
+        refCount--;
+
+        if (refCount is 0) {
+            if (objIAccessible !is null)
+                objIAccessible.dispose();
+            objIAccessible = null;
+
+            if (objIEnumVARIANT !is null)
+                objIEnumVARIANT.dispose();
+            objIEnumVARIANT = null;
+        }
+        return refCount;
+    }
+
+    int accDoDefaultAction(int /*long*/ variant) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        int code = iaccessible.accDoDefaultAction(variant);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    int accHitTest(int xLeft, int yTop, int /*long*/ pvarChild) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        if (accessibleControlListeners.size() is 0) {
+            return iaccessible.accHitTest(xLeft, yTop, pvarChild);
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = ACC.CHILDID_NONE;
+        event.x = xLeft;
+        event.y = yTop;
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getChildAtPoint(event);
+        }
+        int childID = event.childID;
+        if (childID is ACC.CHILDID_NONE) {
+            return iaccessible.accHitTest(xLeft, yTop, pvarChild);
+        }
+        COM.MoveMemory(pvarChild, new short[] { COM.VT_I4 }, 2);
+        COM.MoveMemory(pvarChild + 8, new int[] { childIDToOs(childID) }, 4);
+        return COM.S_OK;
+    }
+
+    int accLocation(int /*long*/ pxLeft, int /*long*/ pyTop, int /*long*/ pcxWidth, int /*long*/ pcyHeight, int /*long*/ variant) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default location from the OS. */
+        int osLeft = 0, osTop = 0, osWidth = 0, osHeight = 0;
+        int code = iaccessible.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, variant);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleControlListeners.size() is 0) return code;
+        if (code is COM.S_OK) {
+            int[] pLeft = new int[1], pTop = new int[1], pWidth = new int[1], pHeight = new int[1];
+            COM.MoveMemory(pLeft, pxLeft, 4);
+            COM.MoveMemory(pTop, pyTop, 4);
+            COM.MoveMemory(pWidth, pcxWidth, 4);
+            COM.MoveMemory(pHeight, pcyHeight, 4);
+            osLeft = pLeft[0]; osTop = pTop[0]; osWidth = pWidth[0]; osHeight = pHeight[0];
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        event.x = osLeft;
+        event.y = osTop;
+        event.width = osWidth;
+        event.height = osHeight;
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getLocation(event);
+        }
+        OS.MoveMemory(pxLeft, new int[] { event.x }, 4);
+        OS.MoveMemory(pyTop, new int[] { event.y }, 4);
+        OS.MoveMemory(pcxWidth, new int[] { event.width }, 4);
+        OS.MoveMemory(pcyHeight, new int[] { event.height }, 4);
+        return COM.S_OK;
+    }
+
+    int accNavigate(int navDir, int /*long*/ variant, int /*long*/ pvarEndUpAt) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        int code = iaccessible.accNavigate(navDir, variant, pvarEndUpAt);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    int accSelect(int flagsSelect, int /*long*/ variant) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        int code = iaccessible.accSelect(flagsSelect, variant);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    /* get_accChild([in] varChild, [out] ppdispChild)
+     * Ownership of ppdispChild transfers from callee to caller so reference count on ppdispChild
+     * must be incremented before returning.  The caller is responsible for releasing ppdispChild.
+     */
+    int get_accChild(int /*long*/ variant, int /*long*/ ppdispChild) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+        if (accessibleControlListeners.size() is 0) {
+            int code = iaccessible.get_accChild(variant, ppdispChild);
+            if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+            return code;
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getChild(event);
+        }
+        Accessible accessible = event.accessible;
+        if (accessible !is null) {
+            accessible.AddRef();
+            COM.MoveMemory(ppdispChild, new int /*long*/[] { accessible.objIAccessible.getAddress() }, OS.PTR_SIZEOF);
+            return COM.S_OK;
+        }
+        return COM.S_FALSE;
+    }
+
+    int get_accChildCount(int /*long*/ pcountChildren) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+
+        /* Get the default child count from the OS. */
+        int osChildCount = 0;
+        int code = iaccessible.get_accChildCount(pcountChildren);
+        if (accessibleControlListeners.size() is 0) return code;
+        if (code is COM.S_OK) {
+            int[] pChildCount = new int[1];
+            COM.MoveMemory(pChildCount, pcountChildren, 4);
+            osChildCount = pChildCount[0];
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = ACC.CHILDID_SELF;
+        event.detail = osChildCount;
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getChildCount(event);
+        }
+
+        COM.MoveMemory(pcountChildren, new int[] { event.detail }, 4);
+        return COM.S_OK;
+    }
+
+    int get_accDefaultAction(int /*long*/ variant, int /*long*/ pszDefaultAction) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default defaultAction from the OS. */
+        String osDefaultAction = null;
+        int code = iaccessible.get_accDefaultAction(variant, pszDefaultAction);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleControlListeners.size() is 0) return code;
+        if (code is COM.S_OK) {
+            int /*long*/[] pDefaultAction = new int /*long*/[1];
+            COM.MoveMemory(pDefaultAction, pszDefaultAction, OS.PTR_SIZEOF);
+            int size = COM.SysStringByteLen(pDefaultAction[0]);
+            if (size > 0) {
+                char[] buffer = new char[(size + 1) /2];
+                COM.MoveMemory(buffer, pDefaultAction[0], size);
+                osDefaultAction = new String(buffer);
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        event.result = osDefaultAction;
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getDefaultAction(event);
+        }
+        if (event.result is null) return code;
+        char[] data = (event.result + "\0").toCharArray();
+        int /*long*/ ptr = COM.SysAllocString(data);
+        COM.MoveMemory(pszDefaultAction, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
+        return COM.S_OK;
+    }
+
+    int get_accDescription(int /*long*/ variant, int /*long*/ pszDescription) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default description from the OS. */
+        String osDescription = null;
+        int code = iaccessible.get_accDescription(variant, pszDescription);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        // TEMPORARY CODE - process tree even if there are no apps listening
+        if (accessibleListeners.size() is 0 && !(control instanceof Tree)) return code;
+        if (code is COM.S_OK) {
+            int /*long*/[] pDescription = new int /*long*/[1];
+            COM.MoveMemory(pDescription, pszDescription, OS.PTR_SIZEOF);
+            int size = COM.SysStringByteLen(pDescription[0]);
+            if (size > 0) {
+                char[] buffer = new char[(size + 1) /2];
+                COM.MoveMemory(buffer, pDescription[0], size);
+                osDescription = new String(buffer);
+            }
+        }
+
+        AccessibleEvent event = new AccessibleEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        event.result = osDescription;
+
+        // TEMPORARY CODE
+        /* Currently our tree columns are emulated using custom draw,
+         * so we need to create the description using the tree column
+         * header text and tree item text. */
+        if (v.lVal !is COM.CHILDID_SELF) {
+            if (control instanceof Tree) {
+                Tree tree = (Tree) control;
+                int columnCount = tree.getColumnCount ();
+                if (columnCount > 1) {
+                    int /*long*/ hwnd = control.handle, hItem = 0;
+                    if (OS.COMCTL32_MAJOR >= 6) {
+                        hItem = OS.SendMessage (hwnd, OS.TVM_MAPACCIDTOHTREEITEM, v.lVal, 0);
+                    } else {
+                        hItem = v.lVal;
+                    }
+                    Widget widget = tree.getDisplay ().findWidget (hwnd, hItem);
+                    event.result = "";
+                    if (widget !is null && widget instanceof TreeItem) {
+                        TreeItem item = (TreeItem) widget;
+                        for (int i = 1; i < columnCount; i++) {
+                            event.result += tree.getColumn(i).getText() + ": " + item.getText(i);
+                            if (i + 1 < columnCount) event.result += ", ";
+                        }
+                    }
+                }
+            }
+        }
+        for (int i = 0; i < accessibleListeners.size(); i++) {
+            AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
+            listener.getDescription(event);
+        }
+        if (event.result is null) return code;
+        char[] data = (event.result + "\0").toCharArray();
+        int /*long*/ ptr = COM.SysAllocString(data);
+        COM.MoveMemory(pszDescription, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
+        return COM.S_OK;
+    }
+
+    /* get_accFocus([out] int pvarChild)
+     * Ownership of pvarChild transfers from callee to caller so reference count on pvarChild
+     * must be incremented before returning.  The caller is responsible for releasing pvarChild.
+     */
+    int get_accFocus(int /*long*/ pvarChild) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+
+        /* Get the default focus child from the OS. */
+        int osChild = ACC.CHILDID_NONE;
+        int code = iaccessible.get_accFocus(pvarChild);
+        if (accessibleControlListeners.size() is 0) return code;
+        if (code is COM.S_OK) {
+            short[] pvt = new short[1];
+            COM.MoveMemory(pvt, pvarChild, 2);
+            if (pvt[0] is COM.VT_I4) {
+                int[] pChild = new int[1];
+                COM.MoveMemory(pChild, pvarChild + 8, 4);
+                osChild = osToChildID(pChild[0]);
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osChild;
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getFocus(event);
+        }
+        Accessible accessible = event.accessible;
+        if (accessible !is null) {
+            accessible.AddRef();
+            COM.MoveMemory(pvarChild, new short[] { COM.VT_DISPATCH }, 2);
+            COM.MoveMemory(pvarChild + 8, new int /*long*/[] { accessible.objIAccessible.getAddress() }, OS.PTR_SIZEOF);
+            return COM.S_OK;
+        }
+        int childID = event.childID;
+        if (childID is ACC.CHILDID_NONE) {
+            COM.MoveMemory(pvarChild, new short[] { COM.VT_EMPTY }, 2);
+            return COM.S_FALSE;
+        }
+        if (childID is ACC.CHILDID_SELF) {
+            AddRef();
+            COM.MoveMemory(pvarChild, new short[] { COM.VT_DISPATCH }, 2);
+            COM.MoveMemory(pvarChild + 8, new int /*long*/[] { objIAccessible.getAddress() }, OS.PTR_SIZEOF);
+            return COM.S_OK;
+        }
+        COM.MoveMemory(pvarChild, new short[] { COM.VT_I4 }, 2);
+        COM.MoveMemory(pvarChild + 8, new int[] { childIDToOs(childID) }, 4);
+        return COM.S_OK;
+    }
+
+    int get_accHelp(int /*long*/ variant, int /*long*/ pszHelp) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default help string from the OS. */
+        String osHelp = null;
+        int code = iaccessible.get_accHelp(variant, pszHelp);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleListeners.size() is 0) return code;
+        if (code is COM.S_OK) {
+            int /*long*/[] pHelp = new int /*long*/[1];
+            COM.MoveMemory(pHelp, pszHelp, OS.PTR_SIZEOF);
+            int size = COM.SysStringByteLen(pHelp[0]);
+            if (size > 0) {
+                char[] buffer = new char[(size + 1) /2];
+                COM.MoveMemory(buffer, pHelp[0], size);
+                osHelp = new String(buffer);
+            }
+        }
+
+        AccessibleEvent event = new AccessibleEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        event.result = osHelp;
+        for (int i = 0; i < accessibleListeners.size(); i++) {
+            AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
+            listener.getHelp(event);
+        }
+        if (event.result is null) return code;
+        char[] data = (event.result + "\0").toCharArray();
+        int /*long*/ ptr = COM.SysAllocString(data);
+        COM.MoveMemory(pszHelp, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
+        return COM.S_OK;
+    }
+
+    int get_accHelpTopic(int /*long*/ pszHelpFile, int /*long*/ variant, int /*long*/ pidTopic) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        int code = iaccessible.get_accHelpTopic(pszHelpFile, variant, pidTopic);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    int get_accKeyboardShortcut(int /*long*/ variant, int /*long*/ pszKeyboardShortcut) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default keyboard shortcut from the OS. */
+        String osKeyboardShortcut = null;
+        int code = iaccessible.get_accKeyboardShortcut(variant, pszKeyboardShortcut);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleListeners.size() is 0) return code;
+        if (code is COM.S_OK) {
+            int /*long*/[] pKeyboardShortcut = new int /*long*/[1];
+            COM.MoveMemory(pKeyboardShortcut, pszKeyboardShortcut, OS.PTR_SIZEOF);
+            int size = COM.SysStringByteLen(pKeyboardShortcut[0]);
+            if (size > 0) {
+                char[] buffer = new char[(size + 1) /2];
+                COM.MoveMemory(buffer, pKeyboardShortcut[0], size);
+                osKeyboardShortcut = new String(buffer);
+            }
+        }
+
+        AccessibleEvent event = new AccessibleEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        event.result = osKeyboardShortcut;
+        for (int i = 0; i < accessibleListeners.size(); i++) {
+            AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
+            listener.getKeyboardShortcut(event);
+        }
+        if (event.result is null) return code;
+        char[] data = (event.result + "\0").toCharArray();
+        int /*long*/ ptr = COM.SysAllocString(data);
+        COM.MoveMemory(pszKeyboardShortcut, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
+        return COM.S_OK;
+    }
+
+    int get_accName(int /*long*/ variant, int /*long*/ pszName) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default name from the OS. */
+        String osName = null;
+        int code = iaccessible.get_accName(variant, pszName);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleListeners.size() is 0) return code;
+        if (code is COM.S_OK) {
+            int /*long*/[] pName = new int /*long*/[1];
+            COM.MoveMemory(pName, pszName, OS.PTR_SIZEOF);
+            int size = COM.SysStringByteLen(pName[0]);
+            if (size > 0) {
+                char[] buffer = new char[(size + 1) /2];
+                COM.MoveMemory(buffer, pName[0], size);
+                osName = new String(buffer);
+            }
+        }
+
+        AccessibleEvent event = new AccessibleEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        event.result = osName;
+        for (int i = 0; i < accessibleListeners.size(); i++) {
+            AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
+            listener.getName(event);
+        }
+        if (event.result is null) return code;
+        char[] data = (event.result + "\0").toCharArray();
+        int /*long*/ ptr = COM.SysAllocString(data);
+        COM.MoveMemory(pszName, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
+        return COM.S_OK;
+    }
+
+    /* get_accParent([out] ppdispParent)
+     * Ownership of ppdispParent transfers from callee to caller so reference count on ppdispParent
+     * must be incremented before returning.  The caller is responsible for releasing ppdispParent.
+     */
+    int get_accParent(int /*long*/ ppdispParent) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        return iaccessible.get_accParent(ppdispParent);
+    }
+
+    int get_accRole(int /*long*/ variant, int /*long*/ pvarRole) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default role from the OS. */
+        int osRole = COM.ROLE_SYSTEM_CLIENT;
+        int code = iaccessible.get_accRole(variant, pvarRole);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        // TEMPORARY CODE - process tree and table even if there are no apps listening
+        if (accessibleControlListeners.size() is 0 && !(control instanceof Tree || control instanceof Table)) return code;
+        if (code is COM.S_OK) {
+            short[] pvt = new short[1];
+            COM.MoveMemory(pvt, pvarRole, 2);
+            if (pvt[0] is COM.VT_I4) {
+                int[] pRole = new int[1];
+                COM.MoveMemory(pRole, pvarRole + 8, 4);
+                osRole = pRole[0];
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        event.detail = osToRole(osRole);
+        // TEMPORARY CODE
+        /* Currently our checkbox table and tree are emulated using state mask
+         * images, so we need to specify 'checkbox' role for the items. */
+        if (v.lVal !is COM.CHILDID_SELF) {
+            if (control instanceof Tree || control instanceof Table) {
+                if ((control.getStyle() & DWT.CHECK) !is 0) event.detail = ACC.ROLE_CHECKBUTTON;
+            }
+        }
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getRole(event);
+        }
+        int role = roleToOs(event.detail);
+        COM.MoveMemory(pvarRole, new short[] { COM.VT_I4 }, 2);
+        COM.MoveMemory(pvarRole + 8, new int[] { role }, 4);
+        return COM.S_OK;
+    }
+
+    /* get_accSelection([out] pvarChildren)
+     * Ownership of pvarChildren transfers from callee to caller so reference count on pvarChildren
+     * must be incremented before returning.  The caller is responsible for releasing pvarChildren.
+     */
+    int get_accSelection(int /*long*/ pvarChildren) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+
+        /* Get the default selection from the OS. */
+        int osChild = ACC.CHILDID_NONE;
+        int code = iaccessible.get_accSelection(pvarChildren);
+        if (accessibleControlListeners.size() is 0) return code;
+        if (code is COM.S_OK) {
+            short[] pvt = new short[1];
+            COM.MoveMemory(pvt, pvarChildren, 2);
+            if (pvt[0] is COM.VT_I4) {
+                int[] pChild = new int[1];
+                COM.MoveMemory(pChild, pvarChildren + 8, 4);
+                osChild = osToChildID(pChild[0]);
+            } else if (pvt[0] is COM.VT_UNKNOWN) {
+                osChild = ACC.CHILDID_MULTIPLE;
+                /* Should get IEnumVARIANT from punkVal field, and enumerate children... */
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osChild;
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getSelection(event);
+        }
+        Accessible accessible = event.accessible;
+        if (accessible !is null) {
+            accessible.AddRef();
+            COM.MoveMemory(pvarChildren, new short[] { COM.VT_DISPATCH }, 2);
+            COM.MoveMemory(pvarChildren + 8, new int /*long*/[] { accessible.objIAccessible.getAddress() }, OS.PTR_SIZEOF);
+            return COM.S_OK;
+        }
+        int childID = event.childID;
+        if (childID is ACC.CHILDID_NONE) {
+            COM.MoveMemory(pvarChildren, new short[] { COM.VT_EMPTY }, 2);
+            return COM.S_FALSE;
+        }
+        if (childID is ACC.CHILDID_MULTIPLE) {
+            AddRef();
+            COM.MoveMemory(pvarChildren, new short[] { COM.VT_UNKNOWN }, 2);
+            COM.MoveMemory(pvarChildren + 8, new int /*long*/[] { objIAccessible.getAddress() }, OS.PTR_SIZEOF);
+            return COM.S_OK;
+        }
+        if (childID is ACC.CHILDID_SELF) {
+            AddRef();
+            COM.MoveMemory(pvarChildren, new short[] { COM.VT_DISPATCH }, 2);
+            COM.MoveMemory(pvarChildren + 8, new int /*long*/[] { objIAccessible.getAddress() }, OS.PTR_SIZEOF);
+            return COM.S_OK;
+        }
+        COM.MoveMemory(pvarChildren, new short[] { COM.VT_I4 }, 2);
+        COM.MoveMemory(pvarChildren + 8, new int[] { childIDToOs(childID) }, 4);
+        return COM.S_OK;
+    }
+
+    int get_accState(int /*long*/ variant, int /*long*/ pvarState) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default state from the OS. */
+        int osState = 0;
+        int code = iaccessible.get_accState(variant, pvarState);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        // TEMPORARY CODE - process tree and table even if there are no apps listening
+        if (accessibleControlListeners.size() is 0 && !(control instanceof Tree || control instanceof Table)) return code;
+        if (code is COM.S_OK) {
+            short[] pvt = new short[1];
+            COM.MoveMemory(pvt, pvarState, 2);
+            if (pvt[0] is COM.VT_I4) {
+                int[] pState = new int[1];
+                COM.MoveMemory(pState, pvarState + 8, 4);
+                osState = pState[0];
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        event.detail = osToState(osState);
+        // TEMPORARY CODE
+        /* Currently our checkbox table and tree are emulated using state mask
+         * images, so we need to determine if the item state is 'checked'. */
+        if (v.lVal !is COM.CHILDID_SELF) {
+            if (control instanceof Tree && (control.getStyle() & DWT.CHECK) !is 0) {
+                int /*long*/ hwnd = control.handle;
+                TVITEM tvItem = new TVITEM ();
+                tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+                tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+                if (OS.COMCTL32_MAJOR >= 6) {
+                    tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_MAPACCIDTOHTREEITEM, v.lVal, 0);
+                } else {
+                    tvItem.hItem = v.lVal;
+                }
+                int /*long*/ result = OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+                bool checked = (result !is 0) && (((tvItem.state >> 12) & 1) is 0);
+                if (checked) event.detail |= ACC.STATE_CHECKED;
+            } else if (control instanceof Table && (control.getStyle() & DWT.CHECK) !is 0) {
+                Table table = (Table) control;
+                TableItem item = table.getItem(event.childID);
+                if (item !is null) {
+                    if (item.getChecked()) event.detail |= ACC.STATE_CHECKED;
+                }
+            }
+        }
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getState(event);
+        }
+        int state = stateToOs(event.detail);
+        COM.MoveMemory(pvarState, new short[] { COM.VT_I4 }, 2);
+        COM.MoveMemory(pvarState + 8, new int[] { state }, 4);
+        return COM.S_OK;
+    }
+
+    int get_accValue(int /*long*/ variant, int /*long*/ pszValue) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT v = new VARIANT();
+        COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default value string from the OS. */
+        String osValue = null;
+        int code = iaccessible.get_accValue(variant, pszValue);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleControlListeners.size() is 0) return code;
+        if (code is COM.S_OK) {
+            int /*long*/[] pValue = new int /*long*/[1];
+            COM.MoveMemory(pValue, pszValue, OS.PTR_SIZEOF);
+            int size = COM.SysStringByteLen(pValue[0]);
+            if (size > 0) {
+                char[] buffer = new char[(size + 1) /2];
+                COM.MoveMemory(buffer, pValue[0], size);
+                osValue = new String(buffer);
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID((int)/*64*/v.lVal);
+        event.result = osValue;
+        for (int i = 0; i < accessibleControlListeners.size(); i++) {
+            AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+            listener.getValue(event);
+        }
+        if (event.result is null) return code;
+        char[] data = (event.result + "\0").toCharArray();
+        int /*long*/ ptr = COM.SysAllocString(data);
+        COM.MoveMemory(pszValue, new int /*long*/[] { ptr }, OS.PTR_SIZEOF);
+        return COM.S_OK;
+    }
+
+    int put_accName(int /*long*/ variant, int /*long*/ szName) {
+        // MSAA: this method is no longer supported
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // We don't implement this. Forward to the proxy.
+        int code = iaccessible.put_accName(variant, szName);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    int put_accValue(int /*long*/ variant, int /*long*/ szValue) {
+        // MSAA: this method is typically only used for edit controls
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // We don't implement this. Forward to the proxy.
+        int code = iaccessible.put_accValue(variant, szValue);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    /* IEnumVARIANT methods: Next, Skip, Reset, Clone */
+    /* Retrieve the next celt items in the enumeration sequence.
+     * If there are fewer than the requested number of elements left
+     * in the sequence, retrieve the remaining elements.
+     * The number of elements actually retrieved is returned in pceltFetched
+     * (unless the caller passed in NULL for that parameter).
+     */
+
+     /* Next([in] celt, [out] rgvar, [in, out] pceltFetched)
+     * Ownership of rgvar transfers from callee to caller so reference count on rgvar
+     * must be incremented before returning.  The caller is responsible for releasing rgvar.
+     */
+    int Next(int celt, int /*long*/ rgvar, int /*long*/ pceltFetched) {
+        /* If there are no listeners, query the proxy for
+         * its IEnumVariant, and get the Next items from it.
+         */
+        if (accessibleControlListeners.size() is 0) {
+            int /*long*/[] ppvObject = new int /*long*/[1];
+            int code = iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
+            if (code !is COM.S_OK) return code;
+            IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
+            int[] celtFetched = new int[1];
+            code = ienumvariant.Next(celt, rgvar, celtFetched);
+            ienumvariant.Release();
+            COM.MoveMemory(pceltFetched, celtFetched, 4);
+            return code;
+        }
+
+        if (rgvar is 0) return COM.E_INVALIDARG;
+        if (pceltFetched is 0 && celt !is 1) return COM.E_INVALIDARG;
+        if (enumIndex is 0) {
+            AccessibleControlEvent event = new AccessibleControlEvent(this);
+            event.childID = ACC.CHILDID_SELF;
+            for (int i = 0; i < accessibleControlListeners.size(); i++) {
+                AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+                listener.getChildren(event);
+            }
+            variants = event.children;
+        }
+        Object[] nextItems = null;
+        if (variants !is null && celt >= 1) {
+            int endIndex = enumIndex + celt - 1;
+            if (endIndex > (variants.length - 1)) endIndex = variants.length - 1;
+            if (enumIndex <= endIndex) {
+                nextItems = new Object[endIndex - enumIndex + 1];
+                for (int i = 0; i < nextItems.length; i++) {
+                    Object child = variants[enumIndex];
+                    if (child instanceof Integer) {
+                        nextItems[i] = new Integer(childIDToOs(((Integer)child).intValue()));
+                    } else {
+                        nextItems[i] = child;
+                    }
+                    enumIndex++;
+                }
+            }
+        }
+        if (nextItems !is null) {
+            for (int i = 0; i < nextItems.length; i++) {
+                Object nextItem = nextItems[i];
+                if (nextItem instanceof Integer) {
+                    int item = ((Integer) nextItem).intValue();
+                    COM.MoveMemory(rgvar + i * 16, new short[] { COM.VT_I4 }, 2);
+                    COM.MoveMemory(rgvar + i * 16 + 8, new int[] { item }, 4);
+                } else {
+                    Accessible accessible = (Accessible) nextItem;
+                    accessible.AddRef();
+                    COM.MoveMemory(rgvar + i * 16, new short[] { COM.VT_DISPATCH }, 2);
+                    COM.MoveMemory(rgvar + i * 16 + 8, new int /*long*/[] { accessible.objIAccessible.getAddress() }, OS.PTR_SIZEOF);
+                }
+            }
+            if (pceltFetched !is 0)
+                COM.MoveMemory(pceltFetched, new int[] {nextItems.length}, 4);
+            if (nextItems.length is celt) return COM.S_OK;
+        } else {
+            if (pceltFetched !is 0)
+                COM.MoveMemory(pceltFetched, new int[] {0}, 4);
+        }
+        return COM.S_FALSE;
+    }
+
+    /* Skip over the specified number of elements in the enumeration sequence. */
+    int Skip(int celt) {
+        /* If there are no listeners, query the proxy
+         * for its IEnumVariant, and tell it to Skip.
+         */
+        if (accessibleControlListeners.size() is 0) {
+            int /*long*/[] ppvObject = new int /*long*/[1];
+            int code = iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
+            if (code !is COM.S_OK) return code;
+            IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
+            code = ienumvariant.Skip(celt);
+            ienumvariant.Release();
+            return code;
+        }
+
+        if (celt < 1 ) return COM.E_INVALIDARG;
+        enumIndex += celt;
+        if (enumIndex > (variants.length - 1)) {
+            enumIndex = variants.length - 1;
+            return COM.S_FALSE;
+        }
+        return COM.S_OK;
+    }
+
+    /* Reset the enumeration sequence to the beginning. */
+    int Reset() {
+        /* If there are no listeners, query the proxy
+         * for its IEnumVariant, and tell it to Reset.
+         */
+        if (accessibleControlListeners.size() is 0) {
+            int /*long*/[] ppvObject = new int /*long*/[1];
+            int code = (int)/*64*/iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
+            if (code !is COM.S_OK) return code;
+            IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
+            code = ienumvariant.Reset();
+            ienumvariant.Release();
+            return code;
+        }
+
+        enumIndex = 0;
+        return COM.S_OK;
+    }
+
+     /* Clone([out] ppEnum)
+     * Ownership of ppEnum transfers from callee to caller so reference count on ppEnum
+     * must be incremented before returning.  The caller is responsible for releasing ppEnum.
+     */
+    int Clone(int /*long*/ ppEnum) {
+        /* If there are no listeners, query the proxy for
+         * its IEnumVariant, and get the Clone from it.
+         */
+        if (accessibleControlListeners.size() is 0) {
+            int /*long*/[] ppvObject = new int /*long*/[1];
+            int code = iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
+            if (code !is COM.S_OK) return code;
+            IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
+            int[] pEnum = new int[1];
+            code = ienumvariant.Clone(pEnum);
+            ienumvariant.Release();
+            COM.MoveMemory(ppEnum, pEnum, 4);
+            return code;
+        }
+
+        if (ppEnum is 0) return COM.E_INVALIDARG;
+        COM.MoveMemory(ppEnum, new int /*long*/[] { objIEnumVARIANT.getAddress()}, OS.PTR_SIZEOF);
+        AddRef();
+        return COM.S_OK;
+    }
+
+    int childIDToOs(int childID) {
+        if (childID is ACC.CHILDID_SELF) return COM.CHILDID_SELF;
+        /*
+        * Feature of Windows:
+        * In Windows XP, tree item ids are 1-based indices. Previous versions
+        * of Windows use the tree item handle for the accessible child ID.
+        * For backward compatibility, we still take a handle childID for tree
+        * items on XP. All other childIDs are 1-based indices.
+        */
+        if (!(control instanceof Tree)) return childID + 1;
+        if (OS.COMCTL32_MAJOR < 6) return childID;
+        return (int)/*64*/OS.SendMessage (control.handle, OS.TVM_MAPHTREEITEMTOACCID, childID, 0);
+    }
+
+    int osToChildID(int osChildID) {
+        if (osChildID is COM.CHILDID_SELF) return ACC.CHILDID_SELF;
+        /*
+        * Feature of Windows:
+        * In Windows XP, tree item ids are 1-based indices. Previous versions
+        * of Windows use the tree item handle for the accessible child ID.
+        * For backward compatibility, we still take a handle childID for tree
+        * items on XP. All other childIDs are 1-based indices.
+        */
+        if (!(control instanceof Tree)) return osChildID - 1;
+        if (OS.COMCTL32_MAJOR < 6) return osChildID;
+        return (int)/*64*/OS.SendMessage (control.handle, OS.TVM_MAPACCIDTOHTREEITEM, osChildID, 0);
+    }
+
+    int stateToOs(int state) {
+        int osState = 0;
+        if ((state & ACC.STATE_SELECTED) !is 0) osState |= COM.STATE_SYSTEM_SELECTED;
+        if ((state & ACC.STATE_SELECTABLE) !is 0) osState |= COM.STATE_SYSTEM_SELECTABLE;
+        if ((state & ACC.STATE_MULTISELECTABLE) !is 0) osState |= COM.STATE_SYSTEM_MULTISELECTABLE;
+        if ((state & ACC.STATE_FOCUSED) !is 0) osState |= COM.STATE_SYSTEM_FOCUSED;
+        if ((state & ACC.STATE_FOCUSABLE) !is 0) osState |= COM.STATE_SYSTEM_FOCUSABLE;
+        if ((state & ACC.STATE_PRESSED) !is 0) osState |= COM.STATE_SYSTEM_PRESSED;
+        if ((state & ACC.STATE_CHECKED) !is 0) osState |= COM.STATE_SYSTEM_CHECKED;
+        if ((state & ACC.STATE_EXPANDED) !is 0) osState |= COM.STATE_SYSTEM_EXPANDED;
+        if ((state & ACC.STATE_COLLAPSED) !is 0) osState |= COM.STATE_SYSTEM_COLLAPSED;
+        if ((state & ACC.STATE_HOTTRACKED) !is 0) osState |= COM.STATE_SYSTEM_HOTTRACKED;
+        if ((state & ACC.STATE_BUSY) !is 0) osState |= COM.STATE_SYSTEM_BUSY;
+        if ((state & ACC.STATE_READONLY) !is 0) osState |= COM.STATE_SYSTEM_READONLY;
+        if ((state & ACC.STATE_INVISIBLE) !is 0) osState |= COM.STATE_SYSTEM_INVISIBLE;
+        if ((state & ACC.STATE_OFFSCREEN) !is 0) osState |= COM.STATE_SYSTEM_OFFSCREEN;
+        if ((state & ACC.STATE_SIZEABLE) !is 0) osState |= COM.STATE_SYSTEM_SIZEABLE;
+        if ((state & ACC.STATE_LINKED) !is 0) osState |= COM.STATE_SYSTEM_LINKED;
+        return osState;
+    }
+
+    int osToState(int osState) {
+        int state = ACC.STATE_NORMAL;
+        if ((osState & COM.STATE_SYSTEM_SELECTED) !is 0) state |= ACC.STATE_SELECTED;
+        if ((osState & COM.STATE_SYSTEM_SELECTABLE) !is 0) state |= ACC.STATE_SELECTABLE;
+        if ((osState & COM.STATE_SYSTEM_MULTISELECTABLE) !is 0) state |= ACC.STATE_MULTISELECTABLE;
+        if ((osState & COM.STATE_SYSTEM_FOCUSED) !is 0) state |= ACC.STATE_FOCUSED;
+        if ((osState & COM.STATE_SYSTEM_FOCUSABLE) !is 0) state |= ACC.STATE_FOCUSABLE;
+        if ((osState & COM.STATE_SYSTEM_PRESSED) !is 0) state |= ACC.STATE_PRESSED;
+        if ((osState & COM.STATE_SYSTEM_CHECKED) !is 0) state |= ACC.STATE_CHECKED;
+        if ((osState & COM.STATE_SYSTEM_EXPANDED) !is 0) state |= ACC.STATE_EXPANDED;
+        if ((osState & COM.STATE_SYSTEM_COLLAPSED) !is 0) state |= ACC.STATE_COLLAPSED;
+        if ((osState & COM.STATE_SYSTEM_HOTTRACKED) !is 0) state |= ACC.STATE_HOTTRACKED;
+        if ((osState & COM.STATE_SYSTEM_BUSY) !is 0) state |= ACC.STATE_BUSY;
+        if ((osState & COM.STATE_SYSTEM_READONLY) !is 0) state |= ACC.STATE_READONLY;
+        if ((osState & COM.STATE_SYSTEM_INVISIBLE) !is 0) state |= ACC.STATE_INVISIBLE;
+        if ((osState & COM.STATE_SYSTEM_OFFSCREEN) !is 0) state |= ACC.STATE_OFFSCREEN;
+        if ((osState & COM.STATE_SYSTEM_SIZEABLE) !is 0) state |= ACC.STATE_SIZEABLE;
+        if ((osState & COM.STATE_SYSTEM_LINKED) !is 0) state |= ACC.STATE_LINKED;
+        return state;
+    }
+
+    int roleToOs(int role) {
+        switch (role) {
+            case ACC.ROLE_CLIENT_AREA: return COM.ROLE_SYSTEM_CLIENT;
+            case ACC.ROLE_WINDOW: return COM.ROLE_SYSTEM_WINDOW;
+            case ACC.ROLE_MENUBAR: return COM.ROLE_SYSTEM_MENUBAR;
+            case ACC.ROLE_MENU: return COM.ROLE_SYSTEM_MENUPOPUP;
+            case ACC.ROLE_MENUITEM: return COM.ROLE_SYSTEM_MENUITEM;
+            case ACC.ROLE_SEPARATOR: return COM.ROLE_SYSTEM_SEPARATOR;
+            case ACC.ROLE_TOOLTIP: return COM.ROLE_SYSTEM_TOOLTIP;
+            case ACC.ROLE_SCROLLBAR: return COM.ROLE_SYSTEM_SCROLLBAR;
+            case ACC.ROLE_DIALOG: return COM.ROLE_SYSTEM_DIALOG;
+            case ACC.ROLE_LABEL: return COM.ROLE_SYSTEM_STATICTEXT;
+            case ACC.ROLE_PUSHBUTTON: return COM.ROLE_SYSTEM_PUSHBUTTON;
+            case ACC.ROLE_CHECKBUTTON: return COM.ROLE_SYSTEM_CHECKBUTTON;
+            case ACC.ROLE_RADIOBUTTON: return COM.ROLE_SYSTEM_RADIOBUTTON;
+            case ACC.ROLE_COMBOBOX: return COM.ROLE_SYSTEM_COMBOBOX;
+            case ACC.ROLE_TEXT: return COM.ROLE_SYSTEM_TEXT;
+            case ACC.ROLE_TOOLBAR: return COM.ROLE_SYSTEM_TOOLBAR;
+            case ACC.ROLE_LIST: return COM.ROLE_SYSTEM_LIST;
+            case ACC.ROLE_LISTITEM: return COM.ROLE_SYSTEM_LISTITEM;
+            case ACC.ROLE_TABLE: return COM.ROLE_SYSTEM_TABLE;
+            case ACC.ROLE_TABLECELL: return COM.ROLE_SYSTEM_CELL;
+            case ACC.ROLE_TABLECOLUMNHEADER: return COM.ROLE_SYSTEM_COLUMNHEADER;
+            case ACC.ROLE_TABLEROWHEADER: return COM.ROLE_SYSTEM_ROWHEADER;
+            case ACC.ROLE_TREE: return COM.ROLE_SYSTEM_OUTLINE;
+            case ACC.ROLE_TREEITEM: return COM.ROLE_SYSTEM_OUTLINEITEM;
+            case ACC.ROLE_TABFOLDER: return COM.ROLE_SYSTEM_PAGETABLIST;
+            case ACC.ROLE_TABITEM: return COM.ROLE_SYSTEM_PAGETAB;
+            case ACC.ROLE_PROGRESSBAR: return COM.ROLE_SYSTEM_PROGRESSBAR;
+            case ACC.ROLE_SLIDER: return COM.ROLE_SYSTEM_SLIDER;
+            case ACC.ROLE_LINK: return COM.ROLE_SYSTEM_LINK;
+        }
+        return COM.ROLE_SYSTEM_CLIENT;
+    }
+
+    int osToRole(int osRole) {
+        switch (osRole) {
+            case COM.ROLE_SYSTEM_CLIENT: return ACC.ROLE_CLIENT_AREA;
+            case COM.ROLE_SYSTEM_WINDOW: return ACC.ROLE_WINDOW;
+            case COM.ROLE_SYSTEM_MENUBAR: return ACC.ROLE_MENUBAR;
+            case COM.ROLE_SYSTEM_MENUPOPUP: return ACC.ROLE_MENU;
+            case COM.ROLE_SYSTEM_MENUITEM: return ACC.ROLE_MENUITEM;
+            case COM.ROLE_SYSTEM_SEPARATOR: return ACC.ROLE_SEPARATOR;
+            case COM.ROLE_SYSTEM_TOOLTIP: return ACC.ROLE_TOOLTIP;
+            case COM.ROLE_SYSTEM_SCROLLBAR: return ACC.ROLE_SCROLLBAR;
+            case COM.ROLE_SYSTEM_DIALOG: return ACC.ROLE_DIALOG;
+            case COM.ROLE_SYSTEM_STATICTEXT: return ACC.ROLE_LABEL;
+            case COM.ROLE_SYSTEM_PUSHBUTTON: return ACC.ROLE_PUSHBUTTON;
+            case COM.ROLE_SYSTEM_CHECKBUTTON: return ACC.ROLE_CHECKBUTTON;
+            case COM.ROLE_SYSTEM_RADIOBUTTON: return ACC.ROLE_RADIOBUTTON;
+            case COM.ROLE_SYSTEM_COMBOBOX: return ACC.ROLE_COMBOBOX;
+            case COM.ROLE_SYSTEM_TEXT: return ACC.ROLE_TEXT;
+            case COM.ROLE_SYSTEM_TOOLBAR: return ACC.ROLE_TOOLBAR;
+            case COM.ROLE_SYSTEM_LIST: return ACC.ROLE_LIST;
+            case COM.ROLE_SYSTEM_LISTITEM: return ACC.ROLE_LISTITEM;
+            case COM.ROLE_SYSTEM_TABLE: return ACC.ROLE_TABLE;
+            case COM.ROLE_SYSTEM_CELL: return ACC.ROLE_TABLECELL;
+            case COM.ROLE_SYSTEM_COLUMNHEADER: return ACC.ROLE_TABLECOLUMNHEADER;
+            case COM.ROLE_SYSTEM_ROWHEADER: return ACC.ROLE_TABLEROWHEADER;
+            case COM.ROLE_SYSTEM_OUTLINE: return ACC.ROLE_TREE;
+            case COM.ROLE_SYSTEM_OUTLINEITEM: return ACC.ROLE_TREEITEM;
+            case COM.ROLE_SYSTEM_PAGETABLIST: return ACC.ROLE_TABFOLDER;
+            case COM.ROLE_SYSTEM_PAGETAB: return ACC.ROLE_TABITEM;
+            case COM.ROLE_SYSTEM_PROGRESSBAR: return ACC.ROLE_PROGRESSBAR;
+            case COM.ROLE_SYSTEM_SLIDER: return ACC.ROLE_SLIDER;
+            case COM.ROLE_SYSTEM_LINK: return ACC.ROLE_LINK;
+        }
+        return ACC.ROLE_CLIENT_AREA;
+    }
+
+    /* checkWidget was copied from Widget, and rewritten to work in this package */
+    void checkWidget () {
+        if (!isValidThread ()) DWT.error (DWT.ERROR_THREAD_INVALID_ACCESS);
+        if (control.isDisposed ()) DWT.error (DWT.ERROR_WIDGET_DISPOSED);
+    }
+
+    /* isValidThread was copied from Widget, and rewritten to work in this package */
+    bool isValidThread () {
+        return control.getDisplay ().getThread () is Thread.currentThread ();
+    }
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/AccessibleAdapter.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.AccessibleAdapter;
+
+import dwt.accessibility.AccessibleListener;
+import dwt.accessibility.AccessibleEvent;
+
+/**
+ * This adapter class provides default implementations for the
+ * methods described by the <code>AccessibleListener</code> interface.
+ * <p>
+ * Classes that wish to deal with <code>AccessibleEvent</code>s can
+ * extend this class and override only the methods that they are
+ * interested in.
+ * </p><p>
+ * Note: Accessibility clients use child identifiers to specify
+ * whether they want information about a control or one of its children.
+ * Child identifiers are increasing integers beginning with 0.
+ * The identifier CHILDID_SELF represents the control itself.
+ * </p>
+ *
+ * @see AccessibleListener
+ * @see AccessibleEvent
+ *
+ * @since 2.0
+ */
+public abstract class AccessibleAdapter : AccessibleListener {
+
+    /**
+     * Sent when an accessibility client requests the name
+     * of the control, or the name of a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return the name of the control or specified child in the
+     * <code>result</code> field of the event object. Returning
+     * an empty string tells the client that the control or child
+     * does not have a name, and returning null tells the client
+     * to use the platform name.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested name string, or null</li>
+     * </ul>
+     */
+    public void getName(AccessibleEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the help string
+     * of the control, or the help string of a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * The information in this property should be similar to the help
+     * provided by toolTipText. It describes what the control or child
+     * does or how to use it, as opposed to getDescription, which
+     * describes appearance.
+     * </p><p>
+     * Return the help string of the control or specified child in
+     * the <code>result</code> field of the event object. Returning
+     * an empty string tells the client that the control or child
+     * does not have a help string, and returning null tells the
+     * client to use the platform help string.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested help string, or null</li>
+     * </ul>
+     */
+    public void getHelp(AccessibleEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the keyboard shortcut
+     * of the control, or the keyboard shortcut of a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * A keyboard shortcut can either be a mnemonic, or an accelerator.
+     * As a general rule, if the control or child can receive keyboard focus,
+     * then you should expose its mnemonic, and if it cannot receive keyboard
+     * focus, then you should expose its accelerator.
+     * </p><p>
+     * Return the keyboard shortcut string of the control or specified child
+     * in the <code>result</code> field of the event object. Returning an
+     * empty string tells the client that the control or child does not
+     * have a keyboard shortcut string, and returning null tells the client
+     * to use the platform keyboard shortcut string.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested keyboard shortcut string (example: "ALT+N"), or null</li>
+     * </ul>
+     */
+    public void getKeyboardShortcut(AccessibleEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests a description
+     * of the control, or a description of a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * This is a textual description of the control or child's visual
+     * appearance, which is typically only necessary if it cannot be
+     * determined from other properties such as role.
+     * </p><p>
+     * Return the description of the control or specified child in
+     * the <code>result</code> field of the event object. Returning
+     * an empty string tells the client that the control or child
+     * does not have a description, and returning null tells the
+     * client to use the platform description.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested description string, or null</li>
+     * </ul>
+     */
+    public void getDescription(AccessibleEvent e) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/AccessibleControlAdapter.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.AccessibleControlAdapter;
+
+import dwt.accessibility.AccessibleControlListener;
+import dwt.accessibility.AccessibleControlEvent;
+
+/**
+ * This adapter class provides default implementations for the
+ * methods described by the <code>AccessibleControlListener</code> interface.
+ * <p>
+ * Classes that wish to deal with <code>AccessibleControlEvent</code>s can
+ * extend this class and override only the methods that they are
+ * interested in.
+ * </p><p>
+ * Note: Accessibility clients use child identifiers to specify
+ * whether they want information about a control or one of its children.
+ * Child identifiers are increasing integers beginning with 0.
+ * The identifier CHILDID_SELF represents the control itself.
+ * When returning a child identifier to a client, you may use CHILDID_NONE
+ * to indicate that no child or control has the required information.
+ * </p><p>
+ * Note: This adapter is typically used by implementors of
+ * a custom control to provide very detailed information about
+ * the control instance to accessibility clients.
+ * </p>
+ *
+ * @see AccessibleControlListener
+ * @see AccessibleControlEvent
+ *
+ * @since 2.0
+ */
+public abstract class AccessibleControlAdapter : AccessibleControlListener {
+
+    /**
+     * Sent when an accessibility client requests the identifier
+     * of the control child at the specified display coordinates.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return the identifier of the child at display point (x, y)
+     * in the <code>childID</code> field of the event object.
+     * Return CHILDID_SELF if point (x, y) is in the control itself
+     * and not in any child. Return CHILDID_NONE if point (x, y)
+     * is not contained in either the control or any of its children.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>x, y [IN] - the specified point in display coordinates</li>
+     *    <li>childID [Typical OUT] - the ID of the child at point, or CHILDID_SELF, or CHILDID_NONE</li>
+     *    <li>accessible [Optional OUT] - the accessible object for the control or child may be returned instead of the childID</li>
+     * </ul>
+     */
+    public void getChildAtPoint(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the location
+     * of the control, or the location of a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return a rectangle describing the location of the specified
+     * control or child in the <code>x, y, width, and height</code>
+     * fields of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>x, y, width, height [OUT] - the control or child location in display coordinates</li>
+     * </ul>
+     */
+    public void getLocation(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the accessible object
+     * for a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return an <code>Accessible</code> for the specified control or
+     * child in the <code>accessible</code> field of the event object.
+     * Return null if the specified child does not have its own
+     * <code>Accessible</code>.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying a child of the control</li>
+     *    <li>accessible [OUT] - an Accessible for the specified childID, or null if one does not exist</li>
+     * </ul>
+     */
+    public void getChild(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the number of
+     * children in the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return the number of child items in the <code>detail</code>
+     * field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>detail [OUT] - the number of child items in this control</li>
+     * </ul>
+     */
+    public void getChildCount(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the default action
+     * of the control, or the default action of a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * This string is typically a verb describing what the user does to it.
+     * For example, a Push Button's default action is "Press", a Check Button's
+     * is "Check" or "UnCheck", and List items have the default action "Double Click".
+     * </p><p>
+     * Return a string describing the default action of the specified
+     * control or child in the <code>result</code> field of the event object.
+     * Returning null tells the client to use the platform default action string.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested default action string, or null</li>
+     * </ul>
+     */
+    public void getDefaultAction(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the identity of
+     * the child or control that has keyboard focus.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return the identifier of the child that has focus in the
+     * <code>childID</code> field of the event object.
+     * Return CHILDID_SELF if the control itself has keyboard focus.
+     * Return CHILDID_NONE if neither the control nor any of its children has focus.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [Typical OUT] - the ID of the child with focus, or CHILDID_SELF, or CHILDID_NONE</li>
+     *    <li>accessible [Optional OUT] - the accessible object for a child may be returned instead of its childID</li>
+     * </ul>
+     */
+    public void getFocus(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the role
+     * of the control, or the role of a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return a role constant (constant defined in ACC beginning with ROLE_)
+     * that describes the role of the specified control or child in the
+     * <code>detail</code> field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>detail [OUT] - a role constant describing the role of the control or child</li>
+     * </ul>
+     */
+    public void getRole(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the identity of
+     * the child or control that is currently selected.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return the identifier of the selected child in the
+     * <code>childID</code> field of the event object.
+     * Return CHILDID_SELF if the control itself is selected.
+     * Return CHILDID_MULTIPLE if multiple children are selected, and return an array of childIDs in the <code>children</code> field.
+     * Return CHILDID_NONE if neither the control nor any of its children are selected.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [Typical OUT] - the ID of the selected child, or CHILDID_SELF, or CHILDID_MULTIPLE, or CHILDID_NONE</li>
+     *    <li>accessible [Optional OUT] - the accessible object for the control or child may be returned instead of the childID</li>
+     * </ul>
+     */
+    public void getSelection(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the state
+     * of the control, or the state of a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return a state mask (mask bit constants defined in ACC beginning with STATE_)
+     * that describes the current state of the specified control or child in the
+     * <code>detail</code> field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>detail [OUT] - a state mask describing the current state of the control or child</li>
+     * </ul>
+     */
+    public void getState(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the value
+     * of the control, or the value of a child of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * Many controls do not return a value. Examples of controls
+     * that do are: Combo returns the text string, Text returns
+     * its contents, ProgressBar returns a string representing a
+     * percentage, and Tree items return a string representing
+     * their level in the tree.
+     * </p><p>
+     * Return a string describing the value of the specified control
+     * or child in the <code>result</code> field of the event object.
+     * Returning null tells the client to use the platform value string.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested value string, or null</li>
+     * </ul>
+     */
+    public void getValue(AccessibleControlEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the children of the control.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return the children as an array of childIDs in the <code>children</code>
+     * field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>children [Typical OUT] - an array of childIDs</li>
+     *    <li>accessible [Optional OUT] - an array of accessible objects for the children may be returned instead of the childIDs</li>
+     * </ul>
+     */
+    public void getChildren(AccessibleControlEvent e) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/AccessibleControlEvent.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.AccessibleControlEvent;
+
+import dwt.accessibility.Accessible;
+
+import dwt.internal.DWTEventObject;
+import tango.text.convert.Format;
+
+/**
+ * Instances of this class are sent as a result of
+ * accessibility clients sending messages to controls
+ * asking for detailed information about the implementation
+ * of the control instance. Typically, only implementors
+ * of custom controls need to listen for this event.
+ * <p>
+ * Note: The meaning of each field depends on the
+ * message that was sent.
+ * </p>
+ *
+ * @see AccessibleControlListener
+ * @see AccessibleControlAdapter
+ *
+ * @since 2.0
+ */
+public class AccessibleControlEvent : DWTEventObject {
+    public int childID;         // IN/OUT
+    public Accessible accessible;   // OUT
+    public int x, y;                // IN/OUT
+    public int width, height;       // OUT
+    public int detail;          // IN/OUT
+    public char[] result;           // OUT
+    public Object children[];       // [OUT]
+
+    //static final long serialVersionUID = 3257281444169529141L;
+
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param source the object that fired the event
+ */
+public this(Object source) {
+    super(source);
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the event
+ */
+public char[] toString () {
+    return Format( "AccessibleControlEvent {childID={} accessible={} x={} y={} width={} heigth={} detail={} result={}",
+        childID, accessible, x, y, width, height, detail, result);
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/AccessibleControlListener.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,235 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.AccessibleControlListener;
+
+import dwt.accessibility.AccessibleControlEvent;
+import dwt.internal.DWTEventListener;
+
+/**
+ * Classes that implement this interface provide methods
+ * that deal with the events that are generated when an
+ * accessibility client sends a message to a control.
+ * <p>
+ * After creating an instance of a class that implements
+ * this interface it can be added to a control using the
+ * <code>addAccessibleControlListener</code> method and removed
+ * using the <code>removeAccessibleControlListener</code> method.
+ * When a client requests information the appropriate method
+ * will be invoked.
+ * </p><p>
+ * Note: Accessibility clients use child identifiers to specify
+ * whether they want information about a control or one of its children.
+ * Child identifiers are increasing integers beginning with 0.
+ * The identifier CHILDID_SELF represents the control itself.
+ * </p><p>
+ * Note: This interface is typically used by implementors of
+ * a custom control to provide very detailed information about
+ * the control instance to accessibility clients.
+ * </p>
+ *
+ * @see AccessibleControlAdapter
+ * @see AccessibleControlEvent
+ *
+ * @since 2.0
+ */
+public interface AccessibleControlListener : DWTEventListener {
+
+    /**
+     * Sent when an accessibility client requests the identifier
+     * of the control child at the specified display coordinates.
+     * <p>
+     * Return the identifier of the child at display point (x, y)
+     * in the <code>childID</code> field of the event object.
+     * Return CHILDID_SELF if point (x, y) is in the control itself
+     * and not in any child. Return CHILDID_NONE if point (x, y)
+     * is not contained in either the control or any of its children.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>x, y [IN] - the specified point in display coordinates</li>
+     *    <li>childID [Typical OUT] - the ID of the child at point, or CHILDID_SELF, or CHILDID_NONE</li>
+     *    <li>accessible [Optional OUT] - the accessible object for the control or child may be returned instead of the childID</li>
+     * </ul>
+     */
+    public void getChildAtPoint(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the location
+     * of the control, or the location of a child of the control.
+     * <p>
+     * Return a rectangle describing the location of the specified
+     * control or child in the <code>x, y, width, and height</code>
+     * fields of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>x, y, width, height [OUT] - the control or child location in display coordinates</li>
+     * </ul>
+     */
+    public void getLocation(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the accessible object
+     * for a child of the control.
+     * <p>
+     * Return an <code>Accessible</code> for the specified control or
+     * child in the <code>accessible</code> field of the event object.
+     * Return null if the specified child does not have its own
+     * <code>Accessible</code>.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying a child of the control</li>
+     *    <li>accessible [OUT] - an Accessible for the specified childID, or null if one does not exist</li>
+     * </ul>
+     */
+    public void getChild(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the number of
+     * children in the control.
+     * <p>
+     * Return the number of child items in the <code>detail</code>
+     * field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>detail [OUT] - the number of child items in this control</li>
+     * </ul>
+     */
+    public void getChildCount(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the default action
+     * of the control, or the default action of a child of the control.
+     * <p>
+     * This string is typically a verb describing what the user does to it.
+     * For example, a Push Button's default action is "Press", a Check Button's
+     * is "Check" or "UnCheck", and List items have the default action "Double Click".
+     * </p><p>
+     * Return a string describing the default action of the specified
+     * control or child in the <code>result</code> field of the event object.
+     * Returning null tells the client to use the platform default action string.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested default action string, or null</li>
+     * </ul>
+     */
+    public void getDefaultAction(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the identity of
+     * the child or control that has keyboard focus.
+     * <p>
+     * Return the identifier of the child that has focus in the
+     * <code>childID</code> field of the event object.
+     * Return CHILDID_SELF if the control itself has keyboard focus.
+     * Return CHILDID_NONE if neither the control nor any of its children has focus.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [Typical OUT] - the ID of the child with focus, or CHILDID_SELF, or CHILDID_NONE</li>
+     *    <li>accessible [Optional OUT] - the accessible object for a child may be returned instead of its childID</li>
+     * </ul>
+     */
+    public void getFocus(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the role
+     * of the control, or the role of a child of the control.
+     * <p>
+     * Return a role constant (constant defined in ACC beginning with ROLE_)
+     * that describes the role of the specified control or child in the
+     * <code>detail</code> field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>detail [OUT] - a role constant describing the role of the control or child</li>
+     * </ul>
+     */
+    public void getRole(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the identity of
+     * the child or control that is currently selected.
+     * <p>
+     * Return the identifier of the selected child in the
+     * <code>childID</code> field of the event object.
+     * Return CHILDID_SELF if the control itself is selected.
+     * Return CHILDID_MULTIPLE if multiple children are selected, and return an array of childIDs in the <code>children</code> field.
+     * Return CHILDID_NONE if neither the control nor any of its children are selected.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [Typical OUT] - the ID of the selected child, or CHILDID_SELF, or CHILDID_MULTIPLE, or CHILDID_NONE</li>
+     *    <li>accessible [Optional OUT] - the accessible object for the control or child may be returned instead of the childID</li>
+     * </ul>
+     */
+    public void getSelection(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the state
+     * of the control, or the state of a child of the control.
+     * <p>
+     * Return a state mask (mask bit constants defined in ACC beginning with STATE_)
+     * that describes the current state of the specified control or child in the
+     * <code>detail</code> field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>detail [OUT] - a state mask describing the current state of the control or child</li>
+     * </ul>
+     */
+    public void getState(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the value
+     * of the control, or the value of a child of the control.
+     * <p>
+     * Many controls do not return a value. Examples of controls
+     * that do are: Combo returns the text string, Text returns
+     * its contents, ProgressBar returns a string representing a
+     * percentage, and Tree items return a string representing
+     * their level in the tree.
+     * </p><p>
+     * Return a string describing the value of the specified control
+     * or child in the <code>result</code> field of the event object.
+     * Returning null tells the client to use the platform value string.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested value string, or null</li>
+     * </ul>
+     */
+    public void getValue(AccessibleControlEvent e);
+
+    /**
+     * Sent when an accessibility client requests the children of the control.
+     * <p>
+     * Return the children as an array of childIDs in the <code>children</code>
+     * field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>children [Typical OUT] - an array of childIDs</li>
+     *    <li>children [Optional OUT] - an array of accessible objects for the children may be returned instead of the childIDs</li>
+     * </ul>
+     */
+    public void getChildren(AccessibleControlEvent e);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/AccessibleEvent.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.AccessibleEvent;
+
+
+import dwt.internal.DWTEventObject;
+import tango.text.convert.Format;
+
+/**
+ * Instances of this class are sent as a result of
+ * accessibility clients sending messages to controls
+ * asking for information about the control instance.
+ * <p>
+ * Note: The meaning of the result field depends
+ * on the message that was sent.
+ * </p>
+ *
+ * @see AccessibleListener
+ * @see AccessibleAdapter
+ *
+ * @since 2.0
+ */
+public class AccessibleEvent : DWTEventObject {
+    /**
+     * The value of this field is set by an accessibility client
+     * before the accessible listener method is called.
+     * ChildID can be CHILDID_SELF, representing the control itself,
+     * or a 0-based integer representing a specific child of the control.
+     */
+    public int childID;
+
+    /**
+     * The value of this field must be set in the accessible listener
+     * method before returning.
+     * What to set it to depends on the listener method called, and
+     * the childID specified by the client.
+     */
+    public char[] result;
+
+    //static final long serialVersionUID = 3257567304224026934L;
+
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param source the object that fired the event
+ */
+public this(Object source) {
+    super(source);
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the event
+ */
+public char[] toString () {
+    return Format( "AccessibleEvent {childID={} result={}}", childID, result );
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/AccessibleListener.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.AccessibleListener;
+
+
+import dwt.internal.DWTEventListener;
+import dwt.accessibility.AccessibleEvent;
+
+/**
+ * Classes that implement this interface provide methods
+ * that deal with the events that are generated when an
+ * accessibility client sends a message to a control.
+ * <p>
+ * After creating an instance of a class that implements
+ * this interface it can be added to a control using the
+ * <code>addAccessibleListener</code> method and removed
+ * using the <code>removeAccessibleListener</code> method.
+ * When a client requests information, the appropriate method
+ * will be invoked.
+ * </p><p>
+ * Note: Accessibility clients use child identifiers to specify
+ * whether they want information about a control or one of its children.
+ * Child identifiers are increasing integers beginning with 0.
+ * The identifier CHILDID_SELF represents the control itself.
+ * </p>
+ *
+ * @see AccessibleAdapter
+ * @see AccessibleEvent
+ *
+ * @since 2.0
+ */
+public interface AccessibleListener : DWTEventListener {
+
+    /**
+     * Sent when an accessibility client requests the name
+     * of the control, or the name of a child of the control.
+     * <p>
+     * Return the name of the control or specified child in the
+     * <code>result</code> field of the event object. Returning
+     * an empty string tells the client that the control or child
+     * does not have a name, and returning null tells the client
+     * to use the platform name.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested name string, or null</li>
+     * </ul>
+     */
+    public void getName(AccessibleEvent e);
+
+    /**
+     * Sent when an accessibility client requests the help string
+     * of the control, or the help string of a child of the control.
+     * <p>
+     * The information in this property should be similar to the help
+     * provided by toolTipText. It describes what the control or child
+     * does or how to use it, as opposed to getDescription, which
+     * describes appearance.
+     * </p><p>
+     * Return the help string of the control or specified child in
+     * the <code>result</code> field of the event object. Returning
+     * an empty string tells the client that the control or child
+     * does not have a help string, and returning null tells the
+     * client to use the platform help string.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested help string, or null</li>
+     * </ul>
+     */
+    public void getHelp(AccessibleEvent e);
+
+    /**
+     * Sent when an accessibility client requests the keyboard shortcut
+     * of the control, or the keyboard shortcut of a child of the control.
+     * <p>
+     * A keyboard shortcut can either be a mnemonic, or an accelerator.
+     * As a general rule, if the control or child can receive keyboard focus,
+     * then you should expose its mnemonic, and if it cannot receive keyboard
+     * focus, then you should expose its accelerator.
+     * </p><p>
+     * Return the keyboard shortcut string of the control or specified child
+     * in the <code>result</code> field of the event object. Returning an
+     * empty string tells the client that the control or child does not
+     * have a keyboard shortcut string, and returning null tells the client
+     * to use the platform keyboard shortcut string.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested keyboard shortcut string (example: "ALT+N"), or null</li>
+     * </ul>
+     */
+    public void getKeyboardShortcut(AccessibleEvent e);
+
+    /**
+     * Sent when an accessibility client requests a description
+     * of the control, or a description of a child of the control.
+     * <p>
+     * This is a textual description of the control or child's visual
+     * appearance, which is typically only necessary if it cannot be
+     * determined from other properties such as role.
+     * </p><p>
+     * Return the description of the control or specified child in
+     * the <code>result</code> field of the event object. Returning
+     * an empty string tells the client that the control or child
+     * does not have a description, and returning null tells the
+     * client to use the platform description.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying the control or one of its children</li>
+     *    <li>result [OUT] - the requested description string, or null</li>
+     * </ul>
+     */
+    public void getDescription(AccessibleEvent e);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/AccessibleTextAdapter.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.AccessibleTextAdapter;
+
+import dwt.accessibility.AccessibleTextEvent;
+import dwt.accessibility.AccessibleTextListener;
+
+/**
+ * This adapter class provides default implementations for the
+ * methods described by the <code>AccessibleTextListener</code> interface.
+ * <p>
+ * Classes that wish to deal with <code>AccessibleTextEvent</code>s can
+ * extend this class and override only the methods that they are
+ * interested in.
+ * </p><p>
+ * Note: Accessibility clients use child identifiers to specify
+ * whether they want information about a control or one of its children.
+ * Child identifiers are increasing integers beginning with 0.
+ * The identifier CHILDID_SELF represents the control itself.
+ * When returning a child identifier to a client, you may use CHILDID_NONE
+ * to indicate that no child or control has the required information.
+ * </p><p>
+ * Note: This adapter is typically used by implementors of
+ * a custom control to provide very detailed information about
+ * the control instance to accessibility clients.
+ * </p>
+ *
+ * @see AccessibleTextListener
+ * @see AccessibleTextEvent
+ *
+ * @since 3.0
+ */
+public abstract class AccessibleTextAdapter : AccessibleTextListener {
+
+    /**
+     * Sent when an accessibility client requests the current character offset
+     * of the text caret.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return the caret offset in the <code>offset</code>
+     * field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying a child of the control</li>
+     *    <li>offset [OUT] - the current offset of the text caret</li>
+     * </ul>
+     */
+    public void getCaretOffset (AccessibleTextEvent e) {
+    }
+
+    /**
+     * Sent when an accessibility client requests the range of the current
+     * text selection.
+     * The default behavior is to do nothing.
+     * <p>
+     * Return the selection start offset and non-negative length in the
+     * <code>offset</code> and <code>length</code> fields of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying a child of the control</li>
+     *    <li>offset [OUT] - the offset of the current text selection</li>
+     *    <li>length [OUT] - the length of the current text selection</li>
+     * </ul>
+     */
+    public void getSelectionRange (AccessibleTextEvent e) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/AccessibleTextEvent.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.AccessibleTextEvent;
+
+
+import dwt.internal.DWTEventObject;
+import tango.text.convert.Format;
+
+/**
+ * Instances of this class are sent as a result of
+ * accessibility clients sending messages to controls
+ * asking for detailed information about the implementation
+ * of the control instance. Typically, only implementors
+ * of custom controls need to listen for this event.
+ * <p>
+ * Note: The meaning of each field depends on the
+ * message that was sent.
+ * </p>
+ *
+ * @see AccessibleTextListener
+ * @see AccessibleTextAdapter
+ *
+ * @since 3.0
+ */
+public class AccessibleTextEvent : DWTEventObject {
+    public int childID;             // IN
+    public int offset, length;      // OUT
+
+    //static final long serialVersionUID = 3977019530868308275L;
+
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param source the object that fired the event
+ */
+public this (Object source) {
+    super (source);
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the event
+ */
+public char[] toString () {
+    return Format( "AccessibleTextEvent {{childID={} offset={} length={}}",
+        childID,
+        offset,
+        length);
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/accessible/AccessibleTextListener.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.accessibility.AccessibleTextListener;
+
+import dwt.accessibility.AccessibleTextEvent;
+
+import dwt.internal.DWTEventListener;
+
+/**
+ * Classes that implement this interface provide methods
+ * that deal with the events that are generated when an
+ * accessibility client sends a message to a control.
+ * <p>
+ * After creating an instance of a class that implements
+ * this interface it can be added to a control using the
+ * <code>addAccessibleTextListener</code> method and removed
+ * using the <code>removeAccessibleTextListener</code> method.
+ * When a client requests information the appropriate method
+ * will be invoked.
+ * </p><p>
+ * Note: Accessibility clients use child identifiers to specify
+ * whether they want information about a control or one of its children.
+ * Child identifiers are increasing integers beginning with 0.
+ * The identifier CHILDID_SELF represents the control itself.
+ * </p><p>
+ * Note: This interface is typically used by implementors of
+ * a custom control to provide very detailed information about
+ * the control instance to accessibility clients.
+ * </p>
+ *
+ * @see AccessibleTextAdapter
+ * @see AccessibleTextEvent
+ *
+ * @since 3.0
+ */
+public interface AccessibleTextListener : DWTEventListener {
+
+    /**
+     * Sent when an accessibility client requests the current character offset
+     * of the text caret.
+     * <p>
+     * Return the caret offset in the <code>offset</code>
+     * field of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying a child of the control</li>
+     *    <li>offset [OUT] - the current offset of the text caret</li>
+     * </ul>
+     */
+    public void getCaretOffset (AccessibleTextEvent e);
+
+    /**
+     * Sent when an accessibility client requests the range of the current
+     * text selection.
+     * <p>
+     * Return the selection start offset and non-negative length in the
+     * <code>offset</code> and <code>length</code> fields of the event object.
+     * </p>
+     *
+     * @param e an event object containing the following fields:<ul>
+     *    <li>childID [IN] - an identifier specifying a child of the control</li>
+     *    <li>offset [OUT] - the offset of the current text selection</li>
+     *    <li>length [OUT] - the length of the current text selection</li>
+     * </ul>
+     */
+    public void getSelectionRange (AccessibleTextEvent e);
+}
--- a/dwt/graphics/TextLayout.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/graphics/TextLayout.d	Mon Jan 28 04:47:28 2008 +0100
@@ -7,6 +7,8 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
  *******************************************************************************/
 module dwt.graphics.TextLayout;
 
--- a/dwt/internal/image/GIFFileFormat.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/GIFFileFormat.d	Mon Jan 28 04:47:28 2008 +0100
@@ -22,9 +22,7 @@
 import dwt.graphics.ImageLoaderEvent;
 import dwt.graphics.ImageLoader;
 import tango.core.Exception;
-
-///FORTING_TYPE
-class Image{}
+import dwt.dwthelper.System;
 
 final class GIFFileFormat : FileFormat {
     char[] signature;
--- a/dwt/internal/image/JPEGDecoder.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/JPEGDecoder.d	Mon Jan 28 04:47:28 2008 +0100
@@ -24,6 +24,7 @@
 import tango.core.Exception;
 import tango.util.Convert;
 import Math = tango.math.Math;
+import dwt.dwthelper.System;
 
 public class JPEGDecoder {
 
--- a/dwt/internal/image/JPEGFileFormat.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/JPEGFileFormat.d	Mon Jan 28 04:47:28 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved.  This source file is made available under the terms contained in the README file
  * accompanying this program.  The README file should be located in the about_files directory of the
@@ -28,6 +28,7 @@
 
 import dwt.graphics.RGB;
 import dwt.graphics.PaletteData;
+import dwt.dwthelper.System;
 
 import tango.core.Exception;
 
--- a/dwt/internal/image/JPEGFrameHeader.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/JPEGFrameHeader.d	Mon Jan 28 04:47:28 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * 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
@@ -17,6 +17,7 @@
 import dwt.internal.image.JPEGVariableSizeSegment;
 import dwt.internal.image.JPEGFileFormat;
 import dwt.internal.image.LEDataInputStream;
+import dwt.dwthelper.System;
 
 final class JPEGFrameHeader : JPEGVariableSizeSegment {
     int maxVFactor;
--- a/dwt/internal/image/JPEGScanHeader.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/JPEGScanHeader.d	Mon Jan 28 04:47:28 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * 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
@@ -16,6 +16,7 @@
 import dwt.internal.image.JPEGVariableSizeSegment;
 import dwt.internal.image.LEDataInputStream;
 import dwt.internal.image.JPEGFileFormat;
+import dwt.dwthelper.System;
 
 final class JPEGScanHeader : JPEGVariableSizeSegment {
     public int[][] componentParameters;
--- a/dwt/internal/image/LZWCodec.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/LZWCodec.d	Mon Jan 28 04:47:28 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * 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
@@ -22,6 +22,7 @@
 import dwt.graphics.ImageLoaderEvent;
 
 import tango.core.Exception;
+import dwt.dwthelper.System;
 
 final class LZWCodec {
     int bitsPerPixel, blockSize, blockIndex, currentByte, bitsLeft,
--- a/dwt/internal/image/OS2BMPFileFormat.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/OS2BMPFileFormat.d	Mon Jan 28 04:47:28 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * 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
@@ -22,6 +22,7 @@
 import dwt.dwthelper.ByteArrayOutputStream;
 
 import tango.core.Exception;
+import dwt.dwthelper.System;
 
 final class OS2BMPFileFormat : FileFormat {
     static final int BMPFileHeaderSize = 14;
--- a/dwt/internal/image/PNGFileFormat.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/PNGFileFormat.d	Mon Jan 28 04:47:28 2008 +0100
@@ -31,6 +31,7 @@
 import dwt.internal.image.PngDecodingDataStream;
 
 import dwt.dwthelper.BufferedInputStream;
+import dwt.dwthelper.System;
 
 import tango.core.Exception;
 
--- a/dwt/internal/image/PngChunk.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/PngChunk.d	Mon Jan 28 04:47:28 2008 +0100
@@ -24,6 +24,7 @@
 
 import tango.core.Exception;
 import tango.text.convert.Format;
+import dwt.dwthelper.System;
 
 class PngChunk {
     byte[] reference;
--- a/dwt/internal/image/PngIdatChunk.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/PngIdatChunk.d	Mon Jan 28 04:47:28 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * 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
@@ -17,6 +17,7 @@
 import dwt.internal.image.PngFileReadState;
 import dwt.internal.image.PngIhdrChunk;
 import dwt.internal.image.PngChunk;
+import dwt.dwthelper.System;
 
 class PngIdatChunk : PngChunk {
 
--- a/dwt/internal/image/TIFFDirectory.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/TIFFDirectory.d	Mon Jan 28 04:47:28 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * 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
@@ -22,9 +22,7 @@
 import dwt.graphics.RGB;
 import dwt.DWT;
 import dwt.dwthelper.Integer;
-
-//PORTING_TYPE
-class Image{}
+import dwt.dwthelper.System;
 
 final class TIFFDirectory {
 
--- a/dwt/internal/image/WinBMPFileFormat.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/WinBMPFileFormat.d	Mon Jan 28 04:47:28 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * 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
@@ -20,6 +20,7 @@
 import dwt.DWT;
 
 import tango.core.Exception;
+import dwt.dwthelper.System;
 
 final class WinBMPFileFormat : FileFormat {
 
--- a/dwt/internal/image/WinICOFileFormat.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/internal/image/WinICOFileFormat.d	Mon Jan 28 04:47:28 2008 +0100
@@ -1,4 +1,4 @@
-/*******************************************************************************
+/*******************************************************************************
  * 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
@@ -18,6 +18,7 @@
 import dwt.DWT;
 
 import tango.core.Exception;
+import dwt.dwthelper.System;
 
 final class WinICOFileFormat : FileFormat {
 
--- a/dwt/layout/FillLayout.d	Mon Jan 28 02:37:23 2008 +0100
+++ b/dwt/layout/FillLayout.d	Mon Jan 28 04:47:28 2008 +0100
@@ -23,6 +23,7 @@
 
 import tango.text.Util;
 import tango.util.Convert;
+import dwt.dwthelper.utils;
 
 /**
  * <code>FillLayout</code> is the simplest layout class. It lays out
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Button.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1270 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Button;
+
+import dwt.widgets.Control;
+
+class Button : Control{
+}
+
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.ImageData;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.ImageList;
+import dwt.internal.win32.BITMAP;
+import dwt.internal.win32.BUTTON_IMAGELIST;
+import dwt.internal.win32.DRAWITEMSTRUCT;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SIZE;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class represent a selectable user interface object that
+ * issues notification when pressed and released.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>ARROW, CHECK, PUSH, RADIO, TOGGLE, FLAT</dd>
+ * <dd>UP, DOWN, LEFT, RIGHT, CENTER</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles ARROW, CHECK, PUSH, RADIO, and TOGGLE
+ * may be specified.
+ * </p><p>
+ * Note: Only one of the styles LEFT, RIGHT, and CENTER may be specified.
+ * </p><p>
+ * Note: Only one of the styles UP, DOWN, LEFT, and RIGHT may be specified
+ * when the ARROW style is specified.
+ * </p><p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+
+public class Button extends Control {
+    String text = "", message = "";
+    Image image, image2, disabledImage;
+    ImageList imageList;
+    bool ignoreMouse;
+    static final int MARGIN = 4;
+    static final int CHECK_WIDTH, CHECK_HEIGHT;
+    static final int ICON_WIDTH = 128, ICON_HEIGHT = 128;
+    static final bool COMMAND_LINK = false;
+    static final int ButtonProc;
+    static final TCHAR ButtonClass = new TCHAR (0, "BUTTON", true);
+    static {
+        int hBitmap = OS.LoadBitmap (0, OS.OBM_CHECKBOXES);
+        if (hBitmap is 0) {
+            CHECK_WIDTH = OS.GetSystemMetrics (OS.IsWinCE ? OS.SM_CXSMICON : OS.SM_CXVSCROLL);
+            CHECK_HEIGHT = OS.GetSystemMetrics (OS.IsWinCE ? OS.SM_CYSMICON : OS.SM_CYVSCROLL);
+        } else {
+            BITMAP bitmap = new BITMAP ();
+            OS.GetObject (hBitmap, BITMAP.sizeof, bitmap);
+            OS.DeleteObject (hBitmap);
+            CHECK_WIDTH = bitmap.bmWidth / 4;
+            CHECK_HEIGHT =  bitmap.bmHeight / 3;
+        }
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, ButtonClass, lpWndClass);
+        ButtonProc = lpWndClass.lpfnWndProc;
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#ARROW
+ * @see DWT#CHECK
+ * @see DWT#PUSH
+ * @see DWT#RADIO
+ * @see DWT#TOGGLE
+ * @see DWT#FLAT
+ * @see DWT#LEFT
+ * @see DWT#RIGHT
+ * @see DWT#CENTER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Button (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+void _setImage (Image image) {
+    if ((style & DWT.COMMAND) !is 0) return;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if (imageList !is null) imageList.dispose ();
+        imageList = null;
+        if (image !is null) {
+            imageList = new ImageList (style & DWT.RIGHT_TO_LEFT);
+            if (OS.IsWindowEnabled (handle)) {
+                imageList.add (image);
+            } else {
+                if (disabledImage !is null) disabledImage.dispose ();
+                disabledImage = new Image (display, image, DWT.IMAGE_DISABLE);
+                imageList.add (disabledImage);
+            }
+            BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
+            buttonImageList.himl = imageList.getHandle ();
+            int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
+            newBits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT);
+            if ((style & DWT.LEFT) !is 0) newBits |= OS.BS_LEFT;
+            if ((style & DWT.CENTER) !is 0) newBits |= OS.BS_CENTER;
+            if ((style & DWT.RIGHT) !is 0) newBits |= OS.BS_RIGHT;
+            if (text.length () is 0) {
+                if ((style & DWT.LEFT) !is 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
+                if ((style & DWT.CENTER) !is 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_CENTER;
+                if ((style & DWT.RIGHT) !is 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_RIGHT;
+            } else {
+                buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
+                buttonImageList.margin_left = computeLeftMargin ();
+                buttonImageList.margin_right = MARGIN;
+                newBits &= ~(OS.BS_CENTER | OS.BS_RIGHT);
+                newBits |= OS.BS_LEFT;
+            }
+            if (newBits !is oldBits) {
+                OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+                OS.InvalidateRect (handle, null, true);
+            }
+            OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
+        } else {
+            OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, 0);
+        }
+        /*
+        * Bug in Windows.  Under certain cirumstances yet to be
+        * isolated, BCM_SETIMAGELIST does not redraw the control
+        * when a new image is set.  The fix is to force a redraw.
+        */
+        OS.InvalidateRect (handle, null, true);
+    } else {
+        if (image2 !is null) image2.dispose ();
+        image2 = null;
+        int hImage = 0, imageBits = 0, fImageType = 0;
+        if (image !is null) {
+            switch (image.type) {
+                case DWT.BITMAP: {
+                    Rectangle rect = image.getBounds ();
+                    ImageData data = image.getImageData ();
+                    switch (data.getTransparencyType ()) {
+                        case DWT.TRANSPARENCY_PIXEL:
+                            if (rect.width <= ICON_WIDTH && rect.height <= ICON_HEIGHT) {
+                                image2 = new Image (display, data, data.getTransparencyMask ());
+                                hImage = image2.handle;
+                                imageBits = OS.BS_ICON;
+                                fImageType = OS.IMAGE_ICON;
+                                break;
+                            }
+                            //FALL THROUGH
+                        case DWT.TRANSPARENCY_ALPHA:
+                            image2 = new Image (display, rect.width, rect.height);
+                            GC gc = new GC (image2);
+                            gc.setBackground (getBackground ());
+                            gc.fillRectangle (rect);
+                            gc.drawImage (image, 0, 0);
+                            gc.dispose ();
+                            hImage = image2.handle;
+                            imageBits = OS.BS_BITMAP;
+                            fImageType = OS.IMAGE_BITMAP;
+                            break;
+                        case DWT.TRANSPARENCY_NONE:
+                            hImage = image.handle;
+                            imageBits = OS.BS_BITMAP;
+                            fImageType = OS.IMAGE_BITMAP;
+                            break;
+                    }
+                    break;
+                }
+                case DWT.ICON: {
+                    hImage = image.handle;
+                    imageBits = OS.BS_ICON;
+                    fImageType = OS.IMAGE_ICON;
+                    break;
+                }
+            }
+            /*
+            * Feature in Windows.  The button control mirrors its image when the
+            * flag WS_EX_LAYOUTRTL is set. This behaviour is not desirable in DWT.
+            * The fix is to set a mirrored version of real image in the button.
+            */
+            if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+                if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+                    Rectangle rect = image.getBounds ();
+                    int hDC = OS.GetDC (handle);
+                    int dstHdc = OS.CreateCompatibleDC (hDC);
+                    int hBitmap = OS.CreateCompatibleBitmap (hDC, rect.width, rect.height);
+                    int oldBitmap = OS.SelectObject (dstHdc, hBitmap);
+                    OS.SetLayout (dstHdc, OS.LAYOUT_RTL);
+                    if (fImageType is OS.IMAGE_BITMAP) {
+                        int srcHdc = OS.CreateCompatibleDC (hDC);
+                        int oldSrcBitmap = OS.SelectObject (srcHdc, hImage);
+                        OS.SetLayout (dstHdc, 0);
+                        OS.BitBlt (dstHdc, 0, 0, rect.width, rect.height, srcHdc, 0, 0, OS.SRCCOPY);
+                        OS.SelectObject (srcHdc, oldSrcBitmap);
+                        OS.DeleteDC (srcHdc);
+                    } else {
+                        Control control = findBackgroundControl ();
+                        if (control is null) control = this;
+                        int newBrush = OS.CreateSolidBrush (control.getBackgroundPixel ());
+                        int oldBrush = OS.SelectObject (dstHdc, newBrush);
+                        OS.PatBlt (dstHdc, 0, 0, rect.width, rect.height, OS.PATCOPY);
+                        OS.DrawIconEx (dstHdc, 0, 0, hImage, 0, 0, 0, 0, OS.DI_NORMAL);
+                        OS.SelectObject (dstHdc, oldBrush);
+                        OS.DeleteObject (newBrush);
+                    }
+                    OS.SelectObject (dstHdc, oldBitmap);
+                    OS.DeleteDC (dstHdc);
+                    OS.ReleaseDC (handle, hDC);
+                    if (image2 !is null) image2.dispose ();
+                    image2 = Image.win32_new (display, DWT.BITMAP, hBitmap);
+                    imageBits = OS.BS_BITMAP;
+                    fImageType = OS.IMAGE_BITMAP;
+                    hImage = hBitmap;
+                }
+            }
+        }
+        int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE), oldBits = newBits;
+        newBits &= ~(OS.BS_BITMAP | OS.BS_ICON);
+        newBits |= imageBits;
+        if (newBits !is oldBits) OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+        OS.SendMessage (handle, OS.BM_SETIMAGE, fImageType, hImage);
+    }
+}
+
+void _setText (String text) {
+    int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        newBits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT);
+        if ((style & DWT.LEFT) !is 0) newBits |= OS.BS_LEFT;
+        if ((style & DWT.CENTER) !is 0) newBits |= OS.BS_CENTER;
+        if ((style & DWT.RIGHT) !is 0) newBits |= OS.BS_RIGHT;
+        if (imageList !is null) {
+            BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
+            buttonImageList.himl = imageList.getHandle ();
+            if (text.length () is 0) {
+                if ((style & DWT.LEFT) !is 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
+                if ((style & DWT.CENTER) !is 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_CENTER;
+                if ((style & DWT.RIGHT) !is 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_RIGHT;
+            } else {
+                buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
+                buttonImageList.margin_left = computeLeftMargin ();
+                buttonImageList.margin_right = MARGIN;
+                newBits &= ~(OS.BS_CENTER | OS.BS_RIGHT);
+                newBits |= OS.BS_LEFT;
+            }
+            OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
+        }
+    } else {
+        newBits &= ~(OS.BS_BITMAP | OS.BS_ICON);
+    }
+    if (newBits !is oldBits) {
+        OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+        OS.InvalidateRect (handle, null, true);
+    }
+    /*
+    * Bug in Windows.  When a Button control is right-to-left and
+    * is disabled, the first pixel of the text is clipped.  The
+    * fix is to add a space to both sides of the text.
+    */
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+        if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+            text = OS.IsWindowEnabled (handle) ? text : " " + text + " ";
+        }
+    }
+    TCHAR buffer = new TCHAR (getCodePage (), text, true);
+    OS.SetWindowText (handle, buffer);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the control is selected by the user.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.CallWindowProc (ButtonProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    style = checkBits (style, DWT.PUSH, DWT.ARROW, DWT.CHECK, DWT.RADIO, DWT.TOGGLE, COMMAND_LINK ? DWT.COMMAND : 0);
+    if ((style & (DWT.PUSH | DWT.TOGGLE)) !is 0) {
+        return checkBits (style, DWT.CENTER, DWT.LEFT, DWT.RIGHT, 0, 0, 0);
+    }
+    if ((style & (DWT.CHECK | DWT.RADIO)) !is 0) {
+        return checkBits (style, DWT.LEFT, DWT.RIGHT, DWT.CENTER, 0, 0, 0);
+    }
+    if ((style & DWT.ARROW) !is 0) {
+        style |= DWT.NO_FOCUS;
+        return checkBits (style, DWT.UP, DWT.DOWN, DWT.LEFT, DWT.RIGHT, 0, 0);
+    }
+    return style;
+}
+
+void click () {
+    /*
+    * Feature in Windows.  BM_CLICK sends a fake WM_LBUTTONDOWN and
+    * WM_LBUTTONUP in order to click the button.  This causes the
+    * application to get unexpected mouse events.  The fix is to
+    * ignore mouse events when they are caused by BM_CLICK.
+    */
+    ignoreMouse = true;
+    OS.SendMessage (handle, OS.BM_CLICK, 0, 0);
+    ignoreMouse = false;
+}
+
+int computeLeftMargin () {
+    if (OS.COMCTL32_MAJOR < 6) return MARGIN;
+    if ((style & (DWT.PUSH | DWT.TOGGLE)) is 0) return MARGIN;
+    int margin = 0;
+    if (image !is null && text.length () !is 0) {
+        Rectangle bounds = image.getBounds ();
+        margin += bounds.width + MARGIN * 2;
+        int oldFont = 0;
+        int hDC = OS.GetDC (handle);
+        int newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+        TCHAR buffer = new TCHAR (getCodePage (), text, true);
+        RECT rect = new RECT ();
+        int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE;
+        OS.DrawText (hDC, buffer, -1, rect, flags);
+        margin += rect.right - rect.left;
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+        OS.GetClientRect (handle, rect);
+        margin = Math.max (MARGIN, (rect.right - rect.left - margin) / 2);
+    }
+    return margin;
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = 0, height = 0, border = getBorderWidth ();
+    if ((style & DWT.ARROW) !is 0) {
+        if ((style & (DWT.UP | DWT.DOWN)) !is 0) {
+            width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+            height += OS.GetSystemMetrics (OS.SM_CYVSCROLL);
+        } else {
+            width += OS.GetSystemMetrics (OS.SM_CXHSCROLL);
+            height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+        }
+    } else {
+        if ((style & DWT.COMMAND) !is 0) {
+            SIZE size = new SIZE ();
+            if (wHint !is DWT.DEFAULT) {
+                size.cx = wHint;
+                OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size);
+                width = size.cx;
+                height = size.cy;
+            } else {
+                OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size);
+                width = size.cy;
+                height = size.cy;
+                size.cy = 0;
+                while (size.cy !is height) {
+                    size.cx = width++;
+                    size.cy = 0;
+                    OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size);
+                }
+            }
+        } else {
+            int extra = 0;
+            bool hasImage = image !is null, hasText = true;
+            if (OS.COMCTL32_MAJOR < 6) {
+                if ((style & DWT.PUSH) is 0) {
+                    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+                    hasImage = (bits & (OS.BS_BITMAP | OS.BS_ICON)) !is 0;
+                    if (hasImage) hasText = false;
+                }
+            }
+            if (hasImage) {
+                if (image !is null) {
+                    Rectangle rect = image.getBounds ();
+                    width = rect.width;
+                    if (hasText && text.length () !is 0) {
+                        width += MARGIN * 2;
+                    }
+                    height = rect.height;
+                    extra = MARGIN * 2;
+                }
+            }
+            if (hasText) {
+                int oldFont = 0;
+                int hDC = OS.GetDC (handle);
+                int newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+                if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+                TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+                OS.GetTextMetrics (hDC, lptm);
+                int length = text.length ();
+                if (length is 0) {
+                    height = Math.max (height, lptm.tmHeight);
+                } else {
+                    extra = Math.max (MARGIN * 2, lptm.tmAveCharWidth);
+                    TCHAR buffer = new TCHAR (getCodePage (), text, true);
+                    RECT rect = new RECT ();
+                    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE;
+                    OS.DrawText (hDC, buffer, -1, rect, flags);
+                    width += rect.right - rect.left;
+                    height = Math.max (height, rect.bottom - rect.top);
+                }
+                if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+                OS.ReleaseDC (handle, hDC);
+            }
+            if ((style & (DWT.CHECK | DWT.RADIO)) !is 0) {
+                width += CHECK_WIDTH + extra;
+                height = Math.max (height, CHECK_HEIGHT + 3);
+            }
+            if ((style & (DWT.PUSH | DWT.TOGGLE)) !is 0) {
+                width += 12;  height += 10;
+            }
+        }
+    }
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    width += border * 2;
+    height += border * 2;
+    return new Point (width, height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    if ((style & DWT.PUSH) is 0) state |= THEME_BACKGROUND;
+    /*
+    * Bug in Windows.  For some reason, the HBRUSH that
+    * is returned from WM_CTRLCOLOR is misaligned when
+    * the button uses it to draw.  If the brush is a solid
+    * color, this does not matter.  However, if the brush
+    * contains an image, the image is misaligned.  The
+    * fix is to draw the background in WM_CTRLCOLOR.
+    *
+    * NOTE: For comctl32.dll 6.0 with themes disabled,
+    * drawing in WM_ERASEBKGND will draw on top of the
+    * text of the control.
+    */
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        if ((style & DWT.RADIO) !is 0) state |= DRAW_BACKGROUND;
+    }
+}
+
+int defaultBackground () {
+    if ((style & (DWT.PUSH | DWT.TOGGLE)) !is 0) {
+        return OS.GetSysColor (OS.COLOR_BTNFACE);
+    }
+    return super.defaultBackground ();
+}
+
+int defaultForeground () {
+    return OS.GetSysColor (OS.COLOR_BTNTEXT);
+}
+
+void enableWidget (bool enabled) {
+    super.enableWidget (enabled);
+    /*
+    * Bug in Windows.  When a button control is right-to-left and
+    * is disabled, the first pixel of the text is clipped.   The
+    * fix is to add a space to both sides of the text.
+    */
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+        if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            bool hasImage = (bits & (OS.BS_BITMAP | OS.BS_ICON)) !is 0;
+            if (!hasImage) {
+                String string = enabled ? text : " " + text + " ";
+                TCHAR buffer = new TCHAR (getCodePage (), string, true);
+                OS.SetWindowText (handle, buffer);
+            }
+        }
+    }
+    /*
+    * Bug in Windows.  When a button has the style BS_CHECKBOX
+    * or BS_RADIOBUTTON, is checked, and is displaying both an
+    * image and some text, when BCM_SETIMAGELIST is used to
+    * assign an image list for each of the button states, the
+    * button does not draw properly.  When the user drags the
+    * mouse in and out of the button, it draws using a blank
+    * image.  The fix is to set the complete image list only
+    * when the button is disabled.
+    */
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if (imageList !is null) {
+            BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
+            OS.SendMessage (handle, OS.BCM_GETIMAGELIST, 0, buttonImageList);
+            if (imageList !is null) imageList.dispose ();
+            imageList = new ImageList (style & DWT.RIGHT_TO_LEFT);
+            if (OS.IsWindowEnabled (handle)) {
+                imageList.add (image);
+            } else {
+                if (disabledImage !is null) disabledImage.dispose ();
+                disabledImage = new Image (display, image, DWT.IMAGE_DISABLE);
+                imageList.add (disabledImage);
+            }
+            buttonImageList.himl = imageList.getHandle ();
+            OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
+            /*
+            * Bug in Windows.  Under certain cirumstances yet to be
+            * isolated, BCM_SETIMAGELIST does not redraw the control
+            * when an image is set.  The fix is to force a redraw.
+            */
+            OS.InvalidateRect (handle, null, true);
+        }
+    }
+}
+
+/**
+ * Returns a value which describes the position of the
+ * text or image in the receiver. The value will be one of
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>
+ * unless the receiver is an <code>ARROW</code> button, in
+ * which case, the alignment will indicate the direction of
+ * the arrow (one of <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>UP</code> or <code>DOWN</code>).
+ *
+ * @return the alignment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getAlignment () {
+    checkWidget ();
+    if ((style & DWT.ARROW) !is 0) {
+        if ((style & DWT.UP) !is 0) return DWT.UP;
+        if ((style & DWT.DOWN) !is 0) return DWT.DOWN;
+        if ((style & DWT.LEFT) !is 0) return DWT.LEFT;
+        if ((style & DWT.RIGHT) !is 0) return DWT.RIGHT;
+        return DWT.UP;
+    }
+    if ((style & DWT.LEFT) !is 0) return DWT.LEFT;
+    if ((style & DWT.CENTER) !is 0) return DWT.CENTER;
+    if ((style & DWT.RIGHT) !is 0) return DWT.RIGHT;
+    return DWT.LEFT;
+}
+
+bool getDefault () {
+    if ((style & DWT.PUSH) is 0) return false;
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    return (bits & OS.BS_DEFPUSHBUTTON) !is 0;
+}
+
+/**
+ * Returns the receiver's image if it has one, or null
+ * if it does not.
+ *
+ * @return the receiver's image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getImage () {
+    checkWidget ();
+    return image;
+}
+
+/**
+ * Returns the widget message. When the widget is created
+ * with the style <code>DWT.COMMAND</code>, the message text
+ * is displayed to provide further information for the user.
+ *
+ * @return the widget message
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+/*public*/ String getMessage () {
+    checkWidget ();
+    return message;
+}
+
+String getNameText () {
+    return getText ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is selected,
+ * and false otherwise.
+ * <p>
+ * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
+ * it is selected when it is checked. When it is of type <code>TOGGLE</code>,
+ * it is selected when it is pushed in. If the receiver is of any other type,
+ * this method returns false.
+ *
+ * @return the selection state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getSelection () {
+    checkWidget ();
+    if ((style & (DWT.CHECK | DWT.RADIO | DWT.TOGGLE)) is 0) return false;
+    int state = OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0);
+    return (state & OS.BST_CHECKED) !is 0;
+}
+
+/**
+ * Returns the receiver's text, which will be an empty
+ * string if it has never been set or if the receiver is
+ * an <code>ARROW</code> button.
+ *
+ * @return the receiver's text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText () {
+    checkWidget ();
+    if ((style & DWT.ARROW) !is 0) return "";
+    return text;
+}
+
+bool isTabItem () {
+    //TEMPORARY CODE
+    //if ((style & DWT.PUSH) !is 0) return true;
+    return super.isTabItem ();
+}
+
+bool mnemonicHit (char ch) {
+    if (!setFocus ()) return false;
+    /*
+    * Feature in Windows.  When a radio button gets focus,
+    * it selects the button in WM_SETFOCUS.  Therefore, it
+    * is not necessary to click the button or send events
+    * because this has already happened in WM_SETFOCUS.
+    */
+    if ((style & DWT.RADIO) is 0) click ();
+    return true;
+}
+
+bool mnemonicMatch (char key) {
+    char mnemonic = findMnemonic (getText ());
+    if (mnemonic is '\0') return false;
+    return Character.toUpperCase (key) is Character.toUpperCase (mnemonic);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if (imageList !is null) imageList.dispose ();
+    imageList = null;
+    if (disabledImage !is null) disabledImage.dispose ();
+    disabledImage = null;
+    if (image2 !is null) image2.dispose ();
+    image2 = null;
+    text = null;
+    image = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+void selectRadio () {
+    /*
+    * This code is intentionally commented.  When two groups
+    * of radio buttons with the same parent are separated by
+    * another control, the correct behavior should be that
+    * the two groups act independently.  This is consistent
+    * with radio tool and menu items.  The commented code
+    * implements this behavior.
+    */
+//  int index = 0;
+//  Control [] children = parent._getChildren ();
+//  while (index < children.length && children [index] !is this) index++;
+//  int i = index - 1;
+//  while (i >= 0 && children [i].setRadioSelection (false)) --i;
+//  int j = index + 1;
+//  while (j < children.length && children [j].setRadioSelection (false)) j++;
+//  setSelection (true);
+    Control [] children = parent._getChildren ();
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        if (this !is child) child.setRadioSelection (false);
+    }
+    setSelection (true);
+}
+
+/**
+ * Controls how text, images and arrows will be displayed
+ * in the receiver. The argument should be one of
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>
+ * unless the receiver is an <code>ARROW</code> button, in
+ * which case, the argument indicates the direction of
+ * the arrow (one of <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>UP</code> or <code>DOWN</code>).
+ *
+ * @param alignment the new alignment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setAlignment (int alignment) {
+    checkWidget ();
+    if ((style & DWT.ARROW) !is 0) {
+        if ((style & (DWT.UP | DWT.DOWN | DWT.LEFT | DWT.RIGHT)) is 0) return;
+        style &= ~(DWT.UP | DWT.DOWN | DWT.LEFT | DWT.RIGHT);
+        style |= alignment & (DWT.UP | DWT.DOWN | DWT.LEFT | DWT.RIGHT);
+        OS.InvalidateRect (handle, null, true);
+        return;
+    }
+    if ((alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER)) is 0) return;
+    style &= ~(DWT.LEFT | DWT.RIGHT | DWT.CENTER);
+    style |= alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER);
+    int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
+    newBits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT);
+    if ((style & DWT.LEFT) !is 0) newBits |= OS.BS_LEFT;
+    if ((style & DWT.CENTER) !is 0) newBits |= OS.BS_CENTER;
+    if ((style & DWT.RIGHT) !is 0) newBits |= OS.BS_RIGHT;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if (imageList !is null) {
+            BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
+            buttonImageList.himl = imageList.getHandle ();
+            if (text.length () is 0) {
+                if ((style & DWT.LEFT) !is 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
+                if ((style & DWT.CENTER) !is 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_CENTER;
+                if ((style & DWT.RIGHT) !is 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_RIGHT;
+            } else {
+                buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
+                buttonImageList.margin_left = computeLeftMargin ();
+                buttonImageList.margin_right = MARGIN;
+                newBits &= ~(OS.BS_CENTER | OS.BS_RIGHT);
+                newBits |= OS.BS_LEFT;
+            }
+            OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
+        }
+    }
+    if (newBits !is oldBits) {
+        OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+        OS.InvalidateRect (handle, null, true);
+    }
+}
+
+void setDefault (bool value) {
+    if ((style & DWT.PUSH) is 0) return;
+    int hwndShell = menuShell ().handle;
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if (value) {
+        bits |= OS.BS_DEFPUSHBUTTON;
+        OS.SendMessage (hwndShell, OS.DM_SETDEFID, handle, 0);
+    } else {
+        bits &= ~OS.BS_DEFPUSHBUTTON;
+        OS.SendMessage (hwndShell, OS.DM_SETDEFID, 0, 0);
+    }
+    OS.SendMessage (handle, OS.BM_SETSTYLE, bits, 1);
+}
+
+bool setFixedFocus () {
+    /*
+    * Feature in Windows.  When a radio button gets focus,
+    * it selects the button in WM_SETFOCUS.  The fix is to
+    * not assign focus to an unselected radio button.
+    */
+    if ((style & DWT.RADIO) !is 0 && !getSelection ()) return false;
+    return super.setFixedFocus ();
+}
+
+/**
+ * Sets the receiver's image to the argument, which may be
+ * <code>null</code> indicating that no image should be displayed.
+ * <p>
+ * Note that a Button can display an image and text simultaneously
+ * on Windows (starting with XP), GTK+ and OSX.  On other platforms,
+ * a Button that has an image and text set into it will display the
+ * image or text that was set most recently.
+ * </p>
+ * @param image the image to display on the receiver (may be <code>null</code>)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (Image image) {
+    checkWidget ();
+    if (image !is null && image.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    if ((style & DWT.ARROW) !is 0) return;
+    this.image = image;
+    /* This code is intentionally commented */
+//  if (OS.COMCTL32_MAJOR < 6) {
+//      if (image is null || text.length () !is 0) {
+//          _setText (text);
+//          return;
+//      }
+//  }
+    _setImage (image);
+}
+
+/**
+ * Sets the widget message. When the widget is created
+ * with the style <code>DWT.COMMAND</code>, the message text
+ * is displayed to provide further information for the user.
+ *
+ * @param message the new message
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+/*public*/ void setMessage (String message) {
+    checkWidget ();
+    if (message is null) error (DWT.ERROR_NULL_ARGUMENT);
+    this.message = message;
+    if (OS.COMCTL32_VERSION >= OS.VERSION (6, 1)) {
+        if ((style & DWT.COMMAND) !is 0) {
+            int length = message.length ();
+            char [] chars = new char [length + 1];
+            message.getChars(0, length, chars, 0);
+            OS.SendMessage (handle, OS.BCM_SETNOTE, 0, chars);
+        }
+    }
+}
+
+bool setRadioFocus () {
+    if ((style & DWT.RADIO) is 0 || !getSelection ()) return false;
+    return setFocus ();
+}
+
+bool setRadioSelection (bool value) {
+    if ((style & DWT.RADIO) is 0) return false;
+    if (getSelection () !is value) {
+        setSelection (value);
+        postEvent (DWT.Selection);
+    }
+    return true;
+}
+
+bool setSavedFocus () {
+    /*
+    * Feature in Windows.  When a radio button gets focus,
+    * it selects the button in WM_SETFOCUS.  If the previous
+    * saved focus widget was a radio button, allowing the shell
+    * to automatically restore the focus to the previous radio
+    * button will unexpectedly check that button.  The fix is to
+    * not assign focus to an unselected radio button.
+    */
+    if ((style & DWT.RADIO) !is 0 && !getSelection ()) return false;
+    return super.setSavedFocus ();
+}
+
+/**
+ * Sets the selection state of the receiver, if it is of type <code>CHECK</code>,
+ * <code>RADIO</code>, or <code>TOGGLE</code>.
+ *
+ * <p>
+ * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
+ * it is selected when it is checked. When it is of type <code>TOGGLE</code>,
+ * it is selected when it is pushed in.
+ *
+ * @param selected the new selection state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (bool selected) {
+    checkWidget ();
+    if ((style & (DWT.CHECK | DWT.RADIO | DWT.TOGGLE)) is 0) return;
+    int flags = selected ? OS.BST_CHECKED : OS.BST_UNCHECKED;
+    /*
+    * Feature in Windows. When BM_SETCHECK is used
+    * to set the checked state of a radio or check
+    * button, it sets the WM_TABSTOP style.  This
+    * is undocumented and unwanted.  The fix is
+    * to save and restore the window style bits.
+    */
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    OS.SendMessage (handle, OS.BM_SETCHECK, flags, 0);
+    OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+}
+
+/**
+ * Sets the receiver's text.
+ * <p>
+ * This method sets the button label.  The label may include
+ * the mnemonic character but must not contain line delimiters.
+ * </p>
+ * <p>
+ * Mnemonics are indicated by an '&amp;' that causes the next
+ * character to be the mnemonic.  When the user presses a
+ * key sequence that matches the mnemonic, a selection
+ * event occurs. On most platforms, the mnemonic appears
+ * underlined but may be emphasized in a platform specific
+ * manner.  The mnemonic indicator character '&amp;' can be
+ * escaped by doubling it in the string, causing a single
+ * '&amp;' to be displayed.
+ * </p><p>
+ * Note that a Button can display an image and text simultaneously
+ * on Windows (starting with XP), GTK+ and OSX.  On other platforms,
+ * a Button that has an image and text set into it will display the
+ * image or text that was set most recently.
+ * </p>
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if ((style & DWT.ARROW) !is 0) return;
+    text = string;
+    /* This code is intentionally commented */
+//  if (OS.COMCTL32_MAJOR < 6) {
+//      if (text.length () is 0 && image !is null) {
+//          _setImage (image);
+//          return;
+//      }
+//  }
+    _setText (string);
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle ();
+    if ((style & DWT.FLAT) !is 0) bits |= OS.BS_FLAT;
+    if ((style & DWT.ARROW) !is 0) return bits | OS.BS_OWNERDRAW;
+    if ((style & DWT.LEFT) !is 0) bits |= OS.BS_LEFT;
+    if ((style & DWT.CENTER) !is 0) bits |= OS.BS_CENTER;
+    if ((style & DWT.RIGHT) !is 0) bits |= OS.BS_RIGHT;
+    if ((style & DWT.PUSH) !is 0) return bits | OS.BS_PUSHBUTTON | OS.WS_TABSTOP;
+    if ((style & DWT.CHECK) !is 0) return bits | OS.BS_CHECKBOX | OS.WS_TABSTOP;
+    if ((style & DWT.RADIO) !is 0) return bits | OS.BS_RADIOBUTTON;
+    if ((style & DWT.TOGGLE) !is 0) return bits | OS.BS_PUSHLIKE | OS.BS_CHECKBOX | OS.WS_TABSTOP;
+    if ((style & DWT.COMMAND) !is 0) return bits | OS.BS_COMMANDLINK | OS.WS_TABSTOP;
+    return bits | OS.BS_PUSHBUTTON | OS.WS_TABSTOP;
+}
+
+TCHAR windowClass () {
+    return ButtonClass;
+}
+
+int windowProc () {
+    return ButtonProc;
+}
+
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug in Windows.  For some reason, the HBRUSH that
+    * is returned from WM_CTRLCOLOR is misaligned when
+    * the button uses it to draw.  If the brush is a solid
+    * color, this does not matter.  However, if the brush
+    * contains an image, the image is misaligned.  The
+    * fix is to draw the background in WM_ERASEBKGND.
+    */
+    if (OS.COMCTL32_MAJOR < 6) {
+        if ((style & (DWT.RADIO | DWT.CHECK)) !is 0) {
+            if (findImageControl () !is null) {
+                drawBackground (wParam);
+                return LRESULT.ONE;
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
+    if (result !is null) return result;
+    if ((style & DWT.ARROW) !is 0) {
+        return new LRESULT (OS.DLGC_STATIC);
+    }
+    return result;
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
+    if ((style & DWT.PUSH) !is 0 && getDefault ()) {
+        menuShell ().setDefaultButton (null, false);
+    }
+    return result;
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    if (ignoreMouse) return null;
+    return super.WM_LBUTTONDOWN (wParam, lParam);
+}
+
+LRESULT WM_LBUTTONUP (int wParam, int lParam) {
+    if (ignoreMouse) return null;
+    return super.WM_LBUTTONUP (wParam, lParam);
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    /*
+    * Feature in Windows. When Windows sets focus to
+    * a radio button, it sets the WM_TABSTOP style.
+    * This is undocumented and unwanted.  The fix is
+    * to save and restore the window style bits.
+    */
+    int bits = 0;
+    if ((style & DWT.RADIO) !is 0) {
+        bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    }
+    LRESULT result = super.WM_SETFOCUS (wParam, lParam);
+    if ((style & DWT.RADIO) !is 0) {
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+    }
+    if ((style & DWT.PUSH) !is 0) {
+        menuShell ().setDefaultButton (this, false);
+    }
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    if (result !is null) return result;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if ((style & (DWT.PUSH | DWT.TOGGLE)) !is 0) {
+            if (imageList !is null && text.length () !is 0) {
+                BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
+                OS.SendMessage (handle, OS.BCM_GETIMAGELIST, 0, buttonImageList);
+                buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
+                buttonImageList.margin_left = computeLeftMargin ();
+                buttonImageList.margin_right = MARGIN;
+                OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) {
+    LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
+    if (result !is null) return result;
+    if (image2 !is null) _setImage (image);
+    return result;
+}
+
+LRESULT WM_UPDATEUISTATE (int wParam, int lParam) {
+    LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  When WM_UPDATEUISTATE is sent to
+    * a button, it sends WM_CTLCOLORBTN to get the foreground
+    * and background.  If drawing happens in WM_CTLCOLORBTN,
+    * it will overwrite the contents of the control.  The
+    * fix is draw the button without drawing the background
+    * and avoid the button window proc.
+    *
+    * NOTE:  This only happens for radio, check and toggle
+    * buttons.
+    */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        if ((style & (DWT.RADIO | DWT.CHECK | DWT.TOGGLE)) !is 0) {
+            bool redraw = findImageControl () !is null;
+            if (!redraw) {
+                if ((state & THEME_BACKGROUND) !is 0) {
+                    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                        redraw = findThemeControl () !is null;
+                    }
+                }
+                if (!redraw) redraw = findBackgroundControl () !is null;
+            }
+            if (redraw) {
+                OS.InvalidateRect (handle, null, false);
+                int code = OS.DefWindowProc (handle, OS.WM_UPDATEUISTATE, wParam, lParam);
+                return new LRESULT (code);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT wmCommandChild (int wParam, int lParam) {
+    int code = wParam >> 16;
+    switch (code) {
+        case OS.BN_CLICKED:
+        case OS.BN_DOUBLECLICKED:
+            if ((style & (DWT.CHECK | DWT.TOGGLE)) !is 0) {
+                setSelection (!getSelection ());
+            } else {
+                if ((style & DWT.RADIO) !is 0) {
+                    if ((parent.getStyle () & DWT.NO_RADIO_GROUP) !is 0) {
+                        setSelection (!getSelection ());
+                    } else {
+                        selectRadio ();
+                    }
+                }
+            }
+            postEvent (DWT.Selection);
+    }
+    return super.wmCommandChild (wParam, lParam);
+}
+
+LRESULT wmColorChild (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  For some reason, the HBRUSH that
+    * is returned from WM_CTRLCOLOR is misaligned when
+    * the button uses it to draw.  If the brush is a solid
+    * color, this does not matter.  However, if the brush
+    * contains an image, the image is misaligned.  The
+    * fix is to draw the background in WM_ERASEBKGND.
+    */
+    LRESULT result = super.wmColorChild (wParam, lParam);
+    if (OS.COMCTL32_MAJOR < 6) {
+        if ((style & (DWT.RADIO | DWT.CHECK)) !is 0) {
+            if (findImageControl () !is null) {
+                OS.SetBkMode (wParam, OS.TRANSPARENT);
+                return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH));
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT wmDrawChild (int wParam, int lParam) {
+    if ((style & DWT.ARROW) is 0) return super.wmDrawChild (wParam, lParam);
+    DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
+    OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof);
+    RECT rect = new RECT ();
+    OS.SetRect (rect, struct.left, struct.top, struct.right, struct.bottom);
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        int iStateId = OS.ABS_LEFTNORMAL;
+        switch (style & (DWT.UP | DWT.DOWN | DWT.LEFT | DWT.RIGHT)) {
+            case DWT.UP: iStateId = OS.ABS_UPNORMAL; break;
+            case DWT.DOWN: iStateId = OS.ABS_DOWNNORMAL; break;
+            case DWT.LEFT: iStateId = OS.ABS_LEFTNORMAL; break;
+            case DWT.RIGHT: iStateId = OS.ABS_RIGHTNORMAL; break;
+        }
+        /*
+        * NOTE: The normal, hot, pressed and disabled state is
+        * computed relying on the fact that the increment between
+        * the direction states is invariant (always separated by 4).
+        */
+        if (!getEnabled ()) iStateId += OS.ABS_UPDISABLED - OS.ABS_UPNORMAL;
+        if ((struct.itemState & OS.ODS_SELECTED) !is 0) iStateId += OS.ABS_UPPRESSED - OS.ABS_UPNORMAL;
+        OS.DrawThemeBackground (display.hScrollBarTheme (), struct.hDC, OS.SBP_ARROWBTN, iStateId, rect, null);
+    } else {
+        int uState = OS.DFCS_SCROLLLEFT;
+        switch (style & (DWT.UP | DWT.DOWN | DWT.LEFT | DWT.RIGHT)) {
+            case DWT.UP: uState = OS.DFCS_SCROLLUP; break;
+            case DWT.DOWN: uState = OS.DFCS_SCROLLDOWN; break;
+            case DWT.LEFT: uState = OS.DFCS_SCROLLLEFT; break;
+            case DWT.RIGHT: uState = OS.DFCS_SCROLLRIGHT; break;
+        }
+        if (!getEnabled ()) uState |= OS.DFCS_INACTIVE;
+        if ((style & DWT.FLAT) is DWT.FLAT) uState |= OS.DFCS_FLAT;
+        if ((struct.itemState & OS.ODS_SELECTED) !is 0) uState |= OS.DFCS_PUSHED;
+        OS.DrawFrameControl (struct.hDC, rect, OS.DFC_SCROLL, uState);
+    }
+    return null;
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Canvas.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Canvas;
+
+import dwt.widgets.Composite;
+
+class Canvas : Composite {
+}
+
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.COMPOSITIONFORM;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+
+/**
+ * Instances of this class provide a surface for drawing
+ * arbitrary graphics.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * This class may be subclassed by custom control implementors
+ * who are building controls that are <em>not</em> constructed
+ * from aggregates of other controls. That is, they are either
+ * painted using DWT graphics calls or are handled by native
+ * methods.
+ * </p>
+ *
+ * @see Composite
+ */
+
+public class Canvas extends Composite {
+    Caret caret;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+Canvas () {
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Canvas (Composite parent, int style) {
+    super (parent, style);
+}
+
+void clearArea (int x, int y, int width, int height) {
+    checkWidget ();
+    if (OS.IsWindowVisible (handle)) {
+        RECT rect = new RECT ();
+        OS.SetRect (rect, x, y, x + width, y + height);
+        int hDC = OS.GetDCEx (handle, 0, OS.DCX_CACHE | OS.DCX_CLIPCHILDREN | OS.DCX_CLIPSIBLINGS);
+        drawBackground (hDC, rect);
+        OS.ReleaseDC (handle, hDC);
+    }
+}
+
+/**
+ * Returns the caret.
+ * <p>
+ * The caret for the control is automatically hidden
+ * and shown when the control is painted or resized,
+ * when focus is gained or lost and when an the control
+ * is scrolled.  To avoid drawing on top of the caret,
+ * the programmer must hide and show the caret when
+ * drawing in the window any other time.
+ * </p>
+ *
+ * @return the caret
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Caret getCaret () {
+    checkWidget ();
+    return caret;
+}
+
+void releaseChildren (bool destroy) {
+    if (caret !is null) {
+        caret.release (false);
+        caret = null;
+    }
+    super.releaseChildren (destroy);
+}
+
+/**
+ * Fills the interior of the rectangle specified by the arguments,
+ * with the receiver's background.
+ *
+ * @param gc the gc where the rectangle is to be filled
+ * @param x the x coordinate of the rectangle to be filled
+ * @param y the y coordinate of the rectangle to be filled
+ * @param width the width of the rectangle to be filled
+ * @param height the height of the rectangle to be filled
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the gc is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the gc has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void drawBackground (GC gc, int x, int y, int width, int height) {
+    checkWidget ();
+    if (gc is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (gc.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+    RECT rect = new RECT ();
+    OS.SetRect (rect, x, y, x + width, y + height);
+    int hDC = gc.handle;
+    int pixel = background is -1 ? gc.getBackground ().handle : -1;
+    drawBackground (hDC, rect, pixel);
+}
+
+/**
+ * Scrolls a rectangular area of the receiver by first copying
+ * the source area to the destination and then causing the area
+ * of the source which is not covered by the destination to
+ * be repainted. Children that intersect the rectangle are
+ * optionally moved during the operation. In addition, outstanding
+ * paint events are flushed before the source area is copied to
+ * ensure that the contents of the canvas are drawn correctly.
+ *
+ * @param destX the x coordinate of the destination
+ * @param destY the y coordinate of the destination
+ * @param x the x coordinate of the source
+ * @param y the y coordinate of the source
+ * @param width the width of the area
+ * @param height the height of the area
+ * @param all <code>true</code>if children should be scrolled, and <code>false</code> otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void scroll (int destX, int destY, int x, int y, int width, int height, bool all) {
+    checkWidget ();
+    forceResize ();
+    bool isFocus = caret !is null && caret.isFocusCaret ();
+    if (isFocus) caret.killFocus ();
+    RECT sourceRect = new RECT ();
+    OS.SetRect (sourceRect, x, y, x + width, y + height);
+    RECT clientRect = new RECT ();
+    OS.GetClientRect (handle, clientRect);
+    if (OS.IntersectRect (clientRect, sourceRect, clientRect)) {
+        if (OS.IsWinCE) {
+            OS.UpdateWindow (handle);
+        } else {
+            int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN;
+            OS.RedrawWindow (handle, null, 0, flags);
+        }
+    }
+    int deltaX = destX - x, deltaY = destY - y;
+    if (findImageControl () !is null) {
+        if (OS.IsWinCE) {
+            OS.InvalidateRect (handle, sourceRect, true);
+        } else {
+            int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
+            if (all) flags |= OS.RDW_ALLCHILDREN;
+            OS.RedrawWindow (handle, sourceRect, 0, flags);
+        }
+        OS.OffsetRect (sourceRect, deltaX, deltaY);
+        if (OS.IsWinCE) {
+            OS.InvalidateRect (handle, sourceRect, true);
+        } else {
+            int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
+            if (all) flags |= OS.RDW_ALLCHILDREN;
+            OS.RedrawWindow (handle, sourceRect, 0, flags);
+        }
+    } else {
+        int flags = OS.SW_INVALIDATE | OS.SW_ERASE;
+        /*
+        * Feature in Windows.  If any child in the widget tree partially
+        * intersects the scrolling rectangle, Windows moves the child
+        * and copies the bits that intersect the scrolling rectangle but
+        * does not redraw the child.
+        *
+        * Feature in Windows.  When any child in the widget tree does not
+        * intersect the scrolling rectangle but the parent does intersect,
+        * Windows does not move the child.  This is the documented (but
+        * strange) Windows behavior.
+        *
+        * The fix is to not use SW_SCROLLCHILDREN and move the children
+        * explicitly after scrolling.
+        */
+//      if (all) flags |= OS.SW_SCROLLCHILDREN;
+        OS.ScrollWindowEx (handle, deltaX, deltaY, sourceRect, null, 0, null, flags);
+    }
+    if (all) {
+        Control [] children = _getChildren ();
+        for (int i=0; i<children.length; i++) {
+            Control child = children [i];
+            Rectangle rect = child.getBounds ();
+            if (Math.min (x + width, rect.x + rect.width) >= Math.max (x, rect.x) &&
+                Math.min (y + height, rect.y + rect.height) >= Math.max (y, rect.y)) {
+                    child.setLocation (rect.x + deltaX, rect.y + deltaY);
+            }
+        }
+    }
+    if (isFocus) caret.setFocus ();
+}
+
+/**
+ * Sets the receiver's caret.
+ * <p>
+ * The caret for the control is automatically hidden
+ * and shown when the control is painted or resized,
+ * when focus is gained or lost and when an the control
+ * is scrolled.  To avoid drawing on top of the caret,
+ * the programmer must hide and show the caret when
+ * drawing in the window any other time.
+ * </p>
+ * @param caret the new caret for the receiver, may be null
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the caret has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setCaret (Caret caret) {
+    checkWidget ();
+    Caret newCaret = caret;
+    Caret oldCaret = this.caret;
+    this.caret = newCaret;
+    if (hasFocus ()) {
+        if (oldCaret !is null) oldCaret.killFocus ();
+        if (newCaret !is null) {
+            if (newCaret.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+            newCaret.setFocus ();
+        }
+    }
+}
+
+public void setFont (Font font) {
+    checkWidget ();
+    if (caret !is null) caret.setFont (font);
+    super.setFont (font);
+}
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (msg is Display.SWT_RESTORECARET) {
+        if ((state & CANVAS) !is 0) {
+            if (caret !is null) {
+                caret.killFocus ();
+                caret.setFocus ();
+                return 1;
+            }
+        }
+    }
+    return super.windowProc (hwnd, msg, wParam, lParam);
+}
+
+LRESULT WM_IME_COMPOSITION (int wParam, int lParam) {
+    LRESULT result  = super.WM_IME_COMPOSITION (wParam, lParam);
+    /*
+    * Bug in Windows.  On Korean Windows XP, the IME window
+    * for the Korean Input System (MS-IME 2002) always opens
+    * in the top left corner of the screen, despite the fact
+    * that ImmSetCompositionWindow() was called to position
+    * the IME when focus is gained.  The fix is to position
+    * the IME on every WM_IME_COMPOSITION message.
+    */
+    if (!OS.IsWinCE && OS.WIN32_VERSION is OS.VERSION (5, 1)) {
+        if (OS.IsDBLocale) {
+            short langID = OS.GetSystemDefaultUILanguage ();
+            short primaryLang = OS.PRIMARYLANGID (langID);
+            if (primaryLang is OS.LANG_KOREAN) {
+                if (caret !is null && caret.isFocusCaret ()) {
+                    POINT ptCurrentPos = new POINT ();
+                    if (OS.GetCaretPos (ptCurrentPos)) {
+                        COMPOSITIONFORM lpCompForm = new COMPOSITIONFORM ();
+                        lpCompForm.dwStyle = OS.CFS_POINT;
+                        lpCompForm.x = ptCurrentPos.x;
+                        lpCompForm.y = ptCurrentPos.y;
+                        int hIMC = OS.ImmGetContext (handle);
+                        OS.ImmSetCompositionWindow (hIMC, lpCompForm);
+                        OS.ImmReleaseContext (handle, hIMC);
+                    }
+                }
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_INPUTLANGCHANGE (int wParam, int lParam) {
+    LRESULT result  = super.WM_INPUTLANGCHANGE (wParam, lParam);
+    if (caret !is null && caret.isFocusCaret ()) {
+        caret.setIMEFont ();
+        caret.resizeIME ();
+    }
+    return result;
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    LRESULT result  = super.WM_KILLFOCUS (wParam, lParam);
+    if (caret !is null) caret.killFocus ();
+    return result;
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    LRESULT result  = super.WM_SETFOCUS (wParam, lParam);
+    if (caret !is null) caret.setFocus ();
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    LRESULT result  = super.WM_SIZE (wParam, lParam);
+    if (caret !is null && caret.isFocusCaret ()) caret.resizeIME ();
+    return result;
+}
+
+LRESULT WM_WINDOWPOSCHANGED (int wParam, int lParam) {
+    LRESULT result  = super.WM_WINDOWPOSCHANGED (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug in Windows.  When a window with style WS_EX_LAYOUTRTL
+    * that contains a caret is resized, Windows does not move the
+    * caret in relation to the mirrored origin in the top right.
+    * The fix is to hide the caret in WM_WINDOWPOSCHANGING and
+    * show the caret in WM_WINDOWPOSCHANGED.
+    */
+    bool isFocus = (style & DWT.RIGHT_TO_LEFT) !is 0 && caret !is null && caret.isFocusCaret ();
+    if (isFocus) caret.setFocus ();
+    return result;
+}
+
+LRESULT WM_WINDOWPOSCHANGING (int wParam, int lParam) {
+    LRESULT result  = super.WM_WINDOWPOSCHANGING (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug in Windows.  When a window with style WS_EX_LAYOUTRTL
+    * that contains a caret is resized, Windows does not move the
+    * caret in relation to the mirrored origin in the top right.
+    * The fix is to hide the caret in WM_WINDOWPOSCHANGING and
+    * show the caret in WM_WINDOWPOSCHANGED.
+    */
+    bool isFocus = (style & DWT.RIGHT_TO_LEFT) !is 0 && caret !is null && caret.isFocusCaret ();
+    if (isFocus) caret.killFocus ();
+    return result;
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Caret.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,572 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Caret;
+
+import dwt.widgets.Widget;
+
+class Caret : Widget {
+    this( Widget, int );
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.COMPOSITIONFORM;
+import dwt.internal.win32.LOGFONT;
+import dwt.internal.win32.LOGFONTA;
+import dwt.internal.win32.LOGFONTW;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+
+/**
+ * Instances of this class provide an i-beam that is typically used
+ * as the insertion point for text.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+
+public class Caret extends Widget {
+    Canvas parent;
+    int x, y, width, height;
+    bool moved, resized;
+    bool isVisible;
+    Image image;
+    Font font;
+    LOGFONT oldFont;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Caret (Canvas parent, int style) {
+    super (parent, style);
+    this.parent = parent;
+    createWidget ();
+}
+
+void createWidget () {
+    isVisible = true;
+    if (parent.getCaret () is null) {
+        parent.setCaret (this);
+    }
+}
+
+int defaultFont () {
+    int hwnd = parent.handle;
+    int hwndIME = OS.ImmGetDefaultIMEWnd (hwnd);
+    int hFont = 0;
+    if (hwndIME !is 0) {
+        hFont = OS.SendMessage (hwndIME, OS.WM_GETFONT, 0, 0);
+    }
+    if (hFont is 0) {
+        hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0);
+    }
+    if (hFont is 0) return parent.defaultFont ();
+    return hFont;
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent (or its display if its parent is null).
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds () {
+    checkWidget();
+    if (image !is null) {
+        Rectangle rect = image.getBounds ();
+        return new Rectangle (x, y, rect.width, rect.height);
+    }
+    return new Rectangle (x, y, width, height);
+}
+
+/**
+ * Returns the font that the receiver will use to paint textual information.
+ *
+ * @return the receiver's font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Font getFont () {
+    checkWidget();
+    if (font is null) {
+        int hFont = defaultFont ();
+        return Font.win32_new (display, hFont);
+    }
+    return font;
+}
+
+/**
+ * Returns the image that the receiver will use to paint the caret.
+ *
+ * @return the receiver's image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getImage () {
+    checkWidget();
+    return image;
+}
+
+/**
+ * Returns a point describing the receiver's location relative
+ * to its parent (or its display if its parent is null).
+ *
+ * @return the receiver's location
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getLocation () {
+    checkWidget();
+    return new Point (x, y);
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Canvas</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Canvas getParent () {
+    checkWidget();
+    return parent;
+}
+
+/**
+ * Returns a point describing the receiver's size.
+ *
+ * @return the receiver's size
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getSize () {
+    checkWidget();
+    if (image !is null) {
+        Rectangle rect = image.getBounds ();
+        return new Point (rect.width, rect.height);
+    }
+    return new Point (width, height);
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible, and
+ * <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getVisible () {
+    checkWidget();
+    return isVisible;
+}
+
+bool hasFocus () {
+    return parent.handle is OS.GetFocus ();
+}
+
+bool isFocusCaret () {
+    return parent.caret is this && hasFocus ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible and all
+ * of the receiver's ancestors are visible and <code>false</code>
+ * otherwise.
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getVisible
+ */
+public bool isVisible () {
+    checkWidget();
+    return isVisible && parent.isVisible () && hasFocus ();
+}
+
+void killFocus () {
+    OS.DestroyCaret ();
+    restoreIMEFont ();
+}
+
+void move () {
+    moved = false;
+    if (!OS.SetCaretPos (x, y)) return;
+    resizeIME ();
+}
+
+void resizeIME () {
+    if (!OS.IsDBLocale) return;
+    POINT ptCurrentPos = new POINT ();
+    if (!OS.GetCaretPos (ptCurrentPos)) return;
+    int hwnd = parent.handle;
+    RECT rect = new RECT ();
+    OS.GetClientRect (hwnd, rect);
+    COMPOSITIONFORM lpCompForm = new COMPOSITIONFORM ();
+    lpCompForm.dwStyle = OS.CFS_RECT;
+    lpCompForm.x = ptCurrentPos.x;
+    lpCompForm.y = ptCurrentPos.y;
+    lpCompForm.left = rect.left;
+    lpCompForm.right = rect.right;
+    lpCompForm.top = rect.top;
+    lpCompForm.bottom = rect.bottom;
+    int hIMC = OS.ImmGetContext (hwnd);
+    OS.ImmSetCompositionWindow (hIMC, lpCompForm);
+    OS.ImmReleaseContext (hwnd, hIMC);
+}
+
+void releaseParent () {
+    super.releaseParent ();
+    if (this is parent.getCaret ()) parent.setCaret (null);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    parent = null;
+    image = null;
+    font = null;
+    oldFont = null;
+}
+
+void resize () {
+    resized = false;
+    int hwnd = parent.handle;
+    OS.DestroyCaret ();
+    int hBitmap = image !is null ? image.handle : 0;
+    OS.CreateCaret (hwnd, hBitmap, width, height);
+    OS.SetCaretPos (x, y);
+    OS.ShowCaret (hwnd);
+    move ();
+}
+
+void restoreIMEFont () {
+    if (!OS.IsDBLocale) return;
+    if (oldFont is null) return;
+    int hwnd = parent.handle;
+    int hIMC = OS.ImmGetContext (hwnd);
+    OS.ImmSetCompositionFont (hIMC, oldFont);
+    OS.ImmReleaseContext (hwnd, hIMC);
+    oldFont = null;
+}
+
+/**
+ * Sets the receiver's size and location to the rectangular
+ * area specified by the arguments. The <code>x</code> and
+ * <code>y</code> arguments are relative to the receiver's
+ * parent (or its display if its parent is null).
+ *
+ * @param x the new x coordinate for the receiver
+ * @param y the new y coordinate for the receiver
+ * @param width the new width for the receiver
+ * @param height the new height for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setBounds (int x, int y, int width, int height) {
+    checkWidget();
+    bool samePosition = this.x is x && this.y is y;
+    bool sameExtent = this.width is width && this.height is height;
+    if (samePosition && sameExtent) return;
+    this.x = x;  this.y = y;
+    this.width = width;  this.height = height;
+    if (sameExtent) {
+        moved = true;
+        if (isVisible && hasFocus ()) move ();
+    } else {
+        resized = true;
+        if (isVisible && hasFocus ()) resize ();
+    }
+}
+
+/**
+ * Sets the receiver's size and location to the rectangular
+ * area specified by the argument. The <code>x</code> and
+ * <code>y</code> fields of the rectangle are relative to
+ * the receiver's parent (or its display if its parent is null).
+ *
+ * @param rect the new bounds for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setBounds (Rectangle rect) {
+    if (rect is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setBounds (rect.x, rect.y, rect.width, rect.height);
+}
+
+void setFocus () {
+    int hwnd = parent.handle;
+    int hBitmap = 0;
+    if (image !is null) hBitmap = image.handle;
+    OS.CreateCaret (hwnd, hBitmap, width, height);
+    move ();
+    setIMEFont ();
+    if (isVisible) OS.ShowCaret (hwnd);
+}
+
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * to the font specified by the argument, or to the default font for that
+ * kind of control if the argument is null.
+ *
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the font has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setFont (Font font) {
+    checkWidget();
+    if (font !is null && font.isDisposed ()) {
+        error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    this.font = font;
+    if (hasFocus ()) setIMEFont ();
+}
+
+/**
+ * Sets the image that the receiver will use to paint the caret
+ * to the image specified by the argument, or to the default
+ * which is a filled rectangle if the argument is null
+ *
+ * @param image the new image (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (Image image) {
+    checkWidget();
+    if (image !is null && image.isDisposed ()) {
+        error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    this.image = image;
+    if (isVisible && hasFocus ()) resize ();
+}
+
+void setIMEFont () {
+    if (!OS.IsDBLocale) return;
+    int hFont = 0;
+    if (font !is null) hFont = font.handle;
+    if (hFont is 0) hFont = defaultFont ();
+    int hwnd = parent.handle;
+    int hIMC = OS.ImmGetContext (hwnd);
+    /* Save the current IME font */
+    if (oldFont is null) {
+        oldFont = OS.IsUnicode ? (LOGFONT) new LOGFONTW () : new LOGFONTA ();
+        if (!OS.ImmGetCompositionFont (hIMC, oldFont)) oldFont = null;
+    }
+    /* Set new IME font */
+    LOGFONT logFont = OS.IsUnicode ? (LOGFONT) new LOGFONTW () : new LOGFONTA ();
+    if (OS.GetObject (hFont, LOGFONT.sizeof, logFont) !is 0) {
+        OS.ImmSetCompositionFont (hIMC, logFont);
+    }
+    OS.ImmReleaseContext (hwnd, hIMC);
+}
+
+/**
+ * Sets the receiver's location to the point specified by
+ * the arguments which are relative to the receiver's
+ * parent (or its display if its parent is null).
+ *
+ * @param x the new x coordinate for the receiver
+ * @param y the new y coordinate for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLocation (int x, int y) {
+    checkWidget();
+    if (this.x is x && this.y is y) return;
+    this.x = x;  this.y = y;
+    moved = true;
+    if (isVisible && hasFocus ()) move ();
+}
+
+/**
+ * Sets the receiver's location to the point specified by
+ * the argument which is relative to the receiver's
+ * parent (or its display if its parent is null).
+ *
+ * @param location the new location for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLocation (Point location) {
+    checkWidget();
+    if (location is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setLocation (location.x, location.y);
+}
+
+/**
+ * Sets the receiver's size to the point specified by the arguments.
+ *
+ * @param width the new width for the receiver
+ * @param height the new height for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSize (int width, int height) {
+    checkWidget();
+    if (this.width is width && this.height is height) return;
+    this.width = width;  this.height = height;
+    resized = true;
+    if (isVisible && hasFocus ()) resize ();
+}
+
+/**
+ * Sets the receiver's size to the point specified by the argument.
+ *
+ * @param size the new extent for the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSize (Point size) {
+    checkWidget();
+    if (size is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setSize (size.x, size.y);
+}
+
+/**
+ * Marks the receiver as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param visible the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setVisible (bool visible) {
+    checkWidget();
+    if (visible is isVisible) return;
+    isVisible = visible;
+    int hwnd = parent.handle;
+    if (OS.GetFocus () !is hwnd) return;
+    if (!isVisible) {
+        OS.HideCaret (hwnd);
+    } else {
+        if (resized) {
+            resize ();
+        } else {
+            if (moved) move ();
+        }
+        OS.ShowCaret (hwnd);
+    }
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/ColorDialog.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.ColorDialog;
+
+import dwt.widgets.Dialog;
+import dwt.widgets.Shell;
+
+class ColorDialog : Dialog {
+    public this (Shell parent, int style) {
+        super (parent, style);
+    }
+}
+
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+import dwt.internal.Callback;
+import dwt.internal.win32.CHOOSECOLOR;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class allow the user to select a color
+ * from a predefined set of available colors.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+
+public class ColorDialog extends Dialog {
+    Display display;
+    int width, height;
+    RGB rgb;
+
+/**
+ * Constructs a new instance of this class given only its parent.
+ *
+ * @param parent a composite control which will be the parent of the new instance
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public ColorDialog (Shell parent) {
+    this (parent, DWT.PRIMARY_MODAL);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public ColorDialog (Shell parent, int style) {
+    super (parent, style);
+    checkSubclass ();
+}
+
+int CCHookProc (int hdlg, int uiMsg, int lParam, int lpData) {
+    switch (uiMsg) {
+        case OS.WM_INITDIALOG: {
+            RECT rect = new RECT ();
+            OS.GetWindowRect (hdlg, rect);
+            width = rect.right - rect.left;
+            height = rect.bottom - rect.top;
+            if (title !is null && title.length () !is 0) {
+                /* Use the character encoding for the default locale */
+                TCHAR buffer = new TCHAR (0, title, true);
+                OS.SetWindowText (hdlg, buffer);
+            }
+            break;
+        }
+        case OS.WM_DESTROY: {
+            RECT rect = new RECT ();
+            OS.GetWindowRect (hdlg, rect);
+            int newWidth = rect.right - rect.left;
+            int newHeight = rect.bottom - rect.top;
+            if (newWidth < width || newHeight < height) {
+                //display.fullOpen = false;
+            } else {
+                if (newWidth > width || newHeight > height) {
+                    //display.fullOpen = true;
+                }
+            }
+            break;
+        }
+    }
+    return 0;
+}
+
+/**
+ * Returns the currently selected color in the receiver.
+ *
+ * @return the RGB value for the selected color, may be null
+ *
+ * @see PaletteData#getRGBs
+ */
+public RGB getRGB () {
+    return rgb;
+}
+
+/**
+ * Makes the receiver visible and brings it to the front
+ * of the display.
+ *
+ * @return the selected color, or null if the dialog was
+ *         cancelled, no color was selected, or an error
+ *         occurred
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public RGB open () {
+
+    /* Get the owner HWND for the dialog */
+    int hwndOwner = parent.handle;
+
+    /* Create the CCHookProc */
+    Callback callback = new Callback (this, "CCHookProc", 4); //$NON-NLS-1$
+    int lpfnHook = callback.getAddress ();
+    if (lpfnHook is 0) DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
+
+    /* Allocate the Custom Colors */
+    display = parent.display;
+    if (display.lpCustColors is 0) {
+        int hHeap = OS.GetProcessHeap ();
+        display.lpCustColors = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, 16 * 4);
+    }
+
+    /* Open the dialog */
+    CHOOSECOLOR lpcc = new CHOOSECOLOR ();
+    lpcc.lStructSize = CHOOSECOLOR.sizeof;
+    lpcc.Flags = OS.CC_ANYCOLOR | OS.CC_ENABLEHOOK;
+    //if (display.fullOpen) lpcc.Flags |= OS.CC_FULLOPEN;
+    lpcc.lpfnHook = lpfnHook;
+    lpcc.hwndOwner = hwndOwner;
+    lpcc.lpCustColors = display.lpCustColors;
+    if (rgb !is null) {
+        lpcc.Flags |= OS.CC_RGBINIT;
+        int red = rgb.red & 0xFF;
+        int green = (rgb.green << 8) & 0xFF00;
+        int blue = (rgb.blue << 16) & 0xFF0000;
+        lpcc.rgbResult = red | green | blue;
+    }
+
+    /* Make the parent shell be temporary modal */
+    Shell oldModal = null;
+    if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) !is 0) {
+        oldModal = display.getModalDialogShell ();
+        display.setModalDialogShell (parent);
+    }
+
+    /* Open the dialog */
+    bool success = OS.ChooseColor (lpcc);
+
+    /* Clear the temporary dialog modal parent */
+    if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) !is 0) {
+        display.setModalDialogShell (oldModal);
+    }
+
+    if (success) {
+        int red = lpcc.rgbResult & 0xFF;
+        int green = (lpcc.rgbResult >> 8) & 0xFF;
+        int blue = (lpcc.rgbResult >> 16) & 0xFF;
+        rgb = new RGB (red, green, blue);
+    }
+
+    /* Free the CCHookProc */
+    callback.dispose ();
+
+    /* Free the Custom Colors */
+    /*
+    * This code is intentionally commented.  Currently,
+    * there is exactly one set of custom colors per display.
+    * The memory associated with these colors is released
+    * when the display is disposed.
+    */
+//  if (lpCustColors !is 0) OS.HeapFree (hHeap, 0, lpCustColors);
+
+    /*
+    * This code is intentionally commented.  On some
+    * platforms, the owner window is repainted right
+    * away when a dialog window exits.  This behavior
+    * is currently unspecified.
+    */
+//  if (hwndOwner !is 0) OS.UpdateWindow (hwndOwner);
+
+    if (!success) return null;
+    return rgb;
+}
+
+/**
+ * Sets the receiver's selected color to be the argument.
+ *
+ * @param rgb the new RGB value for the selected color, may be
+ *        null to let the platform select a default when
+ *        open() is called
+ * @see PaletteData#getRGBs
+ */
+public void setRGB (RGB rgb) {
+    this.rgb = rgb;
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Combo.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,2447 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Combo;
+
+import dwt.widgets.Composite;
+
+class Combo : Composite {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.ModifyListener;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.events.VerifyListener;
+import dwt.graphics.Font;
+import dwt.graphics.Point;
+import dwt.internal.Callback;
+import dwt.internal.win32.COMBOBOXINFO;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MONITORINFO;
+import dwt.internal.win32.MSG;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class are controls that allow the user
+ * to choose an item from a list of items, or optionally
+ * enter a new value by typing it into an editable text
+ * field. Often, <code>Combo</code>s are used in the same place
+ * where a single selection <code>List</code> widget could
+ * be used but space is limited. A <code>Combo</code> takes
+ * less space than a <code>List</code> widget and shows
+ * similar information.
+ * <p>
+ * Note: Since <code>Combo</code>s can contain both a list
+ * and an editable text field, it is possible to confuse methods
+ * which access one versus the other (compare for example,
+ * <code>clearSelection()</code> and <code>deselectAll()</code>).
+ * The API documentation is careful to indicate either "the
+ * receiver's list" or the "the receiver's text field" to
+ * distinguish between the two cases.
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to add children to it, or set a layout on it.
+ * </p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>DROP_DOWN, READ_ONLY, SIMPLE</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>DefaultSelection, Modify, Selection, Verify</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles DROP_DOWN and SIMPLE may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see List
+ */
+
+public class Combo extends Composite {
+    bool noSelection, ignoreDefaultSelection, ignoreCharacter, ignoreModify;
+    int cbtHook, scrollWidth, visibleCount = 5;
+
+    /**
+     * the operating system limit for the number of characters
+     * that the text field in an instance of this class can hold
+     */
+    public static final int LIMIT;
+
+    /*
+     * These values can be different on different platforms.
+     * Therefore they are not initialized in the declaration
+     * to stop the compiler from inlining.
+     */
+    static {
+        LIMIT = OS.IsWinNT ? 0x7FFFFFFF : 0x7FFF;
+    }
+
+    /*
+     * These are the undocumented control id's for the children of
+     * a combo box.  Since there are no constants for these values,
+     * they may change with different versions of Windows (but have
+     * been the same since Windows 3.0).
+     */
+    static final int CBID_LIST = 1000;
+    static final int CBID_EDIT = 1001;
+    static /*final*/ int EditProc, ListProc;
+
+    static final int ComboProc;
+    static final TCHAR ComboClass = new TCHAR (0, "COMBOBOX", true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, ComboClass, lpWndClass);
+        ComboProc = lpWndClass.lpfnWndProc;
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#DROP_DOWN
+ * @see DWT#READ_ONLY
+ * @see DWT#SIMPLE
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Combo (Composite parent, int style) {
+    super (parent, checkStyle (style));
+    /* This code is intentionally commented */
+    //if ((style & DWT.H_SCROLL) !is 0) this.style |= DWT.H_SCROLL;
+    this.style |= DWT.H_SCROLL;
+}
+
+/**
+ * Adds the argument to the end of the receiver's list.
+ *
+ * @param string the new item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #add(String,int)
+ */
+public void add (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    int result = OS.SendMessage (handle, OS.CB_ADDSTRING, 0, buffer);
+    if (result is OS.CB_ERR) error (DWT.ERROR_ITEM_NOT_ADDED);
+    if (result is OS.CB_ERRSPACE) error (DWT.ERROR_ITEM_NOT_ADDED);
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth (buffer, true);
+}
+
+/**
+ * Adds the argument to the receiver's list at the given
+ * zero-relative index.
+ * <p>
+ * Note: To add an item at the end of the list, use the
+ * result of calling <code>getItemCount()</code> as the
+ * index or use <code>add(String)</code>.
+ * </p>
+ *
+ * @param string the new item
+ * @param index the index for the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #add(String)
+ */
+public void add (String string, int index) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+    if (!(0 <= index && index <= count)) {
+        error (DWT.ERROR_INVALID_RANGE);
+    }
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    int result = OS.SendMessage (handle, OS.CB_INSERTSTRING, index, buffer);
+    if (result is OS.CB_ERRSPACE || result is OS.CB_ERR) {
+        error (DWT.ERROR_ITEM_NOT_ADDED);
+    }
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth (buffer, true);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver's text is modified, by sending
+ * it one of the messages defined in the <code>ModifyListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ModifyListener
+ * @see #removeModifyListener
+ */
+public void addModifyListener (ModifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Modify, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's selection, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the user changes the combo's list selection.
+ * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed the combo's text area.
+ * </p>
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver's text is verified, by sending
+ * it one of the messages defined in the <code>VerifyListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see VerifyListener
+ * @see #removeVerifyListener
+ *
+ * @since 3.1
+ */
+public void addVerifyListener (VerifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Verify, typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    if (hwnd is handle) {
+        return OS.CallWindowProc (ComboProc, hwnd, msg, wParam, lParam);
+    }
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwnd is hwndText) {
+        return OS.CallWindowProc (EditProc, hwnd, msg, wParam, lParam);
+    }
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwnd is hwndList) {
+        return OS.CallWindowProc (ListProc, hwnd, msg, wParam, lParam);
+    }
+    return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+}
+
+int CBTProc (int nCode, int wParam, int lParam) {
+    if (nCode is OS.HCBT_CREATEWND) {
+        TCHAR buffer = new TCHAR (0, 128);
+        OS.GetClassName (wParam, buffer, buffer.length ());
+        String className = buffer.toString (0, buffer.strlen ());
+        if (className.equals ("Edit") || className.equals ("EDIT")) { //$NON-NLS-1$  //$NON-NLS-2$
+            int bits = OS.GetWindowLong (wParam, OS.GWL_STYLE);
+            OS.SetWindowLong (wParam, OS.GWL_STYLE, bits & ~OS.ES_NOHIDESEL);
+        }
+    }
+    return OS.CallNextHookEx (cbtHook, nCode, wParam, lParam);
+}
+
+bool checkHandle (int hwnd) {
+    return hwnd is handle || hwnd is OS.GetDlgItem (handle, CBID_EDIT) || hwnd is OS.GetDlgItem (handle, CBID_LIST);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+static int checkStyle (int style) {
+    /*
+     * Feature in Windows.  It is not possible to create
+     * a combo box that has a border using Windows style
+     * bits.  All combo boxes draw their own border and
+     * do not use the standard Windows border styles.
+     * Therefore, no matter what style bits are specified,
+     * clear the BORDER bits so that the DWT style will
+     * match the Windows widget.
+     *
+     * The Windows behavior is currently implemented on
+     * all platforms.
+     */
+    style &= ~DWT.BORDER;
+
+    /*
+     * Even though it is legal to create this widget
+     * with scroll bars, they serve no useful purpose
+     * because they do not automatically scroll the
+     * widget's client area.  The fix is to clear
+     * the DWT style.
+     */
+    style &= ~(DWT.H_SCROLL | DWT.V_SCROLL);
+    style = checkBits (style, DWT.DROP_DOWN, DWT.SIMPLE, 0, 0, 0, 0);
+    if ((style & DWT.SIMPLE) !is 0) return style & ~DWT.READ_ONLY;
+    return style;
+}
+
+/**
+ * Sets the selection in the receiver's text field to an empty
+ * selection starting just before the first character. If the
+ * text field is editable, this has the effect of placing the
+ * i-beam at the start of the text.
+ * <p>
+ * Note: To clear the selected items in the receiver's list,
+ * use <code>deselectAll()</code>.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #deselectAll
+ */
+public void clearSelection () {
+    checkWidget ();
+    OS.SendMessage (handle, OS.CB_SETEDITSEL, 0, -1);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = 0, height = 0;
+    if (wHint is DWT.DEFAULT) {
+        int newFont, oldFont = 0;
+        int hDC = OS.GetDC (handle);
+        newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+        int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+        RECT rect = new RECT ();
+        int flags = OS.DT_CALCRECT | OS.DT_NOPREFIX;
+        if ((style & DWT.READ_ONLY) is 0) flags |= OS.DT_EDITCONTROL;
+        int length = OS.GetWindowTextLength (handle);
+        int cp = getCodePage ();
+        TCHAR buffer = new TCHAR (cp, length + 1);
+        OS.GetWindowText (handle, buffer, length + 1);
+        OS.DrawText (hDC, buffer, length, rect, flags);
+        width = Math.max (width, rect.right - rect.left);
+        if ((style & DWT.H_SCROLL) !is 0) {
+            width = Math.max (width, scrollWidth);
+        } else {
+            for (int i=0; i<count; i++) {
+                length = OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, i, 0);
+                if (length !is OS.CB_ERR) {
+                    if (length + 1 > buffer.length ()) buffer = new TCHAR (cp, length + 1);
+                    int result = OS.SendMessage (handle, OS.CB_GETLBTEXT, i, buffer);
+                    if (result !is OS.CB_ERR) {
+                        OS.DrawText (hDC, buffer, length, rect, flags);
+                        width = Math.max (width, rect.right - rect.left);
+                    }
+                }
+            }
+        }
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+    }
+    if (hHint is DWT.DEFAULT) {
+        if ((style & DWT.SIMPLE) !is 0) {
+            int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+            int itemHeight = OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, 0, 0);
+            height = count * itemHeight;
+        }
+    }
+    if (width is 0) width = DEFAULT_WIDTH;
+    if (height is 0) height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    if ((style & DWT.READ_ONLY) !is 0) {
+        width += 8;
+    } else {
+        int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+        if (hwndText !is 0) {
+            int margins = OS.SendMessage (hwndText, OS.EM_GETMARGINS, 0, 0);
+            int marginWidth = (margins & 0xFFFF) + ((margins >> 16) & 0xFFFF);
+            width += marginWidth + 3;
+        }
+    }
+    COMBOBOXINFO pcbi = new COMBOBOXINFO ();
+    pcbi.cbSize = COMBOBOXINFO.sizeof;
+    if (((style & DWT.SIMPLE) is 0) && !OS.IsWinCE && OS.GetComboBoxInfo (handle, pcbi)) {
+        width += pcbi.itemLeft + (pcbi.buttonRight - pcbi.buttonLeft);
+        height = (pcbi.buttonBottom - pcbi.buttonTop) + pcbi.buttonTop * 2;
+    } else {
+        int border = OS.GetSystemMetrics (OS.SM_CXEDGE);
+        width += OS.GetSystemMetrics (OS.SM_CXVSCROLL) + border * 2;
+        int textHeight = OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, -1, 0);
+        if ((style & DWT.DROP_DOWN) !is 0) {
+            height = textHeight + 6;
+        } else {
+            height += textHeight + 10;
+        }
+    }
+    if ((style & DWT.SIMPLE) !is 0 && (style & DWT.H_SCROLL) !is 0) {
+        height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    }
+    return new Point (width, height);
+}
+
+/**
+ * Copies the selected text.
+ * <p>
+ * The current selection is copied to the clipboard.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public void copy () {
+    checkWidget ();
+    OS.SendMessage (handle, OS.WM_COPY, 0, 0);
+}
+
+void createHandle () {
+    /*
+    * Feature in Windows.  When the selection changes in a combo box,
+    * Windows draws the selection, even when the combo box does not
+    * have focus.  Strictly speaking, this is the correct Windows
+    * behavior because the combo box sets ES_NOHIDESEL on the text
+    * control that it creates.  Despite this, it looks strange because
+    * Windows also clears the selection and selects all the text when
+    * the combo box gets focus.  The fix is use the CBT hook to clear
+    * the ES_NOHIDESEL style bit when the text control is created.
+    */
+    if (OS.IsWinCE || (style & (DWT.READ_ONLY | DWT.SIMPLE)) !is 0) {
+        super.createHandle ();
+    } else {
+        int threadId = OS.GetCurrentThreadId ();
+        Callback cbtCallback = new Callback (this, "CBTProc", 3); //$NON-NLS-1$
+        int cbtProc = cbtCallback.getAddress ();
+        if (cbtProc is 0) error (DWT.ERROR_NO_MORE_CALLBACKS);
+        cbtHook = OS.SetWindowsHookEx (OS.WH_CBT, cbtProc, 0, threadId);
+        super.createHandle ();
+        if (cbtHook !is 0) OS.UnhookWindowsHookEx (cbtHook);
+        cbtHook = 0;
+        cbtCallback.dispose ();
+    }
+    state &= ~(CANVAS | THEME_BACKGROUND);
+
+    /* Get the text and list window procs */
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText !is 0 && EditProc is 0) {
+        EditProc = OS.GetWindowLong (hwndText, OS.GWL_WNDPROC);
+    }
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndList !is 0 && ListProc is 0) {
+        ListProc = OS.GetWindowLong (hwndList, OS.GWL_WNDPROC);
+    }
+
+    /*
+    * Bug in Windows.  If the combo box has the CBS_SIMPLE style,
+    * the list portion of the combo box is not drawn correctly the
+    * first time, causing pixel corruption.  The fix is to ensure
+    * that the combo box has been resized more than once.
+    */
+    if ((style & DWT.SIMPLE) !is 0) {
+        int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE;
+        SetWindowPos (handle, 0, 0, 0, 0x3FFF, 0x3FFF, flags);
+        SetWindowPos (handle, 0, 0, 0, 0, 0, flags);
+    }
+}
+
+/**
+ * Cuts the selected text.
+ * <p>
+ * The current selection is first copied to the
+ * clipboard and then deleted from the widget.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public void cut () {
+    checkWidget ();
+    if ((style & DWT.READ_ONLY) !is 0) return;
+    OS.SendMessage (handle, OS.WM_CUT, 0, 0);
+}
+
+int defaultBackground () {
+    return OS.GetSysColor (OS.COLOR_WINDOW);
+}
+
+void deregister () {
+    super.deregister ();
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText !is 0) display.removeControl (hwndText);
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndList !is 0) display.removeControl (hwndList);
+}
+
+/**
+ * Deselects the item at the given zero-relative index in the receiver's
+ * list.  If the item at the index was already deselected, it remains
+ * deselected. Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to deselect
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int index) {
+    checkWidget ();
+    int selection = OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
+    if (index !is selection) return;
+    OS.SendMessage (handle, OS.CB_SETCURSEL, -1, 0);
+    sendEvent (DWT.Modify);
+    // widget could be disposed at this point
+}
+
+/**
+ * Deselects all selected items in the receiver's list.
+ * <p>
+ * Note: To clear the selection in the receiver's text field,
+ * use <code>clearSelection()</code>.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #clearSelection
+ */
+public void deselectAll () {
+    checkWidget ();
+    OS.SendMessage (handle, OS.CB_SETCURSEL, -1, 0);
+    sendEvent (DWT.Modify);
+    // widget could be disposed at this point
+}
+
+bool dragDetect (int hwnd, int x, int y, bool filter, bool [] detect, bool [] consume) {
+    if (filter && (style & DWT.READ_ONLY) is 0) {
+        int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+        if (hwndText !is 0) {
+            int [] start = new int [1], end = new int [1];
+            OS.SendMessage (handle, OS.CB_GETEDITSEL, start, end);
+            if (start [0] !is end [0]) {
+                int lParam = (x & 0xFFFF) | ((y << 16) & 0xFFFF0000);
+                int position = OS.SendMessage (hwndText, OS.EM_CHARFROMPOS, 0, lParam) & 0xFFFF;
+                if (start [0] <= position && position < end [0]) {
+                    if (super.dragDetect (hwnd, x, y, filter, detect, consume)) {
+                        if (consume !is null) consume [0] = true;
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+    return super.dragDetect (hwnd, x, y, filter, detect, consume);
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver's list. Throws an exception if the index is out
+ * of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getItem (int index) {
+    checkWidget ();
+    int length = OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, index, 0);
+    if (length !is OS.CB_ERR) {
+        TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+        int result = OS.SendMessage (handle, OS.CB_GETLBTEXT, index, buffer);
+        if (result !is OS.CB_ERR) return buffer.toString (0, length);
+    }
+    int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+    if (0 <= index && index < count) error (DWT.ERROR_CANNOT_GET_ITEM);
+    error (DWT.ERROR_INVALID_RANGE);
+    return "";
+}
+
+/**
+ * Returns the number of items contained in the receiver's list.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+    if (count is OS.CB_ERR) error (DWT.ERROR_CANNOT_GET_COUNT);
+    return count;
+}
+
+/**
+ * Returns the height of the area which would be used to
+ * display <em>one</em> of the items in the receiver's list.
+ *
+ * @return the height of one item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemHeight () {
+    checkWidget ();
+    int result = OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, 0, 0);
+    if (result is OS.CB_ERR) error (DWT.ERROR_CANNOT_GET_ITEM_HEIGHT);
+    return result;
+}
+
+/**
+ * Returns a (possibly empty) array of <code>String</code>s which are
+ * the items in the receiver's list.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver's list
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String [] getItems () {
+    checkWidget ();
+    int count = getItemCount ();
+    String [] result = new String [count];
+    for (int i=0; i<count; i++) result [i] = getItem (i);
+    return result;
+}
+
+/**
+ * Returns <code>true</code> if the receiver's list is visible,
+ * and <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's list's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+/*public*/ bool getListVisible () {
+    checkWidget ();
+    if ((style & DWT.DROP_DOWN) !is 0) {
+        return OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) !is 0;
+    }
+    return true;
+}
+
+String getNameText () {
+    return getText ();
+}
+
+/**
+ * Returns the orientation of the receiver.
+ *
+ * @return the orientation style
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public int getOrientation () {
+    checkWidget();
+    return style & (DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT);
+}
+
+/**
+ * Returns a <code>Point</code> whose x coordinate is the
+ * character position representing the start of the selection
+ * in the receiver's text field, and whose y coordinate is the
+ * character position representing the end of the selection.
+ * An "empty" selection is indicated by the x and y coordinates
+ * having the same value.
+ * <p>
+ * Indexing is zero based.  The range of a selection is from
+ * 0..N where N is the number of characters in the widget.
+ * </p>
+ *
+ * @return a point representing the selection start and end
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getSelection () {
+    checkWidget ();
+    if ((style & DWT.DROP_DOWN) !is 0 && (style & DWT.READ_ONLY) !is 0) {
+        return new Point (0, OS.GetWindowTextLength (handle));
+    }
+    int [] start = new int [1], end = new int [1];
+    OS.SendMessage (handle, OS.CB_GETEDITSEL, start, end);
+    if (!OS.IsUnicode && OS.IsDBLocale) {
+        start [0] = mbcsToWcsPos (start [0]);
+        end [0] = mbcsToWcsPos (end [0]);
+    }
+    return new Point (start [0], end [0]);
+}
+
+/**
+ * Returns the zero-relative index of the item which is currently
+ * selected in the receiver's list, or -1 if no item is selected.
+ *
+ * @return the index of the selected item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionIndex () {
+    checkWidget ();
+    if (noSelection) return -1;
+    return OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
+}
+
+/**
+ * Returns a string containing a copy of the contents of the
+ * receiver's text field, or an empty string if there are no
+ * contents.
+ *
+ * @return the receiver's text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText () {
+    checkWidget ();
+    int length = OS.GetWindowTextLength (handle);
+    if (length is 0) return "";
+    TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+    OS.GetWindowText (handle, buffer, length + 1);
+    return buffer.toString (0, length);
+}
+
+/**
+ * Returns the height of the receivers's text field.
+ *
+ * @return the text height
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTextHeight () {
+    checkWidget ();
+    COMBOBOXINFO pcbi = new COMBOBOXINFO ();
+    pcbi.cbSize = COMBOBOXINFO.sizeof;
+    if (((style & DWT.SIMPLE) is 0) && !OS.IsWinCE && OS.GetComboBoxInfo (handle, pcbi)) {
+        return (pcbi.buttonBottom - pcbi.buttonTop) + pcbi.buttonTop * 2;
+    }
+    int result = OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, -1, 0);
+    if (result is OS.CB_ERR) error (DWT.ERROR_CANNOT_GET_ITEM_HEIGHT);
+    return (style & DWT.DROP_DOWN) !is 0 ? result + 6 : result + 10;
+}
+
+/**
+ * Returns the maximum number of characters that the receiver's
+ * text field is capable of holding. If this has not been changed
+ * by <code>setTextLimit()</code>, it will be the constant
+ * <code>Combo.LIMIT</code>.
+ *
+ * @return the text limit
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #LIMIT
+ */
+public int getTextLimit () {
+    checkWidget ();
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText is 0) return LIMIT;
+    return OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF;
+}
+
+/**
+ * Gets the number of items that are visible in the drop
+ * down portion of the receiver's list.
+ * <p>
+ * Note: This operation is a hint and is not supported on
+ * platforms that do not have this concept.
+ * </p>
+ *
+ * @return the number of items that are visible
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public int getVisibleItemCount () {
+    checkWidget ();
+    return visibleCount;
+}
+
+bool hasFocus () {
+    int hwndFocus = OS.GetFocus ();
+    if (hwndFocus is handle) return true;
+    if (hwndFocus is 0) return false;
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndFocus is hwndText) return true;
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndFocus is hwndList) return true;
+    return false;
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param string the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (String string) {
+    return indexOf (string, 0);
+}
+
+/**
+ * Searches the receiver's list starting at the given,
+ * zero-relative index until an item is found that is equal
+ * to the argument, and returns the index of that item. If
+ * no item is found or the starting index is out of range,
+ * returns -1.
+ *
+ * @param string the search item
+ * @param start the zero-relative index at which to begin the search
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (String string, int start) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+
+    /*
+    * Bug in Windows.  For some reason, CB_FINDSTRINGEXACT
+    * will not find empty strings even though it is legal
+    * to insert an empty string into a combo.  The fix is
+    * to search the combo, an item at a time.
+    */
+    if (string.length () is 0) {
+        int count = getItemCount ();
+        for (int i=start; i<count; i++) {
+            if (string.equals (getItem (i))) return i;
+        }
+        return -1;
+    }
+
+    /* Use CB_FINDSTRINGEXACT to search for the item */
+    int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+    if (!(0 <= start && start < count)) return -1;
+    int index = start - 1, last = 0;
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    do {
+        index = OS.SendMessage (handle, OS.CB_FINDSTRINGEXACT, last = index, buffer);
+        if (index is OS.CB_ERR || index <= last) return -1;
+    } while (!string.equals (getItem (index)));
+    return index;
+}
+
+int mbcsToWcsPos (int mbcsPos) {
+    if (mbcsPos <= 0) return 0;
+    if (OS.IsUnicode) return mbcsPos;
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText is 0) return mbcsPos;
+    int mbcsSize = OS.GetWindowTextLengthA (hwndText);
+    if (mbcsSize is 0) return 0;
+    if (mbcsPos >= mbcsSize) return mbcsSize;
+    byte [] buffer = new byte [mbcsSize + 1];
+    OS.GetWindowTextA (hwndText, buffer, mbcsSize + 1);
+    return OS.MultiByteToWideChar (getCodePage (), OS.MB_PRECOMPOSED, buffer, mbcsPos, null, 0);
+}
+
+/**
+ * Pastes text from clipboard.
+ * <p>
+ * The selected text is deleted from the widget
+ * and new text inserted from the clipboard.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public void paste () {
+    checkWidget ();
+    if ((style & DWT.READ_ONLY) !is 0) return;
+    OS.SendMessage (handle, OS.WM_PASTE, 0, 0);
+}
+
+void register () {
+    super.register ();
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText !is 0) display.addControl (hwndText, this);
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndList !is 0) display.addControl (hwndList, this);
+}
+
+/**
+ * Removes the item from the receiver's list at the given
+ * zero-relative index.
+ *
+ * @param index the index for the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int index) {
+    checkWidget ();
+    remove (index, true);
+}
+
+void remove (int index, bool notify) {
+    TCHAR buffer = null;
+    if ((style & DWT.H_SCROLL) !is 0) {
+        int length = OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, index, 0);
+        if (length is OS.CB_ERR) {
+            int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+            if (0 <= index && index < count) error (DWT.ERROR_ITEM_NOT_REMOVED);
+            error (DWT.ERROR_INVALID_RANGE);
+        }
+        buffer = new TCHAR (getCodePage (), length + 1);
+        int result = OS.SendMessage (handle, OS.CB_GETLBTEXT, index, buffer);
+        if (result is OS.CB_ERR) {
+            int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+            if (0 <= index && index < count) error (DWT.ERROR_ITEM_NOT_REMOVED);
+            error (DWT.ERROR_INVALID_RANGE);
+        }
+    }
+    int length = OS.GetWindowTextLength (handle);
+    int code = OS.SendMessage (handle, OS.CB_DELETESTRING, index, 0);
+    if (code is OS.CB_ERR) {
+        int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+        if (0 <= index && index < count) error (DWT.ERROR_ITEM_NOT_REMOVED);
+        error (DWT.ERROR_INVALID_RANGE);
+    }
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth (buffer, true);
+    if (notify && length !is OS.GetWindowTextLength (handle)) {
+        sendEvent (DWT.Modify);
+        if (isDisposed ()) return;
+    }
+    /*
+    * Bug in Windows.  When the combo box is read only
+    * with exactly one item that is currently selected
+    * and that item is removed, the combo box does not
+    * redraw to clear the text area.  The fix is to
+    * force a redraw.
+    */
+    if ((style & DWT.READ_ONLY) !is 0) {
+        int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+        if (count is 0) OS.InvalidateRect (handle, null, true);
+    }
+}
+
+/**
+ * Removes the items from the receiver's list which are
+ * between the given zero-relative start and end
+ * indices (inclusive).
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int start, int end) {
+    checkWidget ();
+    if (start > end) return;
+    int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+    if (!(0 <= start && start <= end && end < count)) {
+        error (DWT.ERROR_INVALID_RANGE);
+    }
+    int textLength = OS.GetWindowTextLength (handle);
+    RECT rect = null;
+    int hDC = 0, oldFont = 0, newFont = 0, newWidth = 0;
+    if ((style & DWT.H_SCROLL) !is 0) {
+        rect = new RECT ();
+        hDC = OS.GetDC (handle);
+        newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    }
+    int cp = getCodePage ();
+    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+    for (int i=start; i<=end; i++) {
+        TCHAR buffer = null;
+        if ((style & DWT.H_SCROLL) !is 0) {
+            int length = OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, start, 0);
+            if (length is OS.CB_ERR) break;
+            buffer = new TCHAR (cp, length + 1);
+            int result = OS.SendMessage (handle, OS.CB_GETLBTEXT, start, buffer);
+            if (result is OS.CB_ERR) break;
+        }
+        int result = OS.SendMessage (handle, OS.CB_DELETESTRING, start, 0);
+        if (result is OS.CB_ERR) error (DWT.ERROR_ITEM_NOT_REMOVED);
+        if ((style & DWT.H_SCROLL) !is 0) {
+            OS.DrawText (hDC, buffer, -1, rect, flags);
+            newWidth = Math.max (newWidth, rect.right - rect.left);
+        }
+    }
+    if ((style & DWT.H_SCROLL) !is 0) {
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+        setScrollWidth (newWidth, false);
+    }
+    if (textLength !is OS.GetWindowTextLength (handle)) {
+        sendEvent (DWT.Modify);
+        if (isDisposed ()) return;
+    }
+    /*
+    * Bug in Windows.  When the combo box is read only
+    * with exactly one item that is currently selected
+    * and that item is removed, the combo box does not
+    * redraw to clear the text area.  The fix is to
+    * force a redraw.
+    */
+    if ((style & DWT.READ_ONLY) !is 0) {
+        count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+        if (count is 0) OS.InvalidateRect (handle, null, true);
+    }
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * until an item is found that is equal to the argument,
+ * and removes that item from the list.
+ *
+ * @param string the item to remove
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int index = indexOf (string, 0);
+    if (index is -1) error (DWT.ERROR_INVALID_ARGUMENT);
+    remove (index);
+}
+
+/**
+ * Removes all of the items from the receiver's list and clear the
+ * contents of receiver's text field.
+ * <p>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void removeAll () {
+    checkWidget ();
+    OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0);
+    sendEvent (DWT.Modify);
+    if (isDisposed ()) return;
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth (0);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the receiver's text is modified.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ModifyListener
+ * @see #addModifyListener
+ */
+public void removeModifyListener (ModifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Modify, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's selection.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is verified.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see VerifyListener
+ * @see #addVerifyListener
+ *
+ * @since 3.1
+ */
+public void removeVerifyListener (VerifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Verify, listener);
+}
+
+bool sendKeyEvent (int type, int msg, int wParam, int lParam, Event event) {
+    if (!super.sendKeyEvent (type, msg, wParam, lParam, event)) {
+        return false;
+    }
+    if ((style & DWT.READ_ONLY) !is 0) return true;
+    if (type !is DWT.KeyDown) return true;
+    if (msg !is OS.WM_CHAR && msg !is OS.WM_KEYDOWN && msg !is OS.WM_IME_CHAR) {
+        return true;
+    }
+    if (event.character is 0) return true;
+    if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return true;
+    char key = event.character;
+    int stateMask = event.stateMask;
+
+    /*
+    * Disable all magic keys that could modify the text
+    * and don't send events when Alt, Shift or Ctrl is
+    * pressed.
+    */
+    switch (msg) {
+        case OS.WM_CHAR:
+            if (key !is 0x08 && key !is 0x7F && key !is '\r' && key !is '\t' && key !is '\n') break;
+            // FALL THROUGH
+        case OS.WM_KEYDOWN:
+            if ((stateMask & (DWT.ALT | DWT.SHIFT | DWT.CONTROL)) !is 0) return false;
+            break;
+    }
+
+    /*
+    * If the left button is down, the text widget refuses the character.
+    */
+    if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
+        return true;
+    }
+
+    /* Verify the character */
+    String oldText = "";
+    int [] start = new int [1], end = new int [1];
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText is 0) return true;
+    OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
+    switch (key) {
+        case 0x08:  /* Bs */
+            if (start [0] is end [0]) {
+                if (start [0] is 0) return true;
+                start [0] = start [0] - 1;
+                if (!OS.IsUnicode && OS.IsDBLocale) {
+                    int [] newStart = new int [1], newEnd = new int [1];
+                    OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
+                    OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd);
+                    if (start [0] !is newStart [0]) start [0] = start [0] - 1;
+                }
+                start [0] = Math.max (start [0], 0);
+            }
+            break;
+        case 0x7F:  /* Del */
+            if (start [0] is end [0]) {
+                int length = OS.GetWindowTextLength (hwndText);
+                if (start [0] is length) return true;
+                end [0] = end [0] + 1;
+                if (!OS.IsUnicode && OS.IsDBLocale) {
+                    int [] newStart = new int [1], newEnd = new int [1];
+                    OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
+                    OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd);
+                    if (end [0] !is newEnd [0]) end [0] = end [0] + 1;
+                }
+                end [0] = Math.min (end [0], length);
+            }
+            break;
+        case '\r':  /* Return */
+            return true;
+        default:    /* Tab and other characters */
+            if (key !is '\t' && key < 0x20) return true;
+            oldText = new String (new char [] {key});
+            break;
+    }
+    String newText = verifyText (oldText, start [0], end [0], event);
+    if (newText is null) return false;
+    if (newText is oldText) return true;
+    TCHAR buffer = new TCHAR (getCodePage (), newText, true);
+    OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
+    OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer);
+    return false;
+}
+
+/**
+ * Selects the item at the given zero-relative index in the receiver's
+ * list.  If the item at the index was already selected, it remains
+ * selected. Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to select
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void select (int index) {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+    if (0 <= index && index < count) {
+        int selection = OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
+        int code = OS.SendMessage (handle, OS.CB_SETCURSEL, index, 0);
+        if (code !is OS.CB_ERR && code !is selection) {
+            sendEvent (DWT.Modify);
+            // widget could be disposed at this point
+        }
+    }
+}
+
+void setBackgroundImage (int hBitmap) {
+    super.setBackgroundImage (hBitmap);
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText !is 0) OS.InvalidateRect (hwndText, null, true);
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndList !is 0) OS.InvalidateRect (hwndList, null, true);
+}
+
+void setBackgroundPixel (int pixel) {
+    super.setBackgroundPixel (pixel);
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText !is 0) OS.InvalidateRect (hwndText, null, true);
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndList !is 0) OS.InvalidateRect (hwndList, null, true);
+}
+
+void setBounds (int x, int y, int width, int height, int flags) {
+    /*
+    * Feature in Windows.  If the combo box has the CBS_DROPDOWN
+    * or CBS_DROPDOWNLIST style, Windows uses the height that the
+    * programmer sets in SetWindowPos () to control height of the
+    * drop down list.  When the width is non-zero, Windows remembers
+    * this value and sets the height to be the height of the text
+    * field part of the combo box.  If the width is zero, Windows
+    * allows the height to have any value.  Therefore, when the
+    * programmer sets and then queries the height, the values can
+    * be different depending on the width.  The problem occurs when
+    * the programmer uses computeSize () to determine the preferred
+    * height (always the height of the text field) and then uses
+    * this value to set the height of the combo box.  The result
+    * is a combo box with a zero size drop down list.  The fix, is
+    * to always set the height to show a fixed number of combo box
+    * items and ignore the height value that the programmer supplies.
+    */
+    if ((style & DWT.DROP_DOWN) !is 0) {
+        height = getTextHeight () + (getItemHeight () * visibleCount) + 2;
+        /*
+        * Feature in Windows.  When a drop down combo box is resized,
+        * the combo box resizes the height of the text field and uses
+        * the height provided in SetWindowPos () to determine the height
+        * of the drop down list.  For some reason, the combo box redraws
+        * the whole area, not just the text field.  The fix is to set the
+        * SWP_NOSIZE bits when the height of text field and the drop down
+        * list is the same as the requested height.
+        *
+        * NOTE:  Setting the width of a combo box to zero does not update
+        * the width of the drop down control rect.  If the width of the
+        * combo box is zero, then do not set SWP_NOSIZE.
+        */
+        RECT rect = new RECT ();
+        OS.GetWindowRect (handle, rect);
+        if (rect.right - rect.left !is 0) {
+            if (OS.SendMessage (handle, OS.CB_GETDROPPEDCONTROLRECT, 0, rect) !is 0) {
+                int oldWidth = rect.right - rect.left, oldHeight = rect.bottom - rect.top;
+                if (oldWidth is width && oldHeight is height) flags |= OS.SWP_NOSIZE;
+            }
+        }
+        SetWindowPos (handle, 0, x, y, width, height, flags);
+    } else {
+        super.setBounds (x, y, width, height, flags);
+    }
+}
+
+public void setFont (Font font) {
+    checkWidget ();
+    super.setFont (font);
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth ();
+}
+
+void setForegroundPixel (int pixel) {
+    super.setForegroundPixel (pixel);
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText !is 0) OS.InvalidateRect (hwndText, null, true);
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndList !is 0) OS.InvalidateRect (hwndList, null, true);
+}
+
+/**
+ * Sets the text of the item in the receiver's list at the given
+ * zero-relative index to the string argument.
+ *
+ * @param index the index for the item
+ * @param string the new text for the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setItem (int index, String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int selection = getSelectionIndex ();
+    remove (index, false);
+    if (isDisposed ()) return;
+    add (string, index);
+    if (selection !is -1) select (selection);
+}
+
+/**
+ * Sets the receiver's list to be the given array of items.
+ *
+ * @param items the array of items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the items array is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setItems (String [] items) {
+    checkWidget ();
+    if (items is null) error (DWT.ERROR_NULL_ARGUMENT);
+    for (int i=0; i<items.length; i++) {
+        if (items [i] is null) error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    RECT rect = null;
+    int hDC = 0, oldFont = 0, newFont = 0, newWidth = 0;
+    if ((style & DWT.H_SCROLL) !is 0) {
+        rect = new RECT ();
+        hDC = OS.GetDC (handle);
+        newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+        setScrollWidth (0);
+    }
+    OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0);
+    int codePage = getCodePage ();
+    for (int i=0; i<items.length; i++) {
+        String string = items [i];
+        TCHAR buffer = new TCHAR (codePage, string, true);
+        int code = OS.SendMessage (handle, OS.CB_ADDSTRING, 0, buffer);
+        if (code is OS.CB_ERR) error (DWT.ERROR_ITEM_NOT_ADDED);
+        if (code is OS.CB_ERRSPACE) error (DWT.ERROR_ITEM_NOT_ADDED);
+        if ((style & DWT.H_SCROLL) !is 0) {
+            int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+            OS.DrawText (hDC, buffer, -1, rect, flags);
+            newWidth = Math.max (newWidth, rect.right - rect.left);
+        }
+    }
+    if ((style & DWT.H_SCROLL) !is 0) {
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+        setScrollWidth (newWidth + 3);
+    }
+    sendEvent (DWT.Modify);
+    // widget could be disposed at this point
+}
+
+/**
+ * Marks the receiver's list as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param visible the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+/*public*/ void setListVisible (bool visible) {
+    checkWidget ();
+    OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, visible ? 1 : 0, 0);
+}
+
+/**
+ * Sets the orientation of the receiver, which must be one
+ * of the constants <code>DWT.LEFT_TO_RIGHT</code> or <code>DWT.RIGHT_TO_LEFT</code>.
+ * <p>
+ *
+ * @param orientation new orientation style
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public void setOrientation (int orientation) {
+    checkWidget();
+    if (OS.IsWinCE) return;
+    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return;
+    int flags = DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT;
+    if ((orientation & flags) is 0 || (orientation & flags) is flags) return;
+    style &= ~flags;
+    style |= orientation & flags;
+    int bits  = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+        style |= DWT.MIRRORED;
+        bits |= OS.WS_EX_LAYOUTRTL;
+    } else {
+        style &= ~DWT.MIRRORED;
+        bits &= ~OS.WS_EX_LAYOUTRTL;
+    }
+    OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits);
+    int hwndText = 0, hwndList = 0;
+    COMBOBOXINFO pcbi = new COMBOBOXINFO ();
+    pcbi.cbSize = COMBOBOXINFO.sizeof;
+    if (OS.GetComboBoxInfo (handle, pcbi)) {
+        hwndText = pcbi.hwndItem;
+        hwndList = pcbi.hwndList;
+    }
+    if (hwndText !is 0) {
+        int bits1 = OS.GetWindowLong (hwndText, OS.GWL_EXSTYLE);
+        int bits2 = OS.GetWindowLong (hwndText, OS.GWL_STYLE);
+        if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+            bits1 |= OS.WS_EX_RIGHT | OS.WS_EX_RTLREADING;
+            bits2 |= OS.ES_RIGHT;
+        } else {
+            bits1 &= ~(OS.WS_EX_RIGHT | OS.WS_EX_RTLREADING);
+            bits2 &= ~OS.ES_RIGHT;
+        }
+        OS.SetWindowLong (hwndText, OS.GWL_EXSTYLE, bits1);
+        OS.SetWindowLong (hwndText, OS.GWL_STYLE, bits2);
+
+        /*
+        * Bug in Windows.  For some reason, the single line text field
+        * portion of the combo box does not redraw to reflect the new
+        * style bits.  The fix is to force the widget to be resized by
+        * temporarily shrinking and then growing the width and height.
+        */
+        RECT rect = new RECT ();
+        OS.GetWindowRect (hwndText, rect);
+        int width = rect.right - rect.left, height = rect.bottom - rect.top;
+        OS.GetWindowRect (handle, rect);
+        int widthCombo = rect.right - rect.left, heightCombo = rect.bottom - rect.top;
+        int uFlags = OS.SWP_NOMOVE | OS.SWP_NOZORDER | OS.SWP_NOACTIVATE;
+        SetWindowPos (hwndText, 0, 0, 0, width - 1, height - 1, uFlags);
+        SetWindowPos (handle, 0, 0, 0, widthCombo - 1, heightCombo - 1, uFlags);
+        SetWindowPos (hwndText, 0, 0, 0, width, height, uFlags);
+        SetWindowPos (handle, 0, 0, 0, widthCombo, heightCombo, uFlags);
+        OS.InvalidateRect (handle, null, true);
+    }
+    if (hwndList !is 0) {
+        int bits1 = OS.GetWindowLong (hwndList, OS.GWL_EXSTYLE);
+        if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+            bits1 |= OS.WS_EX_LAYOUTRTL;
+        } else {
+            bits1 &= ~OS.WS_EX_LAYOUTRTL;
+        }
+        OS.SetWindowLong (hwndList, OS.GWL_EXSTYLE, bits1);
+    }
+}
+
+void setScrollWidth () {
+    int newWidth = 0;
+    RECT rect = new RECT ();
+    int newFont, oldFont = 0;
+    int hDC = OS.GetDC (handle);
+    newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    int cp = getCodePage ();
+    int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+    for (int i=0; i<count; i++) {
+        int length = OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, i, 0);
+        if (length !is OS.CB_ERR) {
+            TCHAR buffer = new TCHAR (cp, length + 1);
+            int result = OS.SendMessage (handle, OS.CB_GETLBTEXT, i, buffer);
+            if (result !is OS.CB_ERR) {
+                OS.DrawText (hDC, buffer, -1, rect, flags);
+                newWidth = Math.max (newWidth, rect.right - rect.left);
+            }
+        }
+    }
+    if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+    OS.ReleaseDC (handle, hDC);
+    setScrollWidth (newWidth + 3);
+}
+
+void setScrollWidth (int scrollWidth) {
+    this.scrollWidth = scrollWidth;
+    if ((style & DWT.SIMPLE) !is 0) {
+        OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, scrollWidth, 0);
+        return;
+    }
+    bool scroll = false;
+    int count = OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
+    if (count > 3) {
+        int maxWidth = 0;
+        if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) {
+            RECT rect = new RECT ();
+            OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0);
+            maxWidth = (rect.right - rect.left) / 4;
+        } else {
+            int hmonitor = OS.MonitorFromWindow (handle, OS.MONITOR_DEFAULTTONEAREST);
+            MONITORINFO lpmi = new MONITORINFO ();
+            lpmi.cbSize = MONITORINFO.sizeof;
+            OS.GetMonitorInfo (hmonitor, lpmi);
+            maxWidth = (lpmi.rcWork_right - lpmi.rcWork_left) / 4;
+        }
+        scroll = scrollWidth > maxWidth;
+    }
+    if (scroll) {
+        OS.SendMessage (handle, OS.CB_SETDROPPEDWIDTH, 0, 0);
+        OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, scrollWidth, 0);
+    } else {
+        scrollWidth += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+        OS.SendMessage (handle, OS.CB_SETDROPPEDWIDTH, scrollWidth, 0);
+        OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, 0, 0);
+    }
+}
+
+void setScrollWidth (TCHAR buffer, bool grow) {
+    RECT rect = new RECT ();
+    int newFont, oldFont = 0;
+    int hDC = OS.GetDC (handle);
+    newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+    OS.DrawText (hDC, buffer, -1, rect, flags);
+    if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+    OS.ReleaseDC (handle, hDC);
+    setScrollWidth (rect.right - rect.left, grow);
+}
+
+void setScrollWidth (int newWidth, bool grow) {
+    if (grow) {
+        if (newWidth <= scrollWidth) return;
+        setScrollWidth (newWidth + 3);
+    } else {
+        if (newWidth < scrollWidth) return;
+        setScrollWidth ();
+    }
+}
+
+/**
+ * Sets the selection in the receiver's text field to the
+ * range specified by the argument whose x coordinate is the
+ * start of the selection and whose y coordinate is the end
+ * of the selection.
+ *
+ * @param selection a point representing the new selection start and end
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (Point selection) {
+    checkWidget ();
+    if (selection is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int start = selection.x, end = selection.y;
+    if (!OS.IsUnicode && OS.IsDBLocale) {
+        start = wcsToMbcsPos (start);
+        end = wcsToMbcsPos (end);
+    }
+    int bits = (start & 0xFFFF) | ((end << 16) & 0xFFFF0000);
+    OS.SendMessage (handle, OS.CB_SETEDITSEL, 0, bits);
+}
+
+/**
+ * Sets the contents of the receiver's text field to the
+ * given string.
+ * <p>
+ * Note: The text field in a <code>Combo</code> is typically
+ * only capable of displaying a single line of text. Thus,
+ * setting the text to a string containing line breaks or
+ * other special characters will probably cause it to
+ * display incorrectly.
+ * </p>
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if ((style & DWT.READ_ONLY) !is 0) {
+        int index = indexOf (string);
+        if (index !is -1) select (index);
+        return;
+    }
+    int limit = LIMIT;
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText !is 0) {
+        limit = OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0);
+    }
+    if (string.length () > limit) string = string.substring (0, limit);
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    if (OS.SetWindowText (handle, buffer)) {
+        sendEvent (DWT.Modify);
+        // widget could be disposed at this point
+    }
+}
+
+/**
+ * Sets the maximum number of characters that the receiver's
+ * text field is capable of holding to be the argument.
+ * <p>
+ * To reset this value to the default, use <code>setTextLimit(Combo.LIMIT)</code>.
+ * Specifying a limit value larger than <code>Combo.LIMIT</code> sets the
+ * receiver's limit to <code>Combo.LIMIT</code>.
+ * </p>
+ * @param limit new text limit
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #LIMIT
+ */
+public void setTextLimit (int limit) {
+    checkWidget ();
+    if (limit is 0) error (DWT.ERROR_CANNOT_BE_ZERO);
+    OS.SendMessage (handle, OS.CB_LIMITTEXT, limit, 0);
+}
+
+void setToolTipText (Shell shell, String string) {
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndText !is 0) shell.setToolTipText (hwndText, string);
+    if (hwndList !is 0) shell.setToolTipText (hwndList, string);
+    shell.setToolTipText (handle, string);
+}
+
+/**
+ * Sets the number of items that are visible in the drop
+ * down portion of the receiver's list.
+ * <p>
+ * Note: This operation is a hint and is not supported on
+ * platforms that do not have this concept.
+ * </p>
+ *
+ * @param count the new number of items to be visible
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setVisibleItemCount (int count) {
+    checkWidget ();
+    if (count < 0) return;
+    visibleCount = count;
+    if ((style & DWT.DROP_DOWN) !is 0) {
+        forceResize ();
+        RECT rect = new RECT ();
+        OS.GetWindowRect (handle, rect);
+        int flags = OS.SWP_NOMOVE | OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE;
+        setBounds (0, 0, rect.right - rect.left, rect.bottom - rect.top, flags);
+    }
+}
+
+void subclass () {
+    super.subclass ();
+    int newProc = display.windowProc;
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText !is 0) {
+        OS.SetWindowLong (hwndText, OS.GWL_WNDPROC, newProc);
+    }
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndList !is 0) {
+        OS.SetWindowLong (hwndList, OS.GWL_WNDPROC, newProc);
+    }
+}
+
+bool translateTraversal (MSG msg) {
+    /*
+    * When the combo box is dropped down, allow return
+    * to select an item in the list and escape to close
+    * the combo box.
+    */
+    switch (msg.wParam) {
+        case OS.VK_RETURN:
+        case OS.VK_ESCAPE:
+            if ((style & DWT.DROP_DOWN) !is 0) {
+                if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) !is 0) {
+                    return false;
+                }
+            }
+    }
+    return super.translateTraversal (msg);
+}
+
+bool traverseEscape () {
+    if ((style & DWT.DROP_DOWN) !is 0) {
+        if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) !is 0) {
+            OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, 0, 0);
+            return true;
+        }
+    }
+    return super.traverseEscape ();
+}
+
+bool traverseReturn () {
+    if ((style & DWT.DROP_DOWN) !is 0) {
+        if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) !is 0) {
+            OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, 0, 0);
+            return true;
+        }
+    }
+    return super.traverseReturn ();
+}
+
+void unsubclass () {
+    super.unsubclass ();
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText !is 0 && EditProc !is 0) {
+        OS.SetWindowLong (hwndText, OS.GWL_WNDPROC, EditProc);
+    }
+    int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+    if (hwndList !is 0 && ListProc !is 0) {
+        OS.SetWindowLong (hwndList, OS.GWL_WNDPROC, ListProc);
+    }
+}
+
+String verifyText (String string, int start, int end, Event keyEvent) {
+    Event event = new Event ();
+    event.text = string;
+    event.start = start;
+    event.end = end;
+    if (keyEvent !is null) {
+        event.character = keyEvent.character;
+        event.keyCode = keyEvent.keyCode;
+        event.stateMask = keyEvent.stateMask;
+    }
+    if (!OS.IsUnicode && OS.IsDBLocale) {
+        event.start = mbcsToWcsPos (start);
+        event.end = mbcsToWcsPos (end);
+    }
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in the verify
+    * event.  If this happens, answer null to cancel
+    * the operation.
+    */
+    sendEvent (DWT.Verify, event);
+    if (!event.doit || isDisposed ()) return null;
+    return event.text;
+}
+
+int wcsToMbcsPos (int wcsPos) {
+    if (wcsPos <= 0) return 0;
+    if (OS.IsUnicode) return wcsPos;
+    int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+    if (hwndText is 0) return wcsPos;
+    int mbcsSize = OS.GetWindowTextLengthA (hwndText);
+    if (mbcsSize is 0) return 0;
+    byte [] buffer = new byte [mbcsSize + 1];
+    OS.GetWindowTextA (hwndText, buffer, mbcsSize + 1);
+    int mbcsPos = 0, wcsCount = 0;
+    while (mbcsPos < mbcsSize) {
+        if (wcsPos is wcsCount) break;
+        if (OS.IsDBCSLeadByte (buffer [mbcsPos++])) mbcsPos++;
+        wcsCount++;
+    }
+    return mbcsPos;
+}
+
+int widgetExtStyle () {
+    return super.widgetExtStyle () & ~OS.WS_EX_NOINHERITLAYOUT;
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.CBS_AUTOHSCROLL | OS.CBS_NOINTEGRALHEIGHT | OS.WS_HSCROLL |OS.WS_VSCROLL;
+    if ((style & DWT.SIMPLE) !is 0) return bits | OS.CBS_SIMPLE;
+    if ((style & DWT.READ_ONLY) !is 0) return bits | OS.CBS_DROPDOWNLIST;
+    return bits | OS.CBS_DROPDOWN;
+}
+
+TCHAR windowClass () {
+    return ComboClass;
+}
+
+int windowProc () {
+    return ComboProc;
+}
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    if (hwnd !is handle) {
+        int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+        int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+        if ((hwndText !is 0 && hwnd is hwndText) || (hwndList !is 0 && hwnd is hwndList)) {
+            LRESULT result = null;
+            switch (msg) {
+                /* Keyboard messages */
+                case OS.WM_CHAR:        result = wmChar (hwnd, wParam, lParam); break;
+                case OS.WM_IME_CHAR:    result = wmIMEChar (hwnd, wParam, lParam); break;
+                case OS.WM_KEYDOWN:     result = wmKeyDown (hwnd, wParam, lParam); break;
+                case OS.WM_KEYUP:       result = wmKeyUp (hwnd, wParam, lParam); break;
+                case OS.WM_SYSCHAR:     result = wmSysChar (hwnd, wParam, lParam); break;
+                case OS.WM_SYSKEYDOWN:  result = wmSysKeyDown (hwnd, wParam, lParam); break;
+                case OS.WM_SYSKEYUP:    result = wmSysKeyUp (hwnd, wParam, lParam); break;
+
+                /* Mouse Messages */
+                case OS.WM_CAPTURECHANGED:  result = wmCaptureChanged (hwnd, wParam, lParam); break;
+                case OS.WM_LBUTTONDBLCLK:   result = wmLButtonDblClk (hwnd, wParam, lParam); break;
+                case OS.WM_LBUTTONDOWN:     result = wmLButtonDown (hwnd, wParam, lParam); break;
+                case OS.WM_LBUTTONUP:       result = wmLButtonUp (hwnd, wParam, lParam); break;
+                case OS.WM_MBUTTONDBLCLK:   result = wmMButtonDblClk (hwnd, wParam, lParam); break;
+                case OS.WM_MBUTTONDOWN:     result = wmMButtonDown (hwnd, wParam, lParam); break;
+                case OS.WM_MBUTTONUP:       result = wmMButtonUp (hwnd, wParam, lParam); break;
+                case OS.WM_MOUSEHOVER:      result = wmMouseHover (hwnd, wParam, lParam); break;
+                case OS.WM_MOUSELEAVE:      result = wmMouseLeave (hwnd, wParam, lParam); break;
+                case OS.WM_MOUSEMOVE:       result = wmMouseMove (hwnd, wParam, lParam); break;
+//              case OS.WM_MOUSEWHEEL:      result = wmMouseWheel (hwnd, wParam, lParam); break;
+                case OS.WM_RBUTTONDBLCLK:   result = wmRButtonDblClk (hwnd, wParam, lParam); break;
+                case OS.WM_RBUTTONDOWN:     result = wmRButtonDown (hwnd, wParam, lParam); break;
+                case OS.WM_RBUTTONUP:       result = wmRButtonUp (hwnd, wParam, lParam); break;
+                case OS.WM_XBUTTONDBLCLK:   result = wmXButtonDblClk (hwnd, wParam, lParam); break;
+                case OS.WM_XBUTTONDOWN:     result = wmXButtonDown (hwnd, wParam, lParam); break;
+                case OS.WM_XBUTTONUP:       result = wmXButtonUp (hwnd, wParam, lParam); break;
+
+                /* Paint messages */
+                case OS.WM_PAINT:           result = wmPaint (hwnd, wParam, lParam); break;
+
+                /* Menu messages */
+                case OS.WM_CONTEXTMENU:     result = wmContextMenu (hwnd, wParam, lParam); break;
+
+                /* Clipboard messages */
+                case OS.WM_CLEAR:
+                case OS.WM_CUT:
+                case OS.WM_PASTE:
+                case OS.WM_UNDO:
+                case OS.EM_UNDO:
+                case OS.WM_SETTEXT:
+                    if (hwnd is hwndText) {
+                        result = wmClipboard (hwnd, msg, wParam, lParam);
+                    }
+                    break;
+            }
+            if (result !is null) return result.value;
+            return callWindowProc (hwnd, msg, wParam, lParam);
+        }
+    }
+    if (msg is OS.CB_SETCURSEL) {
+        if ((style & DWT.READ_ONLY) !is 0) {
+            if (hooks (DWT.Verify) || filters (DWT.Verify)) {
+                String oldText = getText (), newText = null;
+                if (wParam is -1) {
+                    newText = "";
+                } else {
+                    if (0 <= wParam && wParam < getItemCount ()) {
+                        newText = getItem (wParam);
+                    }
+                }
+                if (newText !is null && !newText.equals (oldText)) {
+                    int length = OS.GetWindowTextLength (handle);
+                    oldText = newText;
+                    newText = verifyText (newText, 0, length, null);
+                    if (newText is null) return 0;
+                    if (!newText.equals (oldText)) {
+                        int index = indexOf (newText);
+                        if (index !is -1 && index !is wParam) {
+                            return callWindowProc (handle, OS.CB_SETCURSEL, index, lParam);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return super.windowProc (hwnd, msg, wParam, lParam);
+}
+
+LRESULT WM_CTLCOLOR (int wParam, int lParam) {
+    return wmColorChild (wParam, lParam);
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    int code = callWindowProc (handle, OS.WM_GETDLGCODE, wParam, lParam);
+    return new LRESULT (code | OS.DLGC_WANTARROWS);
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  When a combo box that is read only
+    * is disposed in CBN_KILLFOCUS, Windows segment faults.
+    * The fix is to send focus from WM_KILLFOCUS instead
+    * of CBN_KILLFOCUS.
+    *
+    * NOTE: In version 6 of COMCTL32.DLL, the bug is fixed.
+    */
+    if ((style & DWT.READ_ONLY) !is 0) {
+        return super.WM_KILLFOCUS (wParam, lParam);
+    }
+
+    /*
+    * Return NULL - Focus notification is
+    * done in WM_COMMAND by CBN_KILLFOCUS.
+    */
+    return null;
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When an editable combo box is dropped
+    * down and the text in the entry field partially matches an
+    * item in the list, Windows selects the item but doesn't send
+    * WM_COMMAND with CBN_SELCHANGE.  The fix is to detect that
+    * the selection has changed and issue the notification.
+    */
+    int oldSelection = OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
+    LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+    if ((style & DWT.READ_ONLY) is 0) {
+        int newSelection = OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
+        if (oldSelection !is newSelection) {
+            sendEvent (DWT.Modify);
+            if (isDisposed ()) return LRESULT.ZERO;
+            sendEvent (DWT.Selection);
+            if (isDisposed ()) return LRESULT.ZERO;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    /*
+    * Return NULL - Focus notification is
+    * done by WM_COMMAND with CBN_SETFOCUS.
+    */
+    return null;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  If the combo box has the CBS_SIMPLE style,
+    * the list portion of the combo box is not redrawn when the
+    * combo box is resized.  The fix is to force a redraw when
+    * the size has changed.
+    */
+    if ((style & DWT.SIMPLE) !is 0) {
+        LRESULT result = super.WM_SIZE (wParam, lParam);
+        if (OS.IsWindowVisible (handle)) {
+            if (OS.IsWinCE) {
+                int hwndText = OS.GetDlgItem (handle, CBID_EDIT);
+                if (hwndText !is 0) OS.InvalidateRect (hwndText, null, true);
+                int hwndList = OS.GetDlgItem (handle, CBID_LIST);
+                if (hwndList !is 0) OS.InvalidateRect (hwndList, null, true);
+            } else {
+                int uFlags = OS.RDW_ERASE | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+                OS.RedrawWindow (handle, null, 0, uFlags);
+            }
+        }
+        return result;
+    }
+
+    /*
+    * Feature in Windows.  When an editable drop down combo box
+    * contains text that does not correspond to an item in the
+    * list, when the widget is resized, it selects the closest
+    * match from the list.  The fix is to remember the original
+    * text and reset it after the widget is resized.
+    */
+    LRESULT result = null;
+    if ((style & DWT.READ_ONLY) !is 0) {
+        result = super.WM_SIZE (wParam, lParam);
+    } else {
+        int index = OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
+        bool redraw = false;
+        TCHAR buffer = null;
+        int [] start = null, end = null;
+        if (index is OS.CB_ERR) {
+            int length = OS.GetWindowTextLength (handle);
+            if (length !is 0) {
+                buffer = new TCHAR (getCodePage (), length + 1);
+                OS.GetWindowText (handle, buffer, length + 1);
+                start = new int [1];  end = new int [1];
+                OS.SendMessage (handle, OS.CB_GETEDITSEL, start, end);
+                redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+                if (redraw) setRedraw (false);
+            }
+        }
+        result = super.WM_SIZE (wParam, lParam);
+        /*
+        * It is possible (but unlikely), that application
+        * code could have disposed the widget in the resize
+        * event.  If this happens, end the processing of the
+        * Windows message by returning the result of the
+        * WM_SIZE message.
+        */
+        if (isDisposed ()) return result;
+        if (buffer !is null) {
+            OS.SetWindowText (handle, buffer);
+            int bits = (start [0] & 0xFFFF) | ((end [0] << 16) & 0xFFFF0000);
+            OS.SendMessage (handle, OS.CB_SETEDITSEL, 0, bits);
+            if (redraw) setRedraw (true);
+        }
+    }
+    /*
+    * Feature in Windows.  When CB_SETDROPPEDWIDTH is called with
+    * a width that is smaller than the current size of the combo
+    * box, it is ignored.  This the fix is to set the width after
+    * the combo box has been resized.
+    */
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth (scrollWidth);
+    return result;
+}
+
+LRESULT wmChar (int hwnd, int wParam, int lParam) {
+    if (ignoreCharacter) return null;
+    LRESULT result = super.wmChar (hwnd, wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  For some reason, when the
+    * widget is a single line text widget, when the
+    * user presses tab, return or escape, Windows beeps.
+    * The fix is to look for these keys and not call
+    * the window proc.
+    *
+    * NOTE: This only happens when the drop down list
+    * is not visible.
+    */
+    switch (wParam) {
+        case DWT.TAB: return LRESULT.ZERO;
+        case DWT.CR:
+            if (!ignoreDefaultSelection) postEvent (DWT.DefaultSelection);
+            ignoreDefaultSelection = false;
+            // FALL THROUGH
+        case DWT.ESC:
+            if ((style & DWT.DROP_DOWN) !is 0) {
+                if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) is 0) {
+                    return LRESULT.ZERO;
+                }
+            }
+    }
+    return result;
+}
+
+LRESULT wmClipboard (int hwndText, int msg, int wParam, int lParam) {
+    if ((style & DWT.READ_ONLY) !is 0) return null;
+    if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return null;
+    bool call = false;
+    int [] start = new int [1], end = new int [1];
+    String newText = null;
+    switch (msg) {
+        case OS.WM_CLEAR:
+        case OS.WM_CUT:
+            OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
+            if (start [0] !is end [0]) {
+                newText = "";
+                call = true;
+            }
+            break;
+        case OS.WM_PASTE:
+            OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
+            newText = getClipboardText ();
+            break;
+        case OS.EM_UNDO:
+        case OS.WM_UNDO:
+            if (OS.SendMessage (hwndText, OS.EM_CANUNDO, 0, 0) !is 0) {
+                ignoreModify = true;
+                OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
+                OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
+                int length = OS.GetWindowTextLength (hwndText);
+                int [] newStart = new int [1], newEnd = new int [1];
+                OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd);
+                if (length !is 0 && newStart [0] !is newEnd [0]) {
+                    TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+                    OS.GetWindowText (hwndText, buffer, length + 1);
+                    newText = buffer.toString (newStart [0], newEnd [0] - newStart [0]);
+                } else {
+                    newText = "";
+                }
+                OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
+                ignoreModify = false;
+            }
+            break;
+        case OS.WM_SETTEXT:
+            end [0] = OS.GetWindowTextLength (hwndText);
+            int length = OS.IsUnicode ? OS.wcslen (lParam) : OS.strlen (lParam);
+            TCHAR buffer = new TCHAR (getCodePage (), length);
+            int byteCount = buffer.length () * TCHAR.sizeof;
+            OS.MoveMemory (buffer, lParam, byteCount);
+            newText = buffer.toString (0, length);
+            break;
+    }
+    if (newText !is null) {
+        String oldText = newText;
+        newText = verifyText (newText, start [0], end [0], null);
+        if (newText is null) return LRESULT.ZERO;
+        if (!newText.equals (oldText)) {
+            if (call) {
+                OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
+            }
+            TCHAR buffer = new TCHAR (getCodePage (), newText, true);
+            if (msg is OS.WM_SETTEXT) {
+                int hHeap = OS.GetProcessHeap ();
+                int byteCount = buffer.length () * TCHAR.sizeof;
+                int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+                OS.MoveMemory (pszText, buffer, byteCount);
+                int code = OS.CallWindowProc (EditProc, hwndText, msg, wParam, pszText);
+                OS.HeapFree (hHeap, 0, pszText);
+                return new LRESULT (code);
+            } else {
+                OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer);
+                return LRESULT.ZERO;
+            }
+        }
+    }
+    return null;
+}
+
+LRESULT wmCommandChild (int wParam, int lParam) {
+    int code = wParam >> 16;
+    switch (code) {
+        case OS.CBN_EDITCHANGE:
+            if (ignoreModify) break;
+            /*
+            * Feature in Windows.  If the combo box list selection is
+            * queried using CB_GETCURSEL before the WM_COMMAND (with
+            * CBN_EDITCHANGE) returns, CB_GETCURSEL returns the previous
+            * selection in the list.  It seems that the combo box sends
+            * the WM_COMMAND before it makes the selection in the list box
+            * match the entry field.  The fix is remember that no selection
+            * in the list should exist in this case.
+            */
+            noSelection = true;
+            sendEvent (DWT.Modify);
+            if (isDisposed ()) return LRESULT.ZERO;
+            noSelection = false;
+            break;
+        case OS.CBN_SELCHANGE:
+            /*
+            * Feature in Windows.  If the text in an editable combo box
+            * is queried using GetWindowText () before the WM_COMMAND
+            * (with CBN_SELCHANGE) returns, GetWindowText () returns is
+            * the previous text in the combo box.  It seems that the combo
+            * box sends the WM_COMMAND before it updates the text field to
+            * match the list selection.  The fix is to force the text field
+            * to match the list selection by re-selecting the list item.
+            */
+            int index = OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
+            if (index !is OS.CB_ERR) {
+                OS.SendMessage (handle, OS.CB_SETCURSEL, index, 0);
+            }
+            /*
+            * It is possible (but unlikely), that application
+            * code could have disposed the widget in the modify
+            * event.  If this happens, end the processing of the
+            * Windows message by returning zero as the result of
+            * the window proc.
+            */
+            sendEvent (DWT.Modify);
+            if (isDisposed ()) return LRESULT.ZERO;
+            postEvent (DWT.Selection);
+            break;
+        case OS.CBN_SETFOCUS:
+            sendFocusEvent (DWT.FocusIn);
+            if (isDisposed ()) return LRESULT.ZERO;
+            break;
+        case OS.CBN_KILLFOCUS:
+            /*
+            * Bug in Windows.  When a combo box that is read only
+            * is disposed in CBN_KILLFOCUS, Windows segment faults.
+            * The fix is to send focus from WM_KILLFOCUS instead
+            * of CBN_KILLFOCUS.
+            *
+            * NOTE: In version 6 of COMCTL32.DLL, the bug is fixed.
+            */
+            if ((style & DWT.READ_ONLY) !is 0) break;
+            sendFocusEvent (DWT.FocusOut);
+            if (isDisposed ()) return LRESULT.ZERO;
+            break;
+    }
+    return super.wmCommandChild (wParam, lParam);
+}
+
+LRESULT wmIMEChar (int hwnd, int wParam, int lParam) {
+
+    /* Process a DBCS character */
+    Display display = this.display;
+    display.lastKey = 0;
+    display.lastAscii = wParam;
+    display.lastVirtual = display.lastNull = display.lastDead = false;
+    if (!sendKeyEvent (DWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) {
+        return LRESULT.ZERO;
+    }
+
+    /*
+    * Feature in Windows.  The Windows text widget uses
+    * two 2 WM_CHAR's to process a DBCS key instead of
+    * using WM_IME_CHAR.  The fix is to allow the text
+    * widget to get the WM_CHAR's but ignore sending
+    * them to the application.
+    */
+    ignoreCharacter = true;
+    int result = callWindowProc (hwnd, OS.WM_IME_CHAR, wParam, lParam);
+    MSG msg = new MSG ();
+    int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE;
+    while (OS.PeekMessage (msg, hwnd, OS.WM_CHAR, OS.WM_CHAR, flags)) {
+        OS.TranslateMessage (msg);
+        OS.DispatchMessage (msg);
+    }
+    ignoreCharacter = false;
+
+    sendKeyEvent (DWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam);
+    // widget could be disposed at this point
+    display.lastKey = display.lastAscii = 0;
+    return new LRESULT (result);
+}
+
+LRESULT wmKeyDown (int hwnd, int wParam, int lParam) {
+    if (ignoreCharacter) return null;
+    LRESULT result = super.wmKeyDown (hwnd, wParam, lParam);
+    if (result !is null) return result;
+    ignoreDefaultSelection = false;
+    if (wParam is OS.VK_RETURN) {
+        if ((style & DWT.DROP_DOWN) !is 0) {
+            if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) !is 0) {
+                ignoreDefaultSelection = true;
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT wmSysKeyDown (int hwnd, int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When an editable combo box is dropped
+    * down using Alt+Down and the text in the entry field partially
+    * matches an item in the list, Windows selects the item but doesn't
+    * send WM_COMMAND with CBN_SELCHANGE.  The fix is to detect that
+    * the selection has changed and issue the notification.
+    */
+    int oldSelection = OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
+    LRESULT result = super.wmSysKeyDown (hwnd, wParam, lParam);
+    if (result !is null) return result;
+    if ((style & DWT.READ_ONLY) is 0) {
+        if (wParam is OS.VK_DOWN) {
+            int code = callWindowProc (hwnd, OS.WM_SYSKEYDOWN, wParam, lParam);
+            int newSelection = OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
+            if (oldSelection !is newSelection) {
+                sendEvent (DWT.Modify);
+                if (isDisposed ()) return LRESULT.ZERO;
+                sendEvent (DWT.Selection);
+                if (isDisposed ()) return LRESULT.ZERO;
+            }
+            return new LRESULT (code);
+        }
+    }
+    return result;
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Composite.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1609 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Composite;
+
+import dwt.widgets.Scrollable;
+import dwt.widgets.Control;
+
+class Composite : Scrollable {
+public Control [] getChildren () ;
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.Callback;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MSG;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.NMTTDISPINFO;
+import dwt.internal.win32.NMTTDISPINFOA;
+import dwt.internal.win32.NMTTDISPINFOW;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.PAINTSTRUCT;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.WINDOWPOS;
+
+/**
+ * Instances of this class are controls which are capable
+ * of containing other controls.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>NO_BACKGROUND, NO_FOCUS, NO_MERGE_PAINTS, NO_REDRAW_RESIZE, NO_RADIO_GROUP, EMBEDDED, DOUBLE_BUFFERED</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: The <code>NO_BACKGROUND</code>, <code>NO_FOCUS</code>, <code>NO_MERGE_PAINTS</code>,
+ * and <code>NO_REDRAW_RESIZE</code> styles are intended for use with <code>Canvas</code>.
+ * They can be used with <code>Composite</code> if you are drawing your own, but their
+ * behavior is undefined if they are used with subclasses of <code>Composite</code> other
+ * than <code>Canvas</code>.
+ * </p><p>
+ * Note: The <code>CENTER</code> style, although undefined for composites, has the
+ * same value as <code>EMBEDDED</code> (which is used to embed widgets from other
+ * widget toolkits into DWT).  On some operating systems (GTK, Motif), this may cause
+ * the children of this composite to be obscured.  The <code>EMBEDDED</code> style
+ * is for use by other widget toolkits and should normally never be used.
+ * </p><p>
+ * This class may be subclassed by custom control implementors
+ * who are building controls that are constructed from aggregates
+ * of other controls.
+ * </p>
+ *
+ * @see Canvas
+ */
+
+public class Composite extends Scrollable {
+    Layout layout;
+    int font;
+    WINDOWPOS [] lpwp;
+    Control [] tabList;
+    int layoutCount, backgroundMode;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+Composite () {
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see DWT#NO_BACKGROUND
+ * @see DWT#NO_FOCUS
+ * @see DWT#NO_MERGE_PAINTS
+ * @see DWT#NO_REDRAW_RESIZE
+ * @see DWT#NO_RADIO_GROUP
+ * @see Widget#getStyle
+ */
+public Composite (Composite parent, int style) {
+    super (parent, style);
+}
+
+Control [] _getChildren () {
+    int count = 0;
+    int hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+    if (hwndChild is 0) return new Control [0];
+    while (hwndChild !is 0) {
+        count++;
+        hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+    }
+    Control [] children = new Control [count];
+    int index = 0;
+    hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+    while (hwndChild !is 0) {
+        Control control = display.getControl (hwndChild);
+        if (control !is null && control !is this) {
+            children [index++] = control;
+        }
+        hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+    }
+    if (count is index) return children;
+    Control [] newChildren = new Control [index];
+    System.arraycopy (children, 0, newChildren, 0, index);
+    return newChildren;
+}
+
+Control [] _getTabList () {
+    if (tabList is null) return tabList;
+    int count = 0;
+    for (int i=0; i<tabList.length; i++) {
+        if (!tabList [i].isDisposed ()) count++;
+    }
+    if (count is tabList.length) return tabList;
+    Control [] newList = new Control [count];
+    int index = 0;
+    for (int i=0; i<tabList.length; i++) {
+        if (!tabList [i].isDisposed ()) {
+            newList [index++] = tabList [i];
+        }
+    }
+    tabList = newList;
+    return tabList;
+}
+
+/**
+ * Clears any data that has been cached by a Layout for all widgets that
+ * are in the parent hierarchy of the changed control up to and including the
+ * receiver.  If an ancestor does not have a layout, it is skipped.
+ *
+ * @param changed an array of controls that changed state and require a recalculation of size
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the changed array is null any of its controls are null or have been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void changed (Control[] changed) {
+    checkWidget ();
+    if (changed is null) error (DWT.ERROR_INVALID_ARGUMENT);
+    for (int i=0; i<changed.length; i++) {
+        Control control = changed [i];
+        if (control is null) error (DWT.ERROR_INVALID_ARGUMENT);
+        if (control.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+        bool ancestor = false;
+        Composite composite = control.parent;
+        while (composite !is null) {
+            ancestor = composite is this;
+            if (ancestor) break;
+            composite = composite.parent;
+        }
+        if (!ancestor) error (DWT.ERROR_INVALID_PARENT);
+    }
+    for (int i=0; i<changed.length; i++) {
+        Control child = changed [i];
+        Composite composite = child.parent;
+        while (child !is this) {
+            if (composite.layout is null || !composite.layout.flushCache (child)) {
+                composite.state |= LAYOUT_CHANGED;
+            }
+            child = composite;
+            composite = child.parent;
+        }
+    }
+}
+
+void checkBuffered () {
+    if (OS.IsWinCE || (state & CANVAS) is 0) {
+        super.checkBuffered ();
+    }
+}
+
+protected void checkSubclass () {
+    /* Do nothing - Subclassing is allowed */
+}
+
+Control [] computeTabList () {
+    Control result [] = super.computeTabList ();
+    if (result.length is 0) return result;
+    Control [] list = tabList !is null ? _getTabList () : _getChildren ();
+    for (int i=0; i<list.length; i++) {
+        Control child = list [i];
+        Control [] childList = child.computeTabList ();
+        if (childList.length !is 0) {
+            Control [] newResult = new Control [result.length + childList.length];
+            System.arraycopy (result, 0, newResult, 0, result.length);
+            System.arraycopy (childList, 0, newResult, result.length, childList.length);
+            result = newResult;
+        }
+    }
+    return result;
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    Point size;
+    if (layout !is null) {
+        if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) {
+            changed |= (state & LAYOUT_CHANGED) !is 0;
+            state &= ~LAYOUT_CHANGED;
+            size = layout.computeSize (this, wHint, hHint, changed);
+        } else {
+            size = new Point (wHint, hHint);
+        }
+    } else {
+        size = minimumSize (wHint, hHint, changed);
+    }
+    if (size.x is 0) size.x = DEFAULT_WIDTH;
+    if (size.y is 0) size.y = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) size.x = wHint;
+    if (hHint !is DWT.DEFAULT) size.y = hHint;
+    Rectangle trim = computeTrim (0, 0, size.x, size.y);
+    return new Point (trim.width, trim.height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state |= CANVAS;
+    if ((style & (DWT.H_SCROLL | DWT.V_SCROLL)) is 0) {
+        state |= THEME_BACKGROUND;
+    }
+}
+
+Composite findDeferredControl () {
+    return layoutCount > 0 ? this : parent.findDeferredControl ();
+}
+
+Menu [] findMenus (Control control) {
+    if (control is this) return new Menu [0];
+    Menu result [] = super.findMenus (control);
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        Menu [] menuList = child.findMenus (control);
+        if (menuList.length !is 0) {
+            Menu [] newResult = new Menu [result.length + menuList.length];
+            System.arraycopy (result, 0, newResult, 0, result.length);
+            System.arraycopy (menuList, 0, newResult, result.length, menuList.length);
+            result = newResult;
+        }
+    }
+    return result;
+}
+
+void fixChildren (Shell newShell, Shell oldShell, Decorations newDecorations, Decorations oldDecorations, Menu [] menus) {
+    super.fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus);
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        children [i].fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus);
+    }
+}
+
+void fixTabList (Control control) {
+    if (tabList is null) return;
+    int count = 0;
+    for (int i=0; i<tabList.length; i++) {
+        if (tabList [i] is control) count++;
+    }
+    if (count is 0) return;
+    Control [] newList = null;
+    int length = tabList.length - count;
+    if (length !is 0) {
+        newList = new Control [length];
+        int index = 0;
+        for (int i=0; i<tabList.length; i++) {
+            if (tabList [i] !is control) {
+                newList [index++] = tabList [i];
+            }
+        }
+    }
+    tabList = newList;
+}
+
+/**
+ * Returns the receiver's background drawing mode. This
+ * will be one of the following constants defined in class
+ * <code>DWT</code>:
+ * <code>INHERIT_NONE</code>, <code>INHERIT_DEFAULT</code>,
+ * <code>INHERTIT_FORCE</code>.
+ *
+ * @return the background mode
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT
+ *
+ * @since 3.2
+ */
+public int getBackgroundMode () {
+    checkWidget ();
+    return backgroundMode;
+}
+
+/**
+ * Returns a (possibly empty) array containing the receiver's children.
+ * Children are returned in the order that they are drawn.  The topmost
+ * control appears at the beginning of the array.  Subsequent controls
+ * draw beneath this control and appear later in the array.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of children, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return an array of children
+ *
+ * @see Control#moveAbove
+ * @see Control#moveBelow
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Control [] getChildren () {
+    checkWidget ();
+    return _getChildren ();
+}
+
+int getChildrenCount () {
+    /*
+    * NOTE: The current implementation will count
+    * non-registered children.
+    */
+    int count = 0;
+    int hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+    while (hwndChild !is 0) {
+        count++;
+        hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+    }
+    return count;
+}
+
+/**
+ * Returns layout which is associated with the receiver, or
+ * null if one has not been set.
+ *
+ * @return the receiver's layout or null
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Layout getLayout () {
+    checkWidget ();
+    return layout;
+}
+
+/**
+ * Gets the (possibly empty) tabbing order for the control.
+ *
+ * @return tabList the ordered list of controls representing the tab order
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setTabList
+ */
+public Control [] getTabList () {
+    checkWidget ();
+    Control [] tabList = _getTabList ();
+    if (tabList is null) {
+        int count = 0;
+        Control [] list =_getChildren ();
+        for (int i=0; i<list.length; i++) {
+            if (list [i].isTabGroup ()) count++;
+        }
+        tabList = new Control [count];
+        int index = 0;
+        for (int i=0; i<list.length; i++) {
+            if (list [i].isTabGroup ()) {
+                tabList [index++] = list [i];
+            }
+        }
+    }
+    return tabList;
+}
+
+bool hooksKeys () {
+    return hooks (DWT.KeyDown) || hooks (DWT.KeyUp);
+}
+
+/**
+ * Returns <code>true</code> if the receiver has deferred
+ * the performing of layout, and <code>false</code> otherwise.
+ *
+ * @return the receiver's deferred layout state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setLayoutDeferred(bool)
+ * @see #isLayoutDeferred()
+ *
+ * @since 3.1
+ */
+public bool getLayoutDeferred () {
+    checkWidget ();
+    return layoutCount > 0 ;
+}
+
+/**
+ * Returns <code>true</code> if the receiver or any ancestor
+ * up to and including the receiver's nearest ancestor shell
+ * has deferred the performing of layouts.  Otherwise, <code>false</code>
+ * is returned.
+ *
+ * @return the receiver's deferred layout state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setLayoutDeferred(bool)
+ * @see #getLayoutDeferred()
+ *
+ * @since 3.1
+ */
+public bool isLayoutDeferred () {
+    checkWidget ();
+    return findDeferredControl () !is null;
+}
+
+/**
+ * If the receiver has a layout, asks the layout to <em>lay out</em>
+ * (that is, set the size and location of) the receiver's children.
+ * If the receiver does not have a layout, do nothing.
+ * <p>
+ * This is equivalent to calling <code>layout(true)</code>.
+ * </p>
+ * <p>
+ * Note: Layout is different from painting. If a child is
+ * moved or resized such that an area in the parent is
+ * exposed, then the parent will paint. If no child is
+ * affected, the parent will not paint.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void layout () {
+    checkWidget ();
+    layout (true);
+}
+
+/**
+ * If the receiver has a layout, asks the layout to <em>lay out</em>
+ * (that is, set the size and location of) the receiver's children.
+ * If the argument is <code>true</code> the layout must not rely
+ * on any information it has cached about the immediate children. If it
+ * is <code>false</code> the layout may (potentially) optimize the
+ * work it is doing by assuming that none of the receiver's
+ * children has changed state since the last layout.
+ * If the receiver does not have a layout, do nothing.
+ * <p>
+ * If a child is resized as a result of a call to layout, the
+ * resize event will invoke the layout of the child.  The layout
+ * will cascade down through all child widgets in the receiver's widget
+ * tree until a child is encountered that does not resize.  Note that
+ * a layout due to a resize will not flush any cached information
+ * (same as <code>layout(false)</code>).
+ * </p>
+ * <p>
+ * Note: Layout is different from painting. If a child is
+ * moved or resized such that an area in the parent is
+ * exposed, then the parent will paint. If no child is
+ * affected, the parent will not paint.
+ * </p>
+ *
+ * @param changed <code>true</code> if the layout must flush its caches, and <code>false</code> otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void layout (bool changed) {
+    checkWidget ();
+    if (layout is null) return;
+    layout (changed, false);
+}
+
+/**
+ * If the receiver has a layout, asks the layout to <em>lay out</em>
+ * (that is, set the size and location of) the receiver's children.
+ * If the changed argument is <code>true</code> the layout must not rely
+ * on any information it has cached about its children. If it
+ * is <code>false</code> the layout may (potentially) optimize the
+ * work it is doing by assuming that none of the receiver's
+ * children has changed state since the last layout.
+ * If the all argument is <code>true</code> the layout will cascade down
+ * through all child widgets in the receiver's widget tree, regardless of
+ * whether the child has changed size.  The changed argument is applied to
+ * all layouts.  If the all argument is <code>false</code>, the layout will
+ * <em>not</em> cascade down through all child widgets in the receiver's widget
+ * tree.  However, if a child is resized as a result of a call to layout, the
+ * resize event will invoke the layout of the child.  Note that
+ * a layout due to a resize will not flush any cached information
+ * (same as <code>layout(false)</code>).
+ * </p>
+ * <p>
+ * Note: Layout is different from painting. If a child is
+ * moved or resized such that an area in the parent is
+ * exposed, then the parent will paint. If no child is
+ * affected, the parent will not paint.
+ * </p>
+ *
+ * @param changed <code>true</code> if the layout must flush its caches, and <code>false</code> otherwise
+ * @param all <code>true</code> if all children in the receiver's widget tree should be laid out, and <code>false</code> otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void layout (bool changed, bool all) {
+    checkWidget ();
+    if (layout is null && !all) return;
+    markLayout (changed, all);
+    updateLayout (true, all);
+}
+
+/**
+ * Forces a lay out (that is, sets the size and location) of all widgets that
+ * are in the parent hierarchy of the changed control up to and including the
+ * receiver.  The layouts in the hierarchy must not rely on any information
+ * cached about the changed control or any of its ancestors.  The layout may
+ * (potentially) optimize the work it is doing by assuming that none of the
+ * peers of the changed control have changed state since the last layout.
+ * If an ancestor does not have a layout, skip it.
+ * <p>
+ * Note: Layout is different from painting. If a child is
+ * moved or resized such that an area in the parent is
+ * exposed, then the parent will paint. If no child is
+ * affected, the parent will not paint.
+ * </p>
+ *
+ * @param changed a control that has had a state change which requires a recalculation of its size
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the changed array is null any of its controls are null or have been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void layout (Control [] changed) {
+    checkWidget ();
+    if (changed is null) error (DWT.ERROR_INVALID_ARGUMENT);
+    for (int i=0; i<changed.length; i++) {
+        Control control = changed [i];
+        if (control is null) error (DWT.ERROR_INVALID_ARGUMENT);
+        if (control.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+        bool ancestor = false;
+        Composite composite = control.parent;
+        while (composite !is null) {
+            ancestor = composite is this;
+            if (ancestor) break;
+            composite = composite.parent;
+        }
+        if (!ancestor) error (DWT.ERROR_INVALID_PARENT);
+    }
+    int updateCount = 0;
+    Composite [] update = new Composite [16];
+    for (int i=0; i<changed.length; i++) {
+        Control child = changed [i];
+        Composite composite = child.parent;
+        while (child !is this) {
+            if (composite.layout !is null) {
+                composite.state |= LAYOUT_NEEDED;
+                if (!composite.layout.flushCache (child)) {
+                    composite.state |= LAYOUT_CHANGED;
+                }
+            }
+            if (updateCount is update.length) {
+                Composite [] newUpdate = new Composite [update.length + 16];
+                System.arraycopy (update, 0, newUpdate, 0, update.length);
+                update = newUpdate;
+            }
+            child = update [updateCount++] = composite;
+            composite = child.parent;
+        }
+    }
+    for (int i=updateCount-1; i>=0; i--) {
+        update [i].updateLayout (true, false);
+    }
+}
+
+void markLayout (bool changed, bool all) {
+    if (layout !is null) {
+        state |= LAYOUT_NEEDED;
+        if (changed) state |= LAYOUT_CHANGED;
+    }
+    if (all) {
+        Control [] children = _getChildren ();
+        for (int i=0; i<children.length; i++) {
+            children [i].markLayout (changed, all);
+        }
+    }
+}
+
+Point minimumSize (int wHint, int hHint, bool changed) {
+    Control [] children = _getChildren ();
+    int width = 0, height = 0;
+    for (int i=0; i<children.length; i++) {
+        Rectangle rect = children [i].getBounds ();
+        width = Math.max (width, rect.x + rect.width);
+        height = Math.max (height, rect.y + rect.height);
+    }
+    return new Point (width, height);
+}
+
+bool redrawChildren () {
+    if (!super.redrawChildren ()) return false;
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        children [i].redrawChildren ();
+    }
+    return true;
+}
+
+void releaseChildren (bool destroy) {
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        if (child !is null && !child.isDisposed ()) {
+            child.release (false);
+        }
+    }
+    super.releaseChildren (destroy);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if ((state & CANVAS) !is 0 && (style & DWT.EMBEDDED) !is 0) {
+        int hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+        if (hwndChild !is 0) {
+            int threadId = OS.GetWindowThreadProcessId (hwndChild, null);
+            if (threadId !is OS.GetCurrentThreadId ()) {
+                OS.ShowWindow (hwndChild, OS.SW_HIDE);
+                OS.SetParent (hwndChild, 0);
+            }
+        }
+    }
+    layout = null;
+    tabList = null;
+    lpwp = null;
+}
+
+void removeControl (Control control) {
+    fixTabList (control);
+    resizeChildren ();
+}
+
+void resizeChildren () {
+    if (lpwp is null) return;
+    do {
+        WINDOWPOS [] currentLpwp = lpwp;
+        lpwp = null;
+        if (!resizeChildren (true, currentLpwp)) {
+            resizeChildren (false, currentLpwp);
+        }
+    } while (lpwp !is null);
+}
+
+bool resizeChildren (bool defer, WINDOWPOS [] pwp) {
+    if (pwp is null) return true;
+    int hdwp = 0;
+    if (defer) {
+        hdwp = OS.BeginDeferWindowPos (pwp.length);
+        if (hdwp is 0) return false;
+    }
+    for (int i=0; i<pwp.length; i++) {
+        WINDOWPOS wp = pwp [i];
+        if (wp !is null) {
+            /*
+            * This code is intentionally commented.  All widgets that
+            * are created by DWT have WS_CLIPSIBLINGS to ensure that
+            * application code does not draw outside of the control.
+            */
+//          int count = parent.getChildrenCount ();
+//          if (count > 1) {
+//              int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+//              if ((bits & OS.WS_CLIPSIBLINGS) is 0) wp.flags |= OS.SWP_NOCOPYBITS;
+//          }
+            if (defer) {
+                hdwp = DeferWindowPos (hdwp, wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
+                if (hdwp is 0) return false;
+            } else {
+                SetWindowPos (wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
+            }
+        }
+    }
+    if (defer) return OS.EndDeferWindowPos (hdwp);
+    return true;
+}
+
+void resizeEmbeddedHandle(int embeddedHandle, int width, int height) {
+    if (embeddedHandle is 0) return;
+    int [] processID = new int [1];
+    int threadId = OS.GetWindowThreadProcessId (embeddedHandle, processID);
+    if (threadId !is OS.GetCurrentThreadId ()) {
+        if (processID [0] is OS.GetCurrentProcessId ()) {
+            if (display.msgHook is 0) {
+                if (!OS.IsWinCE) {
+                    display.getMsgCallback = new Callback (display, "getMsgProc", 3);
+                    display.getMsgProc = display.getMsgCallback.getAddress ();
+                    if (display.getMsgProc is 0) error (DWT.ERROR_NO_MORE_CALLBACKS);
+                    display.msgHook = OS.SetWindowsHookEx (OS.WH_GETMESSAGE, display.getMsgProc, OS.GetLibraryHandle(), threadId);
+                    OS.PostThreadMessage (threadId, OS.WM_NULL, 0, 0);
+                }
+            }
+        }
+        int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE | OS.SWP_ASYNCWINDOWPOS;
+        OS.SetWindowPos (embeddedHandle, 0, 0, 0, width, height, flags);
+    }
+}
+
+void sendResize () {
+    setResizeChildren (false);
+    sendEvent (DWT.Resize);
+    if (isDisposed ()) return;
+    if (layout !is null) {
+        markLayout (false, false);
+        updateLayout (false, false);
+    }
+    setResizeChildren (true);
+}
+
+/**
+ * Sets the background drawing mode to the argument which should
+ * be one of the following constants defined in class <code>DWT</code>:
+ * <code>INHERIT_NONE</code>, <code>INHERIT_DEFAULT</code>,
+ * <code>INHERIT_FORCE</code>.
+ *
+ * @param mode the new background mode
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT
+ *
+ * @since 3.2
+ */
+public void setBackgroundMode (int mode) {
+    checkWidget ();
+    backgroundMode = mode;
+    Control [] children = _getChildren ();
+    for (int i = 0; i < children.length; i++) {
+        children [i].updateBackgroundMode ();
+    }
+}
+
+bool setFixedFocus () {
+    checkWidget ();
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        if (child.setRadioFocus ()) return true;
+    }
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        if (child.setFixedFocus ()) return true;
+    }
+    return super.setFixedFocus ();
+}
+
+public bool setFocus () {
+    checkWidget ();
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        if (child.setRadioFocus ()) return true;
+    }
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        if (child.setFocus ()) return true;
+    }
+    return super.setFocus ();
+}
+
+/**
+ * Sets the layout which is associated with the receiver to be
+ * the argument which may be null.
+ *
+ * @param layout the receiver's new layout or null
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLayout (Layout layout) {
+    checkWidget ();
+    this.layout = layout;
+}
+
+/**
+ * If the argument is <code>true</code>, causes subsequent layout
+ * operations in the receiver or any of its children to be ignored.
+ * No layout of any kind can occur in the receiver or any of its
+ * children until the flag is set to false.
+ * Layout operations that occurred while the flag was
+ * <code>true</code> are remembered and when the flag is set to
+ * <code>false</code>, the layout operations are performed in an
+ * optimized manner.  Nested calls to this method are stacked.
+ *
+ * @param defer the new defer state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #layout(bool)
+ * @see #layout(Control[])
+ *
+ * @since 3.1
+ */
+public void setLayoutDeferred (bool defer) {
+    if (!defer) {
+        if (--layoutCount is 0) {
+            if ((state & LAYOUT_CHILD) !is 0 || (state & LAYOUT_NEEDED) !is 0) {
+                updateLayout (true, true);
+            }
+        }
+    } else {
+        layoutCount++;
+    }
+}
+/**
+ * Sets the tabbing order for the specified controls to
+ * match the order that they occur in the argument list.
+ *
+ * @param tabList the ordered list of controls representing the tab order or null
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if a widget in the tabList is null or has been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if widget in the tabList is not in the same widget tree</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setTabList (Control [] tabList) {
+    checkWidget ();
+    if (tabList !is null) {
+        for (int i=0; i<tabList.length; i++) {
+            Control control = tabList [i];
+            if (control is null) error (DWT.ERROR_INVALID_ARGUMENT);
+            if (control.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+            if (control.parent !is this) error (DWT.ERROR_INVALID_PARENT);
+        }
+        Control [] newList = new Control [tabList.length];
+        System.arraycopy (tabList, 0, newList, 0, tabList.length);
+        tabList = newList;
+    }
+    this.tabList = tabList;
+}
+
+void setResizeChildren (bool resize) {
+    if (resize) {
+        resizeChildren ();
+    } else {
+        int count = getChildrenCount ();
+        if (count > 1 && lpwp is null) {
+            lpwp = new WINDOWPOS [count];
+        }
+    }
+}
+
+bool setTabGroupFocus () {
+    if (isTabItem ()) return setTabItemFocus ();
+    bool takeFocus = (style & DWT.NO_FOCUS) is 0;
+    if ((state & CANVAS) !is 0) {
+        takeFocus = hooksKeys ();
+        if ((style & DWT.EMBEDDED) !is 0) takeFocus = true;
+    }
+    if (takeFocus && setTabItemFocus ()) return true;
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        if (child.isTabItem () && child.setRadioFocus ()) return true;
+    }
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        if (child.isTabItem () && child.setTabItemFocus ()) return true;
+    }
+    return false;
+}
+
+String toolTipText (NMTTDISPINFO hdr) {
+    Shell shell = getShell ();
+    if ((hdr.uFlags & OS.TTF_IDISHWND) is 0) {
+        String string = null;
+        ToolTip toolTip = shell.findToolTip (hdr.idFrom);
+        if (toolTip !is null) {
+            string = toolTip.message;
+            if (string is null || string.length () is 0) string = " ";
+        }
+        return string;
+    }
+    shell.setToolTipTitle (hdr.hwndFrom, null, 0);
+    OS.SendMessage (hdr.hwndFrom, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
+    Control control = display.getControl (hdr.idFrom);
+    return control !is null ? control.toolTipText : null;
+}
+
+bool translateMnemonic (Event event, Control control) {
+    if (super.translateMnemonic (event, control)) return true;
+    if (control !is null) {
+        Control [] children = _getChildren ();
+        for (int i=0; i<children.length; i++) {
+            Control child = children [i];
+            if (child.translateMnemonic (event, control)) return true;
+        }
+    }
+    return false;
+}
+
+bool translateTraversal (MSG msg) {
+    if ((state & CANVAS) !is 0 ) {
+        if ((style & DWT.EMBEDDED) !is 0) return false;
+        switch (msg.wParam) {
+            case OS.VK_UP:
+            case OS.VK_LEFT:
+            case OS.VK_DOWN:
+            case OS.VK_RIGHT:
+            case OS.VK_PRIOR:
+            case OS.VK_NEXT:
+                OS.SendMessage (msg.hwnd, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
+                break;
+        }
+    }
+    return super.translateTraversal (msg);
+}
+
+void updateBackgroundColor () {
+    super.updateBackgroundColor ();
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        if ((children [i].state & PARENT_BACKGROUND) !is 0) {
+            children [i].updateBackgroundColor ();
+        }
+    }
+}
+
+void updateBackgroundImage () {
+    super.updateBackgroundImage ();
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        if ((children [i].state & PARENT_BACKGROUND) !is 0) {
+            children [i].updateBackgroundImage ();
+        }
+    }
+}
+
+void updateBackgroundMode () {
+    super.updateBackgroundMode ();
+    Control [] children = _getChildren ();
+    for (int i = 0; i < children.length; i++) {
+        children [i].updateBackgroundMode ();
+    }
+}
+
+void updateFont (Font oldFont, Font newFont) {
+    super.updateFont (oldFont, newFont);
+    Control [] children = _getChildren ();
+    for (int i=0; i<children.length; i++) {
+        Control control = children [i];
+        if (!control.isDisposed ()) {
+            control.updateFont (oldFont, newFont);
+        }
+    }
+}
+
+void updateLayout (bool resize, bool all) {
+    Composite parent = findDeferredControl ();
+    if (parent !is null) {
+        parent.state |= LAYOUT_CHILD;
+        return;
+    }
+    if ((state & LAYOUT_NEEDED) !is 0) {
+        bool changed = (state & LAYOUT_CHANGED) !is 0;
+        state &= ~(LAYOUT_NEEDED | LAYOUT_CHANGED);
+        if (resize) setResizeChildren (false);
+        layout.layout (this, changed);
+        if (resize) setResizeChildren (true);
+    }
+    if (all) {
+        state &= ~LAYOUT_CHILD;
+        Control [] children = _getChildren ();
+        for (int i=0; i<children.length; i++) {
+            children [i].updateLayout (resize, all);
+        }
+    }
+}
+
+int widgetStyle () {
+    /* Force clipping of children by setting WS_CLIPCHILDREN */
+    return super.widgetStyle () | OS.WS_CLIPCHILDREN;
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    if (result !is null) return result;
+    if ((state & CANVAS) !is 0) {
+        /* Return zero to indicate that the background was not erased */
+        if ((style & DWT.NO_BACKGROUND) !is 0) return LRESULT.ZERO;
+    }
+    return result;
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
+    if (result !is null) return result;
+    if ((state & CANVAS) !is 0) {
+        int flags = 0;
+        if (hooksKeys ()) {
+            flags |= OS.DLGC_WANTALLKEYS | OS.DLGC_WANTARROWS | OS.DLGC_WANTTAB;
+        }
+        if ((style & DWT.NO_FOCUS) !is 0) flags |= OS.DLGC_STATIC;
+        if (OS.GetWindow (handle, OS.GW_CHILD) !is 0) flags |= OS.DLGC_STATIC;
+        if (flags !is 0) return new LRESULT (flags);
+    }
+    return result;
+}
+
+LRESULT WM_GETFONT (int wParam, int lParam) {
+    LRESULT result = super.WM_GETFONT (wParam, lParam);
+    if (result !is null) return result;
+    int code = callWindowProc (handle, OS.WM_GETFONT, wParam, lParam);
+    if (code !is 0) return new LRESULT (code);
+    if (font is 0) font = defaultFont ();
+    return new LRESULT (font);
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+
+    /* Set focus for a canvas with no children */
+    if ((state & CANVAS) !is 0) {
+        if ((style & DWT.NO_FOCUS) is 0 && hooksKeys ()) {
+            if (OS.GetWindow (handle, OS.GW_CHILD) is 0) setFocus ();
+        }
+    }
+    return result;
+}
+
+LRESULT WM_NCPAINT (int wParam, int lParam) {
+    LRESULT result = super.WM_NCPAINT (wParam, lParam);
+    if (result !is null) return result;
+    if ((state & CANVAS) !is 0) {
+        result = wmNCPaint (handle, wParam, lParam);
+    }
+    return result;
+}
+
+LRESULT WM_PARENTNOTIFY (int wParam, int lParam) {
+    if ((state & CANVAS) !is 0 && (style & DWT.EMBEDDED) !is 0) {
+        if ((wParam & 0xFFFF) is OS.WM_CREATE) {
+            RECT rect = new RECT ();
+            OS.GetClientRect (handle, rect);
+            resizeEmbeddedHandle (lParam, rect.right - rect.left, rect.bottom - rect.top);
+        }
+    }
+    return super.WM_PARENTNOTIFY (wParam, lParam);
+}
+
+LRESULT WM_PAINT (int wParam, int lParam) {
+    if ((state & CANVAS) is 0 || (state & FOREIGN_HANDLE) !is 0) {
+        return super.WM_PAINT (wParam, lParam);
+    }
+
+    /* Set the clipping bits */
+    int oldBits = 0, newBits = 0;
+    if (!OS.IsWinCE) {
+        oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        newBits = oldBits | OS.WS_CLIPSIBLINGS | OS.WS_CLIPCHILDREN;
+        if (newBits !is oldBits) OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+    }
+
+    /* Paint the control and the background */
+    PAINTSTRUCT ps = new PAINTSTRUCT ();
+    if (hooks (DWT.Paint)) {
+
+        /* Use the buffered paint when possible */
+        bool bufferedPaint = false;
+        if ((style & DWT.DOUBLE_BUFFERED) !is 0) {
+            if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                if ((style & (DWT.NO_MERGE_PAINTS | DWT.RIGHT_TO_LEFT)) is 0) {
+                    bufferedPaint = true;
+                }
+            }
+        }
+        if (bufferedPaint) {
+            int hDC = OS.BeginPaint (handle, ps);
+            int width = ps.right - ps.left;
+            int height = ps.bottom - ps.top;
+            if (width !is 0 && height !is 0) {
+                int [] phdc = new int [1];
+                int flags = OS.BPBF_COMPATIBLEBITMAP;
+                RECT prcTarget = new RECT ();
+                OS.SetRect (prcTarget, ps.left, ps.top, ps.right, ps.bottom);
+                int hBufferedPaint = OS.BeginBufferedPaint (hDC, prcTarget, flags, null, phdc);
+                GCData data = new GCData ();
+                if ((OS.GetLayout (hDC) & OS.LAYOUT_RTL) !is 0) {
+                    data.style = DWT.RIGHT_TO_LEFT | DWT.MIRRORED;
+                } else {
+                    data.style = DWT.LEFT_TO_RIGHT;
+                }
+                data.device = display;
+                data.foreground = getForegroundPixel ();
+                Control control = findBackgroundControl ();
+                if (control is null) control = this;
+                data.background = control.getBackgroundPixel ();
+                data.hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+                data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+                if ((style & DWT.NO_BACKGROUND) !is 0) {
+                    /* This code is intentionally commented because it may be slow to copy bits from the screen */
+                    //paintGC.copyArea (image, ps.left, ps.top);
+                } else {
+                    RECT rect = new RECT ();
+                    OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+                    drawBackground (phdc [0], rect);
+                }
+                GC gc = GC.win32_new (phdc [0], data);
+                Event event = new Event ();
+                event.gc = gc;
+                event.x = ps.left;
+                event.y = ps.top;
+                event.width = width;
+                event.height = height;
+                sendEvent (DWT.Paint, event);
+                gc.dispose ();
+                OS.EndBufferedPaint (hBufferedPaint, true);
+            }
+            OS.EndPaint (handle, ps);
+        } else {
+
+            /* Create the paint GC */
+            GCData data = new GCData ();
+            data.ps = ps;
+            data.hwnd = handle;
+            GC gc = GC.win32_new (this, data);
+
+            /* Get the system region for the paint HDC */
+            int sysRgn = 0;
+            if ((style & (DWT.NO_MERGE_PAINTS | DWT.DOUBLE_BUFFERED)) !is 0) {
+                sysRgn = OS.CreateRectRgn (0, 0, 0, 0);
+                if (OS.GetRandomRgn (gc.handle, sysRgn, OS.SYSRGN) is 1) {
+                    if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+                        if ((OS.GetLayout (gc.handle) & OS.LAYOUT_RTL) !is 0) {
+                            int nBytes = OS.GetRegionData (sysRgn, 0, null);
+                            int [] lpRgnData = new int [nBytes / 4];
+                            OS.GetRegionData (sysRgn, nBytes, lpRgnData);
+                            int newSysRgn = OS.ExtCreateRegion (new float [] {-1, 0, 0, 1, 0, 0}, nBytes, lpRgnData);
+                            OS.DeleteObject (sysRgn);
+                            sysRgn = newSysRgn;
+                        }
+                    }
+                    if (OS.IsWinNT) {
+                        POINT pt = new POINT();
+                        OS.MapWindowPoints (0, handle, pt, 1);
+                        OS.OffsetRgn (sysRgn, pt.x, pt.y);
+                    }
+                }
+            }
+
+            /* Send the paint event */
+            int width = ps.right - ps.left;
+            int height = ps.bottom - ps.top;
+            if (width !is 0 && height !is 0) {
+                GC paintGC = null;
+                Image image = null;
+                if ((style & DWT.DOUBLE_BUFFERED) !is 0) {
+                    image = new Image (display, width, height);
+                    paintGC = gc;
+                    gc = new GC (image, paintGC.getStyle() & DWT.RIGHT_TO_LEFT);
+                    GCData gcData = gc.getGCData ();
+                    gcData.uiState = data.uiState;
+                    gc.setForeground (getForeground ());
+                    gc.setBackground (getBackground ());
+                    gc.setFont (getFont ());
+                    OS.OffsetRgn (sysRgn, -ps.left, -ps.top);
+                    OS.SelectClipRgn (gc.handle, sysRgn);
+                    OS.OffsetRgn (sysRgn, ps.left, ps.top);
+                    OS.SetMetaRgn (gc.handle);
+                    OS.SetWindowOrgEx (gc.handle, ps.left, ps.top, null);
+                    OS.SetBrushOrgEx (gc.handle, ps.left, ps.top, null);
+                    if ((style & DWT.NO_BACKGROUND) !is 0) {
+                        /* This code is intentionally commented because it may be slow to copy bits from the screen */
+                        //paintGC.copyArea (image, ps.left, ps.top);
+                    } else {
+                        RECT rect = new RECT ();
+                        OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+                        drawBackground (gc.handle, rect);
+                    }
+                }
+                Event event = new Event ();
+                event.gc = gc;
+                RECT rect = null;
+                if ((style & DWT.NO_MERGE_PAINTS) !is 0 && OS.GetRgnBox (sysRgn, rect = new RECT ()) is OS.COMPLEXREGION) {
+                    int nBytes = OS.GetRegionData (sysRgn, 0, null);
+                    int [] lpRgnData = new int [nBytes / 4];
+                    OS.GetRegionData (sysRgn, nBytes, lpRgnData);
+                    int count = lpRgnData [2];
+                    for (int i=0; i<count; i++) {
+                        int offset = 8 + (i << 2);
+                        OS.SetRect (rect, lpRgnData [offset], lpRgnData [offset + 1], lpRgnData [offset + 2], lpRgnData [offset + 3]);
+                        if ((style & (DWT.DOUBLE_BUFFERED | DWT.NO_BACKGROUND)) is 0) {
+                            drawBackground (gc.handle, rect);
+                        }
+                        event.x = rect.left;
+                        event.y = rect.top;
+                        event.width = rect.right - rect.left;
+                        event.height = rect.bottom - rect.top;
+                        event.count = count - 1 - i;
+                        sendEvent (DWT.Paint, event);
+                    }
+                } else {
+                    if ((style & (DWT.DOUBLE_BUFFERED | DWT.NO_BACKGROUND)) is 0) {
+                        if (rect is null) rect = new RECT ();
+                        OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+                        drawBackground (gc.handle, rect);
+                    }
+                    event.x = ps.left;
+                    event.y = ps.top;
+                    event.width = width;
+                    event.height = height;
+                    sendEvent (DWT.Paint, event);
+                }
+                // widget could be disposed at this point
+                event.gc = null;
+                if ((style & DWT.DOUBLE_BUFFERED) !is 0) {
+                    gc.dispose();
+                    if (!isDisposed ()) paintGC.drawImage (image, ps.left, ps.top);
+                    image.dispose ();
+                    gc = paintGC;
+                }
+            }
+            if (sysRgn !is 0) OS.DeleteObject (sysRgn);
+
+            /* Dispose the paint GC */
+            gc.dispose ();
+        }
+    } else {
+        int hDC = OS.BeginPaint (handle, ps);
+        if ((style & DWT.NO_BACKGROUND) is 0) {
+            RECT rect = new RECT ();
+            OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+            drawBackground (hDC, rect);
+        }
+        OS.EndPaint (handle, ps);
+    }
+
+    /* Restore the clipping bits */
+    if (!OS.IsWinCE && !isDisposed ()) {
+        if (newBits !is oldBits) {
+            /*
+            * It is possible (but unlikely), that application
+            * code could have disposed the widget in the paint
+            * event.  If this happens, don't attempt to restore
+            * the style.
+            */
+            if (!isDisposed ()) {
+                OS.SetWindowLong (handle, OS.GWL_STYLE, oldBits);
+            }
+        }
+    }
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_PRINTCLIENT (int wParam, int lParam) {
+    LRESULT result = super.WM_PRINTCLIENT (wParam, lParam);
+    if (result !is null) return result;
+    if ((state & CANVAS) !is 0) {
+        forceResize ();
+        int nSavedDC = OS.SaveDC (wParam);
+        RECT rect = new RECT ();
+        OS.GetClientRect (handle, rect);
+        if ((style & DWT.NO_BACKGROUND) is 0) {
+            drawBackground (wParam, rect);
+        }
+        if (hooks (DWT.Paint) || filters (DWT.Paint)) {
+            GCData data = new GCData ();
+            data.device = display;
+            data.foreground = getForegroundPixel ();
+            Control control = findBackgroundControl ();
+            if (control is null) control = this;
+            data.background = control.getBackgroundPixel ();
+            data.hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+            data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+            GC gc = GC.win32_new (wParam, data);
+            Event event = new Event ();
+            event.gc = gc;
+            event.x = rect.left;
+            event.y = rect.top;
+            event.width = rect.right - rect.left;
+            event.height = rect.bottom - rect.top;
+            sendEvent (DWT.Paint, event);
+            event.gc = null;
+            gc.dispose ();
+        }
+        OS.RestoreDC (wParam, nSavedDC);
+    }
+    return result;
+}
+
+LRESULT WM_SETFONT (int wParam, int lParam) {
+    if (lParam !is 0) OS.InvalidateRect (handle, null, true);
+    return super.WM_SETFONT (font = wParam, lParam);
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+
+    /* Begin deferred window positioning */
+    setResizeChildren (false);
+
+    /* Resize and Layout */
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in the resize
+    * event.  If this happens, end the processing of the
+    * Windows message by returning the result of the
+    * WM_SIZE message.
+    */
+    if (isDisposed ()) return result;
+    if (layout !is null) {
+        markLayout (false, false);
+        updateLayout (false, false);
+    }
+
+    /* End deferred window positioning */
+    setResizeChildren (true);
+
+    /* Damage the widget to cause a repaint */
+    if (OS.IsWindowVisible (handle)) {
+        if ((state & CANVAS) !is 0) {
+            if ((style & DWT.NO_REDRAW_RESIZE) is 0) {
+                if (hooks (DWT.Paint)) {
+                    OS.InvalidateRect (handle, null, true);
+                }
+            }
+        }
+        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+            if (findThemeControl () !is null) redrawChildren ();
+        }
+    }
+
+    /* Resize the embedded window */
+    if ((state & CANVAS) !is 0 && (style & DWT.EMBEDDED) !is 0) {
+        resizeEmbeddedHandle (OS.GetWindow (handle, OS.GW_CHILD), lParam & 0xFFFF, lParam >> 16);
+    }
+    return result;
+}
+
+LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) {
+    LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
+    if (result !is null) return result;
+    int hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+    while (hwndChild !is 0) {
+        OS.SendMessage (hwndChild, OS.WM_SYSCOLORCHANGE, 0, 0);
+        hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+    }
+    return result;
+}
+
+LRESULT WM_SYSCOMMAND (int wParam, int lParam) {
+    LRESULT result = super.WM_SYSCOMMAND (wParam, lParam);
+    if (result !is null) return result;
+
+    /*
+    * Check to see if the command is a system command or
+    * a user menu item that was added to the system menu.
+    */
+    if ((wParam & 0xF000) is 0) return result;
+
+    /*
+    * Bug in Windows.  When a vertical or horizontal scroll bar is
+    * hidden or shown while the opposite scroll bar is being scrolled
+    * by the user (with WM_HSCROLL code SB_LINEDOWN), the scroll bar
+    * does not redraw properly.  The fix is to detect this case and
+    * redraw the non-client area.
+    */
+    if (!OS.IsWinCE) {
+        int cmd = wParam & 0xFFF0;
+        switch (cmd) {
+            case OS.SC_HSCROLL:
+            case OS.SC_VSCROLL:
+                bool showHBar = horizontalBar !is null && horizontalBar.getVisible ();
+                bool showVBar = verticalBar !is null && verticalBar.getVisible ();
+                int code = callWindowProc (handle, OS.WM_SYSCOMMAND, wParam, lParam);
+                if ((showHBar !is (horizontalBar !is null && horizontalBar.getVisible ())) ||
+                    (showVBar !is (verticalBar !is null && verticalBar.getVisible ()))) {
+                        int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_UPDATENOW;
+                        OS.RedrawWindow (handle, null, 0, flags);
+                    }
+                if (code is 0) return LRESULT.ZERO;
+                return new LRESULT (code);
+        }
+    }
+    /* Return the result */
+    return result;
+}
+
+LRESULT WM_UPDATEUISTATE (int wParam, int lParam) {
+    LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam);
+    if (result !is null) return result;
+    if ((state & CANVAS) !is 0) OS.InvalidateRect (handle, null, false);
+    return result;
+}
+
+LRESULT wmNCPaint (int hwnd, int wParam, int lParam) {
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        int bits1 = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE);
+        if ((bits1 & OS.WS_EX_CLIENTEDGE) !is 0) {
+            int code = 0;
+            int bits2 = OS.GetWindowLong (hwnd, OS.GWL_STYLE);
+            if ((bits2 & (OS.WS_HSCROLL | OS.WS_VSCROLL)) !is 0) {
+                code = callWindowProc (hwnd, OS.WM_NCPAINT, wParam, lParam);
+            }
+            int hDC = OS.GetWindowDC (hwnd);
+            RECT rect = new RECT ();
+            OS.GetWindowRect (hwnd, rect);
+            rect.right -= rect.left;
+            rect.bottom -= rect.top;
+            rect.left = rect.top = 0;
+            int border = OS.GetSystemMetrics (OS.SM_CXEDGE);
+            OS.ExcludeClipRect (hDC, border, border, rect.right - border, rect.bottom - border);
+            OS.DrawThemeBackground (display.hEditTheme (), hDC, OS.EP_EDITTEXT, OS.ETS_NORMAL, rect, null);
+            OS.ReleaseDC (hwnd, hDC);
+            return new LRESULT (code);
+        }
+    }
+    return null;
+}
+
+LRESULT wmNotify (NMHDR hdr, int wParam, int lParam) {
+    if (!OS.IsWinCE) {
+        switch (hdr.code) {
+            /*
+            * Feature in Windows.  When the tool tip control is
+            * created, the parent of the tool tip is the shell.
+            * If SetParent () is used to reparent the tool bar
+            * into a new shell, the tool tip is not reparented
+            * and pops up underneath the new shell.  The fix is
+            * to make sure the tool tip is a topmost window.
+            */
+            case OS.TTN_SHOW:
+            case OS.TTN_POP: {
+                /*
+                * Bug in Windows 98 and NT.  Setting the tool tip to be the
+                * top most window using HWND_TOPMOST can result in a parent
+                * dialog shell being moved behind its parent if the dialog
+                * has a sibling that is currently on top.  The fix is to
+                * lock the z-order of the active window.
+                *
+                * Feature in Windows.  Using SetWindowPos() with HWND_NOTOPMOST
+                * to clear the topmost state of a window whose parent is already
+                * topmost clears the topmost state of the parent.  The fix is to
+                * check if the parent is already on top and neither set or clear
+                * the topmost status of the tool tip.
+                */
+                int hwndParent = hdr.hwndFrom;
+                do {
+                    hwndParent = OS.GetParent (hwndParent);
+                    if (hwndParent is 0) break;
+                    int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE);
+                    if ((bits & OS.WS_EX_TOPMOST) !is 0) break;
+                } while (true);
+                if (hwndParent !is 0) break;
+                display.lockActiveWindow = true;
+                int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOSIZE;
+                int hwndInsertAfter = hdr.code is OS.TTN_SHOW ? OS.HWND_TOPMOST : OS.HWND_NOTOPMOST;
+                SetWindowPos (hdr.hwndFrom, hwndInsertAfter, 0, 0, 0, 0, flags);
+                display.lockActiveWindow = false;
+                break;
+            }
+            /*
+            * Bug in Windows 98.  For some reason, the tool bar control
+            * sends both TTN_GETDISPINFOW and TTN_GETDISPINFOA to get
+            * the tool tip text and the tab folder control sends only
+            * TTN_GETDISPINFOW.  The fix is to handle only TTN_GETDISPINFOW,
+            * even though it should never be sent on Windows 98.
+            *
+            * NOTE:  Because the size of NMTTDISPINFO differs between
+            * Windows 98 and NT, guard against the case where the wrong
+            * kind of message occurs by inlining the memory moves and
+            * the UNICODE conversion code.
+            */
+            case OS.TTN_GETDISPINFOA:
+            case OS.TTN_GETDISPINFOW: {
+                NMTTDISPINFO lpnmtdi;
+                if (hdr.code is OS.TTN_GETDISPINFOA) {
+                    lpnmtdi = new NMTTDISPINFOA ();
+                    OS.MoveMemory ((NMTTDISPINFOA)lpnmtdi, lParam, NMTTDISPINFOA.sizeof);
+                } else {
+                    lpnmtdi = new NMTTDISPINFOW ();
+                    OS.MoveMemory ((NMTTDISPINFOW)lpnmtdi, lParam, NMTTDISPINFOW.sizeof);
+                }
+                String string = toolTipText (lpnmtdi);
+                if (string !is null) {
+                    Shell shell = getShell ();
+                    string = Display.withCrLf (string);
+                    int length = string.length ();
+                    char [] chars = new char [length + 1];
+                    string.getChars (0, length, chars, 0);
+
+                    /*
+                    * Ensure that the orientation of the tool tip matches
+                    * the orientation of the control.
+                    */
+                    int hwnd = hdr.idFrom;
+                    if (hwnd !is 0 && ((lpnmtdi.uFlags & OS.TTF_IDISHWND) !is 0)) {
+                        Control control = display.getControl (hwnd);
+                        if (control !is null) {
+                            if ((control.getStyle () & DWT.RIGHT_TO_LEFT) !is 0) {
+                                lpnmtdi.uFlags |= OS.TTF_RTLREADING;
+                            } else {
+                                lpnmtdi.uFlags &= ~OS.TTF_RTLREADING;
+                            }
+                        }
+                    }
+
+                    if (hdr.code is OS.TTN_GETDISPINFOA) {
+                        byte [] bytes = new byte [chars.length * 2];
+                        OS.WideCharToMultiByte (getCodePage (), 0, chars, chars.length, bytes, bytes.length, null, null);
+                        shell.setToolTipText (lpnmtdi, bytes);
+                        OS.MoveMemory (lParam, (NMTTDISPINFOA)lpnmtdi, NMTTDISPINFOA.sizeof);
+                    } else {
+                        shell.setToolTipText (lpnmtdi, chars);
+                        OS.MoveMemory (lParam, (NMTTDISPINFOW)lpnmtdi, NMTTDISPINFOW.sizeof);
+                    }
+                    return LRESULT.ZERO;
+                }
+                break;
+            }
+        }
+    }
+    return super.wmNotify (hdr, wParam, lParam);
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Control.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,4639 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Control;
+
+import dwt.widgets.Widget;
+import dwt.widgets.Composite;
+import dwt.graphics.Drawable;
+import dwt.graphics.Rectangle;
+import dwt.graphics.Point;
+import dwt.graphics.GCData;
+import dwt.internal.win32.OS;
+
+class Control : Widget, Drawable {
+    this();
+    this( Widget, int );
+public HDC internal_new_GC (GCData data) ;
+public void internal_dispose_GC (HDC hDC, GCData data) ;
+public Point computeSize (int wHint, int hHint) ;
+public Point computeSize (int wHint, int hHint, bool changed) ;
+public Composite getParent () ;
+public Object getLayoutData () ;
+public int getBorderWidth () ;
+public void setLayoutData (Object layoutData) ;
+public void setBounds (int x, int y, int width, int height) ;
+void setBounds (int x, int y, int width, int height, int flags) ;
+void setBounds (int x, int y, int width, int height, int flags, bool defer) ;
+public void setBounds (Rectangle rect) ;
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.accessibility.Accessible;
+import dwt.events.ControlListener;
+import dwt.events.DragDetectListener;
+import dwt.events.FocusListener;
+import dwt.events.HelpListener;
+import dwt.events.KeyListener;
+import dwt.events.MenuDetectListener;
+import dwt.events.MouseEvent;
+import dwt.events.MouseListener;
+import dwt.events.MouseMoveListener;
+import dwt.events.MouseTrackListener;
+import dwt.events.MouseWheelListener;
+import dwt.events.PaintListener;
+import dwt.events.TraverseListener;
+import dwt.graphics.Color;
+import dwt.graphics.Cursor;
+import dwt.graphics.Drawable;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.CREATESTRUCT;
+import dwt.internal.win32.DRAWITEMSTRUCT;
+import dwt.internal.win32.HELPINFO;
+import dwt.internal.win32.LOGFONT;
+import dwt.internal.win32.LOGFONTA;
+import dwt.internal.win32.LOGFONTW;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MEASUREITEMSTRUCT;
+import dwt.internal.win32.MENUITEMINFO;
+import dwt.internal.win32.MONITORINFO;
+import dwt.internal.win32.MSG;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WINDOWPOS;
+
+/**
+ * Control is the abstract superclass of all windowed user interface classes.
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b>
+ * <dd>BORDER</dd>
+ * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd>
+ * <dt><b>Events:</b>
+ * <dd>DragDetect, FocusIn, FocusOut, Help, KeyDown, KeyUp, MenuDetect, MouseDoubleClick, MouseDown, MouseEnter,
+ *     MouseExit, MouseHover, MouseUp, MouseMove, Move, Paint, Resize, Traverse</dd>
+ * </dl>
+ * </p><p>
+ * Only one of LEFT_TO_RIGHT or RIGHT_TO_LEFT may be specified.
+ * </p><p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+
+public abstract class Control extends Widget implements Drawable {
+    /**
+     * the handle to the OS 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 int handle;
+    Composite parent;
+    Cursor cursor;
+    Menu menu;
+    String toolTipText;
+    Object layoutData;
+    Accessible accessible;
+    Image backgroundImage;
+    int drawCount, foreground, background;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+Control () {
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#BORDER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Control (Composite parent, int style) {
+    super (parent, style);
+    this.parent = parent;
+    createWidget ();
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is moved or resized, by sending
+ * it one of the messages defined in the <code>ControlListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #removeControlListener
+ */
+public void addControlListener(ControlListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Resize,typedListener);
+    addListener (DWT.Move,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when a drag gesture occurs, by sending it
+ * one of the messages defined in the <code>DragDetectListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DragDetectListener
+ * @see #removeDragDetectListener
+ *
+ * @since 3.3
+ */
+public void addDragDetectListener (DragDetectListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.DragDetect,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control gains or loses focus, by sending
+ * it one of the messages defined in the <code>FocusListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see FocusListener
+ * @see #removeFocusListener
+ */
+public void addFocusListener (FocusListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.FocusIn,typedListener);
+    addListener (DWT.FocusOut,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when help events are generated for the control,
+ * by sending it one of the messages defined in the
+ * <code>HelpListener</code> interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see HelpListener
+ * @see #removeHelpListener
+ */
+public void addHelpListener (HelpListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Help, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when keys are pressed and released on the system keyboard, by sending
+ * it one of the messages defined in the <code>KeyListener</code>
+ * interface.
+ * <p>
+ * When a key listener is added to a control, the control
+ * will take part in widget traversal.  By default, all
+ * traversal keys (such as the tab key and so on) are
+ * delivered to the control.  In order for a control to take
+ * part in traversal, it should listen for traversal events.
+ * Otherwise, the user can traverse into a control but not
+ * out.  Note that native controls such as table and tree
+ * implement key traversal in the operating system.  It is
+ * not necessary to add traversal listeners for these controls,
+ * unless you want to override the default traversal.
+ * </p>
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see KeyListener
+ * @see #removeKeyListener
+ */
+public void addKeyListener (KeyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.KeyUp,typedListener);
+    addListener (DWT.KeyDown,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the platform-specific context menu trigger
+ * has occurred, by sending it one of the messages defined in
+ * the <code>MenuDetectListener</code> interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MenuDetectListener
+ * @see #removeMenuDetectListener
+ *
+ * @since 3.3
+ */
+public void addMenuDetectListener (MenuDetectListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.MenuDetect, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when mouse buttons are pressed and released, by sending
+ * it one of the messages defined in the <code>MouseListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MouseListener
+ * @see #removeMouseListener
+ */
+public void addMouseListener (MouseListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.MouseDown,typedListener);
+    addListener (DWT.MouseUp,typedListener);
+    addListener (DWT.MouseDoubleClick,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the mouse passes or hovers over controls, by sending
+ * it one of the messages defined in the <code>MouseTrackListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MouseTrackListener
+ * @see #removeMouseTrackListener
+ */
+public void addMouseTrackListener (MouseTrackListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.MouseEnter,typedListener);
+    addListener (DWT.MouseExit,typedListener);
+    addListener (DWT.MouseHover,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the mouse moves, by sending it one of the
+ * messages defined in the <code>MouseMoveListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MouseMoveListener
+ * @see #removeMouseMoveListener
+ */
+public void addMouseMoveListener (MouseMoveListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.MouseMove,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the mouse wheel is scrolled, by sending
+ * it one of the messages defined in the
+ * <code>MouseWheelListener</code> interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MouseWheelListener
+ * @see #removeMouseWheelListener
+ *
+ * @since 3.3
+ */
+public void addMouseWheelListener (MouseWheelListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.MouseWheel, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver needs to be painted, by sending it
+ * one of the messages defined in the <code>PaintListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see PaintListener
+ * @see #removePaintListener
+ */
+public void addPaintListener (PaintListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Paint,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when traversal events occur, by sending it
+ * one of the messages defined in the <code>TraverseListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see TraverseListener
+ * @see #removeTraverseListener
+ */
+public void addTraverseListener (TraverseListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Traverse,typedListener);
+}
+
+int borderHandle () {
+    return handle;
+}
+
+void checkBackground () {
+    Shell shell = getShell ();
+    if (this is shell) return;
+    state &= ~PARENT_BACKGROUND;
+    Composite composite = parent;
+    do {
+        int mode = composite.backgroundMode;
+        if (mode !is 0) {
+            if (mode is DWT.INHERIT_DEFAULT) {
+                Control control = this;
+                do {
+                    if ((control.state & THEME_BACKGROUND) is 0) {
+                        return;
+                    }
+                    control = control.parent;
+                } while (control !is composite);
+            }
+            state |= PARENT_BACKGROUND;
+            return;
+        }
+        if (composite is shell) break;
+        composite = composite.parent;
+    } while (true);
+}
+
+void checkBorder () {
+    if (getBorderWidth () is 0) style &= ~DWT.BORDER;
+}
+
+void checkBuffered () {
+    style &= ~DWT.DOUBLE_BUFFERED;
+}
+
+bool checkHandle (int hwnd) {
+    return hwnd is handle;
+}
+
+void checkMirrored () {
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+        if ((bits & OS.WS_EX_LAYOUTRTL) !is 0) style |= DWT.MIRRORED;
+    }
+}
+
+/**
+ * Returns the preferred size of the receiver.
+ * <p>
+ * The <em>preferred size</em> of a control is the size that it would
+ * best be displayed at. The width hint and height hint arguments
+ * allow the caller to ask a control questions such as "Given a particular
+ * width, how high does the control need to be to show all of the contents?"
+ * To indicate that the caller does not wish to constrain a particular
+ * dimension, the constant <code>DWT.DEFAULT</code> is passed for the hint.
+ * </p>
+ *
+ * @param wHint the width hint (can be <code>DWT.DEFAULT</code>)
+ * @param hHint the height hint (can be <code>DWT.DEFAULT</code>)
+ * @return the preferred size of the control
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Layout
+ * @see #getBorderWidth
+ * @see #getBounds
+ * @see #getSize
+ * @see #pack(bool)
+ * @see "computeTrim, getClientArea for controls that implement them"
+ */
+public Point computeSize (int wHint, int hHint) {
+    return computeSize (wHint, hHint, true);
+}
+
+/**
+ * Returns the preferred size of the receiver.
+ * <p>
+ * The <em>preferred size</em> of a control is the size that it would
+ * best be displayed at. The width hint and height hint arguments
+ * allow the caller to ask a control questions such as "Given a particular
+ * width, how high does the control need to be to show all of the contents?"
+ * To indicate that the caller does not wish to constrain a particular
+ * dimension, the constant <code>DWT.DEFAULT</code> is passed for the hint.
+ * </p><p>
+ * If the changed flag is <code>true</code>, it indicates that the receiver's
+ * <em>contents</em> have changed, therefore any caches that a layout manager
+ * containing the control may have been keeping need to be flushed. When the
+ * control is resized, the changed flag will be <code>false</code>, so layout
+ * manager caches can be retained.
+ * </p>
+ *
+ * @param wHint the width hint (can be <code>DWT.DEFAULT</code>)
+ * @param hHint the height hint (can be <code>DWT.DEFAULT</code>)
+ * @param changed <code>true</code> if the control's contents have changed, and <code>false</code> otherwise
+ * @return the preferred size of the control.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Layout
+ * @see #getBorderWidth
+ * @see #getBounds
+ * @see #getSize
+ * @see #pack(bool)
+ * @see "computeTrim, getClientArea for controls that implement them"
+ */
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = DEFAULT_WIDTH;
+    int height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    int border = getBorderWidth ();
+    width += border * 2;
+    height += border * 2;
+    return new Point (width, height);
+}
+
+Control computeTabGroup () {
+    if (isTabGroup ()) return this;
+    return parent.computeTabGroup ();
+}
+
+Control computeTabRoot () {
+    Control [] tabList = parent._getTabList ();
+    if (tabList !is null) {
+        int index = 0;
+        while (index < tabList.length) {
+            if (tabList [index] is this) break;
+            index++;
+        }
+        if (index is tabList.length) {
+            if (isTabGroup ()) return this;
+        }
+    }
+    return parent.computeTabRoot ();
+}
+
+Control [] computeTabList () {
+    if (isTabGroup ()) {
+        if (getVisible () && getEnabled ()) {
+            return new Control [] {this};
+        }
+    }
+    return new Control [0];
+}
+
+void createHandle () {
+    int hwndParent = widgetParent ();
+    handle = OS.CreateWindowEx (
+        widgetExtStyle (),
+        windowClass (),
+        null,
+        widgetStyle (),
+        OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
+        hwndParent,
+        0,
+        OS.GetModuleHandle (null),
+        widgetCreateStruct ());
+    if (handle is 0) error (DWT.ERROR_NO_HANDLES);
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.WS_CHILD) !is 0) {
+        OS.SetWindowLong (handle, OS.GWL_ID, handle);
+    }
+    if (OS.IsDBLocale && hwndParent !is 0) {
+        int hIMC = OS.ImmGetContext (hwndParent);
+        OS.ImmAssociateContext (handle, hIMC);
+        OS.ImmReleaseContext (hwndParent, hIMC);
+    }
+}
+
+void createWidget () {
+    state |= DRAG_DETECT;
+    foreground = background = -1;
+    checkOrientation (parent);
+    createHandle ();
+    checkBackground ();
+    checkBuffered ();
+    register ();
+    subclass ();
+    setDefaultFont ();
+    checkMirrored ();
+    checkBorder ();
+    if ((state & PARENT_BACKGROUND) !is 0) {
+        setBackground ();
+    }
+}
+
+int defaultBackground () {
+    if (OS.IsWinCE) return OS.GetSysColor (OS.COLOR_WINDOW);
+    return OS.GetSysColor (OS.COLOR_BTNFACE);
+}
+
+int defaultFont () {
+    return display.getSystemFont ().handle;
+}
+
+int defaultForeground () {
+    return OS.GetSysColor (OS.COLOR_WINDOWTEXT);
+}
+
+void deregister () {
+    display.removeControl (handle);
+}
+
+void destroyWidget () {
+    int hwnd = topHandle ();
+    releaseHandle ();
+    if (hwnd !is 0) {
+        OS.DestroyWindow (hwnd);
+    }
+}
+
+/**
+ * Detects a drag and drop gesture.  This method is used
+ * to detect a drag gesture when called from within a mouse
+ * down listener.
+ *
+ * <p>By default, a drag is detected when the gesture
+ * occurs anywhere within the client area of a control.
+ * Some controls, such as tables and trees, override this
+ * behavior.  In addition to the operating system specific
+ * drag gesture, they require the mouse to be inside an
+ * item.  Custom widget writers can use <code>setDragDetect</code>
+ * to disable the default detection, listen for mouse down,
+ * and then call <code>dragDetect()</code> from within the
+ * listener to conditionally detect a drag.
+ * </p>
+ *
+ * @param event the mouse down event
+ *
+ * @return <code>true</code> if the gesture occurred, and <code>false</code> otherwise.
+ *
+ * @exception IllegalArgumentException <ul>
+ *   <li>ERROR_NULL_ARGUMENT when the event is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DragDetectListener
+ * @see #addDragDetectListener
+ *
+ * @see #getDragDetect
+ * @see #setDragDetect
+ *
+ * @since 3.3
+ */
+public bool dragDetect (Event event) {
+    checkWidget ();
+    if (event is null) error (DWT.ERROR_NULL_ARGUMENT);
+    return dragDetect (event.button, event.count, event.stateMask, event.x, event.y);
+}
+
+/**
+ * Detects a drag and drop gesture.  This method is used
+ * to detect a drag gesture when called from within a mouse
+ * down listener.
+ *
+ * <p>By default, a drag is detected when the gesture
+ * occurs anywhere within the client area of a control.
+ * Some controls, such as tables and trees, override this
+ * behavior.  In addition to the operating system specific
+ * drag gesture, they require the mouse to be inside an
+ * item.  Custom widget writers can use <code>setDragDetect</code>
+ * to disable the default detection, listen for mouse down,
+ * and then call <code>dragDetect()</code> from within the
+ * listener to conditionally detect a drag.
+ * </p>
+ *
+ * @param event the mouse down event
+ *
+ * @return <code>true</code> if the gesture occurred, and <code>false</code> otherwise.
+ *
+ * @exception IllegalArgumentException <ul>
+ *   <li>ERROR_NULL_ARGUMENT when the event is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DragDetectListener
+ * @see #addDragDetectListener
+ *
+ * @see #getDragDetect
+ * @see #setDragDetect
+ *
+ * @since 3.3
+ */
+public bool dragDetect (MouseEvent event) {
+    checkWidget ();
+    if (event is null) error (DWT.ERROR_NULL_ARGUMENT);
+    return dragDetect (event.button, event.count, event.stateMask, event.x, event.y);
+}
+
+bool dragDetect (int button, int count, int stateMask, int x, int y) {
+    if (button !is 1 || count !is 1) return false;
+    bool dragging = dragDetect (handle, x, y, false, null, null);
+    if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
+        if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+    }
+    if (!dragging) {
+        /*
+        * Feature in Windows.  DragDetect() captures the mouse
+        * and tracks its movement until the user releases the
+        * left mouse button, presses the ESC key, or moves the
+        * mouse outside the drag rectangle.  If the user moves
+        * the mouse outside of the drag rectangle, DragDetect()
+        * returns true and a drag and drop operation can be
+        * started.  When the left mouse button is released or
+        * the ESC key is pressed, these events are consumed by
+        * DragDetect() so that application code that matches
+        * mouse down/up pairs or looks for the ESC key will not
+        * function properly.  The fix is to send the missing
+        * events when the drag has not started.
+        *
+        * NOTE: For now, don't send a fake WM_KEYDOWN/WM_KEYUP
+        * events for the ESC key.  This would require computing
+        * wParam (the key) and lParam (the repeat count, scan code,
+        * extended-key flag, context code, previous key-state flag,
+        * and transition-state flag) which is non-trivial.
+        */
+        if (button is 1 && OS.GetKeyState (OS.VK_ESCAPE) >= 0) {
+            int wParam = 0;
+            if ((stateMask & DWT.CTRL) !is 0) wParam |= OS.MK_CONTROL;
+            if ((stateMask & DWT.SHIFT) !is 0) wParam |= OS.MK_SHIFT;
+            if ((stateMask & DWT.ALT) !is 0) wParam |= OS.MK_ALT;
+            if ((stateMask & DWT.BUTTON1) !is 0) wParam |= OS.MK_LBUTTON;
+            if ((stateMask & DWT.BUTTON2) !is 0) wParam |= OS.MK_MBUTTON;
+            if ((stateMask & DWT.BUTTON3) !is 0) wParam |= OS.MK_RBUTTON;
+            if ((stateMask & DWT.BUTTON4) !is 0) wParam |= OS.MK_XBUTTON1;
+            if ((stateMask & DWT.BUTTON5) !is 0) wParam |= OS.MK_XBUTTON2;
+            int lParam = (x & 0xFFFF) | ((y << 16) & 0xFFFF0000);
+            OS.SendMessage (handle, OS.WM_LBUTTONUP, wParam, lParam);
+        }
+        return false;
+    }
+    return sendDragEvent (button, stateMask, x, y);
+}
+
+void drawBackground (int hDC) {
+    RECT rect = new RECT ();
+    OS.GetClientRect (handle, rect);
+    drawBackground (hDC, rect);
+}
+
+void drawBackground (int hDC, RECT rect) {
+    drawBackground (hDC, rect, -1);
+}
+
+void drawBackground (int hDC, RECT rect, int pixel) {
+    Control control = findBackgroundControl ();
+    if (control !is null) {
+        if (control.backgroundImage !is null) {
+            fillImageBackground (hDC, control, rect);
+            return;
+        }
+        pixel = control.getBackgroundPixel ();
+    }
+    if (pixel is -1) {
+        if ((state & THEME_BACKGROUND) !is 0) {
+            if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                control = findThemeControl ();
+                if (control !is null) {
+                    fillThemeBackground (hDC, control, rect);
+                    return;
+                }
+            }
+        }
+    }
+    if (pixel is -1) pixel = getBackgroundPixel ();
+    fillBackground (hDC, pixel, rect);
+}
+
+void drawImageBackground (int hDC, int hwnd, int hBitmap, RECT rect) {
+    RECT rect2 = new RECT ();
+    OS.GetClientRect (hwnd, rect2);
+    OS.MapWindowPoints (hwnd, handle, rect2, 2);
+    int hBrush = findBrush (hBitmap, OS.BS_PATTERN);
+    POINT lpPoint = new POINT ();
+    OS.GetWindowOrgEx (hDC, lpPoint);
+    OS.SetBrushOrgEx (hDC, -rect2.left - lpPoint.x, -rect2.top - lpPoint.y, lpPoint);
+    int hOldBrush = OS.SelectObject (hDC, hBrush);
+    OS.PatBlt (hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
+    OS.SetBrushOrgEx (hDC, lpPoint.x, lpPoint.y, null);
+    OS.SelectObject (hDC, hOldBrush);
+}
+
+void drawThemeBackground (int hDC, int hwnd, RECT rect) {
+    /* Do nothing */
+}
+
+void enableDrag (bool enabled) {
+    /* Do nothing */
+}
+
+void enableWidget (bool enabled) {
+    OS.EnableWindow (handle, enabled);
+}
+
+void fillBackground (int hDC, int pixel, RECT rect) {
+    if (rect.left > rect.right || rect.top > rect.bottom) return;
+    int hPalette = display.hPalette;
+    if (hPalette !is 0) {
+        OS.SelectPalette (hDC, hPalette, false);
+        OS.RealizePalette (hDC);
+    }
+    OS.FillRect (hDC, rect, findBrush (pixel, OS.BS_SOLID));
+}
+
+void fillImageBackground (int hDC, Control control, RECT rect) {
+    if (rect.left > rect.right || rect.top > rect.bottom) return;
+    if (control !is null) {
+        Image image = control.backgroundImage;
+        if (image !is null) {
+            control.drawImageBackground (hDC, handle, image.handle, rect);
+        }
+    }
+}
+
+void fillThemeBackground (int hDC, Control control, RECT rect) {
+    if (rect.left > rect.right || rect.top > rect.bottom) return;
+    if (control !is null) {
+        control.drawThemeBackground (hDC, handle, rect);
+    }
+}
+
+Control findBackgroundControl () {
+    if (background !is -1 || backgroundImage !is null) return this;
+    return (state & PARENT_BACKGROUND) !is 0 ? parent.findBackgroundControl () : null;
+}
+
+int findBrush (int value, int lbStyle) {
+    return parent.findBrush (value, lbStyle);
+}
+
+Cursor findCursor () {
+    if (cursor !is null) return cursor;
+    return parent.findCursor ();
+}
+
+Control findImageControl () {
+    Control control = findBackgroundControl ();
+    return control !is null && control.backgroundImage !is null ? control : null;
+}
+
+Control findThemeControl () {
+    return background is -1 && backgroundImage is null ? parent.findThemeControl () : null;
+}
+
+Menu [] findMenus (Control control) {
+    if (menu !is null && this !is control) return new Menu [] {menu};
+    return new Menu [0];
+}
+
+char findMnemonic (String string) {
+    int index = 0;
+    int length = string.length ();
+    do {
+        while (index < length && string.charAt (index) !is '&') index++;
+        if (++index >= length) return '\0';
+        if (string.charAt (index) !is '&') return string.charAt (index);
+        index++;
+    } while (index < length);
+    return '\0';
+}
+
+void fixChildren (Shell newShell, Shell oldShell, Decorations newDecorations, Decorations oldDecorations, Menu [] menus) {
+    oldShell.fixShell (newShell, this);
+    oldDecorations.fixDecorations (newDecorations, this, menus);
+}
+
+void fixFocus (Control focusControl) {
+    Shell shell = getShell ();
+    Control control = this;
+    while (control !is shell && (control = control.parent) !is null) {
+        if (control.setFixedFocus ()) return;
+    }
+    shell.setSavedFocus (focusControl);
+    OS.SetFocus (0);
+}
+
+/**
+ * Forces the receiver to have the <em>keyboard focus</em>, causing
+ * all keyboard events to be delivered to it.
+ *
+ * @return <code>true</code> if the control got focus, and <code>false</code> if it was unable to.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setFocus
+ */
+public bool forceFocus () {
+    checkWidget ();
+    if (display.focusEvent is DWT.FocusOut) return false;
+    Decorations shell = menuShell ();
+    shell.setSavedFocus (this);
+    if (!isEnabled () || !isVisible () || !isActive ()) return false;
+    if (isFocusControl ()) return true;
+    shell.setSavedFocus (null);
+    /*
+    * This code is intentionally commented.
+    *
+    * When setting focus to a control, it is
+    * possible that application code can set
+    * the focus to another control inside of
+    * WM_SETFOCUS.  In this case, the original
+    * control will no longer have the focus
+    * and the call to setFocus() will return
+    * false indicating failure.
+    *
+    * We are still working on a solution at
+    * this time.
+    */
+//  if (OS.GetFocus () !is OS.SetFocus (handle)) return false;
+    OS.SetFocus (handle);
+    if (isDisposed ()) return false;
+    shell.setSavedFocus (this);
+    return isFocusControl ();
+}
+
+void forceResize () {
+    if (parent is null) return;
+    WINDOWPOS [] lpwp = parent.lpwp;
+    if (lpwp is null) return;
+    for (int i=0; i<lpwp.length; i++) {
+        WINDOWPOS wp = lpwp [i];
+        if (wp !is null && wp.hwnd is handle) {
+            /*
+            * This code is intentionally commented.  All widgets that
+            * are created by DWT have WS_CLIPSIBLINGS to ensure that
+            * application code does not draw outside of the control.
+            */
+//          int count = parent.getChildrenCount ();
+//          if (count > 1) {
+//              int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+//              if ((bits & OS.WS_CLIPSIBLINGS) is 0) wp.flags |= OS.SWP_NOCOPYBITS;
+//          }
+            SetWindowPos (wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
+            lpwp [i] = null;
+            return;
+        }
+    }
+}
+
+/**
+ * Returns the accessible object for the receiver.
+ * If this is the first time this object is requested,
+ * then the object is created and returned.
+ *
+ * @return the accessible object
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Accessible#addAccessibleListener
+ * @see Accessible#addAccessibleControlListener
+ *
+ * @since 2.0
+ */
+public Accessible getAccessible () {
+    checkWidget ();
+    if (accessible is null) accessible = new_Accessible (this);
+    return accessible;
+}
+
+/**
+ * Returns the receiver's background color.
+ *
+ * @return the background color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Color getBackground () {
+    checkWidget ();
+    Control control = findBackgroundControl ();
+    if (control is null) control = this;
+    return Color.win32_new (display, control.getBackgroundPixel ());
+}
+
+/**
+ * Returns the receiver's background image.
+ *
+ * @return the background image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public Image getBackgroundImage () {
+    checkWidget ();
+    Control control = findBackgroundControl ();
+    if (control is null) control = this;
+    return control.backgroundImage;
+}
+
+int getBackgroundPixel () {
+    return background !is -1 ? background :  defaultBackground ();
+}
+
+/**
+ * Returns the receiver's border width.
+ *
+ * @return the border width
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getBorderWidth () {
+    checkWidget ();
+    int borderHandle = borderHandle ();
+    int bits1 = OS.GetWindowLong (borderHandle, OS.GWL_EXSTYLE);
+    if ((bits1 & OS.WS_EX_CLIENTEDGE) !is 0) return OS.GetSystemMetrics (OS.SM_CXEDGE);
+    if ((bits1 & OS.WS_EX_STATICEDGE) !is 0) return OS.GetSystemMetrics (OS.SM_CXBORDER);
+    int bits2 = OS.GetWindowLong (borderHandle, OS.GWL_STYLE);
+    if ((bits2 & OS.WS_BORDER) !is 0) return OS.GetSystemMetrics (OS.SM_CXBORDER);
+    return 0;
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent (or its display if its parent is null),
+ * unless the receiver is a shell. In this case, the location is
+ * relative to the display.
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds () {
+    checkWidget ();
+    forceResize ();
+    RECT rect = new RECT ();
+    OS.GetWindowRect (topHandle (), rect);
+    int hwndParent = parent is null ? 0 : parent.handle;
+    OS.MapWindowPoints (0, hwndParent, rect, 2);
+    int width = rect.right - rect.left;
+    int height =  rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+int getCodePage () {
+    if (OS.IsUnicode) return OS.CP_ACP;
+    int hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    LOGFONT logFont = OS.IsUnicode ? (LOGFONT) new LOGFONTW () : new LOGFONTA ();
+    OS.GetObject (hFont, LOGFONT.sizeof, logFont);
+    int cs = logFont.lfCharSet & 0xFF;
+    int [] lpCs = new int [8];
+    if (OS.TranslateCharsetInfo (cs, lpCs, OS.TCI_SRCCHARSET)) {
+        return lpCs [1];
+    }
+    return OS.GetACP ();
+}
+
+String getClipboardText () {
+    String string = "";
+    if (OS.OpenClipboard (0)) {
+        int hMem = OS.GetClipboardData (OS.IsUnicode ? OS.CF_UNICODETEXT : OS.CF_TEXT);
+        if (hMem !is 0) {
+            /* Ensure byteCount is a multiple of 2 bytes on UNICODE platforms */
+            int byteCount = OS.GlobalSize (hMem) / TCHAR.sizeof * TCHAR.sizeof;
+            int ptr = OS.GlobalLock (hMem);
+            if (ptr !is 0) {
+                /* Use the character encoding for the default locale */
+                TCHAR buffer = new TCHAR (0, byteCount / TCHAR.sizeof);
+                OS.MoveMemory (buffer, ptr, byteCount);
+                string = buffer.toString (0, buffer.strlen ());
+                OS.GlobalUnlock (hMem);
+            }
+        }
+        OS.CloseClipboard ();
+    }
+    return string;
+}
+
+/**
+ * Returns the receiver's cursor, or null if it has not been set.
+ * <p>
+ * When the mouse pointer passes over a control its appearance
+ * is changed to match the control's cursor.
+ * </p>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public Cursor getCursor () {
+    checkWidget ();
+    return cursor;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is detecting
+ * drag gestures, and  <code>false</code> otherwise.
+ *
+ * @return the receiver's drag detect state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public bool getDragDetect () {
+    checkWidget ();
+    return (state & DRAG_DETECT) !is 0;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled, and
+ * <code>false</code> otherwise. A disabled control is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #isEnabled
+ */
+public bool getEnabled () {
+    checkWidget ();
+    return OS.IsWindowEnabled (handle);
+}
+
+/**
+ * Returns the font that the receiver will use to paint textual information.
+ *
+ * @return the receiver's font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Font getFont () {
+    checkWidget ();
+    int hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (hFont is 0) hFont = defaultFont ();
+    return Font.win32_new (display, hFont);
+}
+
+/**
+ * Returns the foreground color that the receiver will use to draw.
+ *
+ * @return the receiver's foreground color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Color getForeground () {
+    checkWidget ();
+    return Color.win32_new (display, getForegroundPixel ());
+}
+
+int getForegroundPixel () {
+    return foreground !is -1 ? foreground : defaultForeground ();
+}
+
+/**
+ * Returns layout data which is associated with the receiver.
+ *
+ * @return the receiver's layout data
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Object getLayoutData () {
+    checkWidget ();
+    return layoutData;
+}
+
+/**
+ * Returns a point describing the receiver's location relative
+ * to its parent (or its display if its parent is null), unless
+ * the receiver is a shell. In this case, the point is
+ * relative to the display.
+ *
+ * @return the receiver's location
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getLocation () {
+    checkWidget ();
+    forceResize ();
+    RECT rect = new RECT ();
+    OS.GetWindowRect (topHandle (), rect);
+    int hwndParent = parent is null ? 0 : parent.handle;
+    OS.MapWindowPoints (0, hwndParent, rect, 2);
+    return new Point (rect.left, rect.top);
+}
+
+/**
+ * Returns the receiver's pop up menu if it has one, or null
+ * if it does not. All controls may optionally have a pop up
+ * menu that is displayed when the user requests one for
+ * the control. The sequence of key strokes, button presses
+ * and/or button releases that are used to request a pop up
+ * menu is platform specific.
+ *
+ * @return the receiver's menu
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Menu getMenu () {
+    checkWidget ();
+    return menu;
+}
+
+/**
+ * Returns the receiver's monitor.
+ *
+ * @return the receiver's monitor
+ *
+ * @since 3.0
+ */
+public Monitor getMonitor () {
+    checkWidget ();
+    if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) {
+        return display.getPrimaryMonitor ();
+    }
+    int hmonitor = OS.MonitorFromWindow (handle, OS.MONITOR_DEFAULTTONEAREST);
+    MONITORINFO lpmi = new MONITORINFO ();
+    lpmi.cbSize = MONITORINFO.sizeof;
+    OS.GetMonitorInfo (hmonitor, lpmi);
+    Monitor monitor = new Monitor ();
+    monitor.handle = hmonitor;
+    monitor.x = lpmi.rcMonitor_left;
+    monitor.y = lpmi.rcMonitor_top;
+    monitor.width = lpmi.rcMonitor_right - lpmi.rcMonitor_left;
+    monitor.height = lpmi.rcMonitor_bottom - lpmi.rcMonitor_top;
+    monitor.clientX = lpmi.rcWork_left;
+    monitor.clientY = lpmi.rcWork_top;
+    monitor.clientWidth = lpmi.rcWork_right - lpmi.rcWork_left;
+    monitor.clientHeight = lpmi.rcWork_bottom - lpmi.rcWork_top;
+    return monitor;
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Composite</code>
+ * or null when the receiver is a shell that was created with null or
+ * a display for a parent.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Composite getParent () {
+    checkWidget ();
+    return parent;
+}
+
+Control [] getPath () {
+    int count = 0;
+    Shell shell = getShell ();
+    Control control = this;
+    while (control !is shell) {
+        count++;
+        control = control.parent;
+    }
+    control = this;
+    Control [] result = new Control [count];
+    while (control !is shell) {
+        result [--count] = control;
+        control = control.parent;
+    }
+    return result;
+}
+
+/**
+ * Returns the receiver's shell. For all controls other than
+ * shells, this simply returns the control's nearest ancestor
+ * shell. Shells return themselves, even if they are children
+ * of other shells.
+ *
+ * @return the receiver's shell
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getParent
+ */
+public Shell getShell () {
+    checkWidget ();
+    return parent.getShell ();
+}
+
+/**
+ * Returns a point describing the receiver's size. The
+ * x coordinate of the result is the width of the receiver.
+ * The y coordinate of the result is the height of the
+ * receiver.
+ *
+ * @return the receiver's size
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getSize () {
+    checkWidget ();
+    forceResize ();
+    RECT rect = new RECT ();
+    OS.GetWindowRect (topHandle (), rect);
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+    return new Point (width, height);
+}
+
+/**
+ * Returns the receiver's tool tip text, or null if it has
+ * not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getToolTipText () {
+    checkWidget ();
+    return toolTipText;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible, and
+ * <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getVisible () {
+    checkWidget ();
+    if (drawCount !is 0) return (state & HIDDEN) is 0;
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    return (bits & OS.WS_VISIBLE) !is 0;
+}
+
+bool hasCursor () {
+    RECT rect = new RECT ();
+    if (!OS.GetClientRect (handle, rect)) return false;
+    OS.MapWindowPoints (handle, 0, rect, 2);
+    POINT pt = new POINT ();
+    return OS.GetCursorPos (pt) && OS.PtInRect (rect, pt);
+}
+
+bool hasFocus () {
+    /*
+    * If a non-DWT child of the control has focus,
+    * then this control is considered to have focus
+    * even though it does not have focus in Windows.
+    */
+    int hwndFocus = OS.GetFocus ();
+    while (hwndFocus !is 0) {
+        if (hwndFocus is handle) return true;
+        if (display.getControl (hwndFocus) !is null) {
+            return false;
+        }
+        hwndFocus = OS.GetParent (hwndFocus);
+    }
+    return false;
+}
+
+/**
+ * Invokes platform specific functionality to allocate a new GC handle.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Control</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 data the platform specific GC data
+ * @return the platform specific GC handle
+ */
+public int internal_new_GC (GCData data) {
+    checkWidget();
+    int hwnd = handle;
+    if (data !is null && data.hwnd !is 0) hwnd = data.hwnd;
+    if (data !is null) data.hwnd = hwnd;
+    int hDC = 0;
+    if (data is null || data.ps is null) {
+        hDC = OS.GetDC (hwnd);
+    } else {
+        hDC = OS.BeginPaint (hwnd, data.ps);
+    }
+    if (hDC is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+    if (data !is null) {
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+            int mask = DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT;
+            if ((data.style & mask) !is 0) {
+                data.layout = (data.style & DWT.RIGHT_TO_LEFT) !is 0 ? OS.LAYOUT_RTL : 0;
+            } else {
+                int flags = OS.GetLayout (hDC);
+                if ((flags & OS.LAYOUT_RTL) !is 0) {
+                    data.style |= DWT.RIGHT_TO_LEFT | DWT.MIRRORED;
+                } else {
+                    data.style |= DWT.LEFT_TO_RIGHT;
+                }
+            }
+        } else {
+            data.style |= DWT.LEFT_TO_RIGHT;
+        }
+        data.device = display;
+        int foreground = getForegroundPixel ();
+        if (foreground !is OS.GetTextColor (hDC)) data.foreground = foreground;
+        Control control = findBackgroundControl ();
+        if (control is null) control = this;
+        int background = control.getBackgroundPixel ();
+        if (background !is OS.GetBkColor (hDC)) data.background = background;
+        data.hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0);
+        data.uiState = OS.SendMessage (hwnd, OS.WM_QUERYUISTATE, 0, 0);
+    }
+    return hDC;
+}
+
+/**
+ * Invokes platform specific functionality to dispose a GC handle.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Control</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 hDC the platform specific GC handle
+ * @param data the platform specific GC data
+ */
+public void internal_dispose_GC (int hDC, GCData data) {
+    checkWidget ();
+    int hwnd = handle;
+    if (data !is null && data.hwnd !is 0) {
+        hwnd = data.hwnd;
+    }
+    if (data is null || data.ps is null) {
+        OS.ReleaseDC (hwnd, hDC);
+    } else {
+        OS.EndPaint (hwnd, data.ps);
+    }
+}
+
+bool isActive () {
+    Shell dialogShell = display.getModalDialogShell ();
+    if (dialogShell !is null && dialogShell !is getShell ()) {
+        return false;
+    }
+    Shell shell = null;
+    Shell [] modalShells = display.modalShells;
+    if (modalShells !is null) {
+        int bits = DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL;
+        int index = modalShells.length;
+        while (--index >= 0) {
+            Shell modal = modalShells [index];
+            if (modal !is null) {
+                if ((modal.style & bits) !is 0) {
+                    Control control = this;
+                    while (control !is null) {
+                        if (control is modal) break;
+                        control = control.parent;
+                    }
+                    if (control !is modal) return false;
+                    break;
+                }
+                if ((modal.style & DWT.PRIMARY_MODAL) !is 0) {
+                    if (shell is null) shell = getShell ();
+                    if (modal.parent is shell) return false;
+                }
+            }
+        }
+    }
+    if (shell is null) shell = getShell ();
+    return shell.getEnabled ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled and all
+ * ancestors up to and including the receiver's nearest ancestor
+ * shell are enabled.  Otherwise, <code>false</code> is returned.
+ * A disabled control is typically not selectable from the user
+ * interface and draws with an inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getEnabled
+ */
+public bool isEnabled () {
+    checkWidget ();
+    return getEnabled () && parent.isEnabled ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver has the user-interface
+ * focus, and <code>false</code> otherwise.
+ *
+ * @return the receiver's focus state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool isFocusControl () {
+    checkWidget ();
+    Control focusControl = display.focusControl;
+    if (focusControl !is null && !focusControl.isDisposed ()) {
+        return this is focusControl;
+    }
+    return hasFocus ();
+}
+
+bool isFocusAncestor (Control control) {
+    while (control !is null && control !is this && !(control instanceof Shell)) {
+        control = control.parent;
+    }
+    return control is this;
+}
+
+/**
+ * Returns <code>true</code> if the underlying operating
+ * system supports this reparenting, otherwise <code>false</code>
+ *
+ * @return <code>true</code> if the widget can be reparented, otherwise <code>false</code>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool isReparentable () {
+    checkWidget ();
+    return true;
+}
+
+bool isShowing () {
+    /*
+    * This is not complete.  Need to check if the
+    * widget is obscured by a parent or sibling.
+    */
+    if (!isVisible ()) return false;
+    Control control = this;
+    while (control !is null) {
+        Point size = control.getSize ();
+        if (size.x is 0 || size.y is 0) {
+            return false;
+        }
+        control = control.parent;
+    }
+    return true;
+    /*
+    * Check to see if current damage is included.
+    */
+//  if (!OS.IsWindowVisible (handle)) return false;
+//  int flags = OS.DCX_CACHE | OS.DCX_CLIPCHILDREN | OS.DCX_CLIPSIBLINGS;
+//  int hDC = OS.GetDCEx (handle, 0, flags);
+//  int result = OS.GetClipBox (hDC, new RECT ());
+//  OS.ReleaseDC (handle, hDC);
+//  return result !is OS.NULLREGION;
+}
+
+bool isTabGroup () {
+    Control [] tabList = parent._getTabList ();
+    if (tabList !is null) {
+        for (int i=0; i<tabList.length; i++) {
+            if (tabList [i] is this) return true;
+        }
+    }
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    return (bits & OS.WS_TABSTOP) !is 0;
+}
+
+bool isTabItem () {
+    Control [] tabList = parent._getTabList ();
+    if (tabList !is null) {
+        for (int i=0; i<tabList.length; i++) {
+            if (tabList [i] is this) return false;
+        }
+    }
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.WS_TABSTOP) !is 0) return false;
+    int code = OS.SendMessage (handle, OS.WM_GETDLGCODE, 0, 0);
+    if ((code & OS.DLGC_STATIC) !is 0) return false;
+    if ((code & OS.DLGC_WANTALLKEYS) !is 0) return false;
+    if ((code & OS.DLGC_WANTARROWS) !is 0) return false;
+    if ((code & OS.DLGC_WANTTAB) !is 0) return false;
+    return true;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible and all
+ * ancestors up to and including the receiver's nearest ancestor
+ * shell are visible. Otherwise, <code>false</code> is returned.
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getVisible
+ */
+public bool isVisible () {
+    checkWidget ();
+    if (OS.IsWindowVisible (handle)) return true;
+    return getVisible () && parent.isVisible ();
+}
+
+void mapEvent (int hwnd, Event event) {
+    if (hwnd !is handle) {
+        POINT point = new POINT ();
+        point.x = event.x;
+        point.y = event.y;
+        OS.MapWindowPoints (hwnd, handle, point, 1);
+        event.x = point.x;
+        event.y = point.y;
+    }
+}
+
+void markLayout (bool changed, bool all) {
+    /* Do nothing */
+}
+
+Decorations menuShell () {
+    return parent.menuShell ();
+}
+
+bool mnemonicHit (char key) {
+    return false;
+}
+
+bool mnemonicMatch (char key) {
+    return false;
+}
+
+/**
+ * Moves the receiver above the specified control in the
+ * drawing order. If the argument is null, then the receiver
+ * is moved to the top of the drawing order. The control at
+ * the top of the drawing order will not be covered by other
+ * controls even if they occupy intersecting areas.
+ *
+ * @param control the sibling control (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Control#moveBelow
+ * @see Composite#getChildren
+ */
+public void moveAbove (Control control) {
+    checkWidget ();
+    int topHandle = topHandle (), hwndAbove = OS.HWND_TOP;
+    if (control !is null) {
+        if (control.isDisposed ()) error(DWT.ERROR_INVALID_ARGUMENT);
+        if (parent !is control.parent) return;
+        int hwnd = control.topHandle ();
+        if (hwnd is 0 || hwnd is topHandle) return;
+        hwndAbove = OS.GetWindow (hwnd, OS.GW_HWNDPREV);
+        /*
+        * Bug in Windows.  For some reason, when GetWindow ()
+        * with GW_HWNDPREV is used to query the previous window
+        * in the z-order with the first child, Windows returns
+        * the first child instead of NULL.  The fix is to detect
+        * this case and move the control to the top.
+        */
+        if (hwndAbove is 0 || hwndAbove is hwnd) {
+            hwndAbove = OS.HWND_TOP;
+        }
+    }
+    int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
+    SetWindowPos (topHandle, hwndAbove, 0, 0, 0, 0, flags);
+}
+
+/**
+ * Moves the receiver below the specified control in the
+ * drawing order. If the argument is null, then the receiver
+ * is moved to the bottom of the drawing order. The control at
+ * the bottom of the drawing order will be covered by all other
+ * controls which occupy intersecting areas.
+ *
+ * @param control the sibling control (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Control#moveAbove
+ * @see Composite#getChildren
+ */
+public void moveBelow (Control control) {
+    checkWidget ();
+    int topHandle = topHandle (), hwndAbove = OS.HWND_BOTTOM;
+    if (control !is null) {
+        if (control.isDisposed ()) error(DWT.ERROR_INVALID_ARGUMENT);
+        if (parent !is control.parent) return;
+        hwndAbove = control.topHandle ();
+    } else {
+        /*
+        * Bug in Windows.  When SetWindowPos() is called
+        * with HWND_BOTTOM on a dialog shell, the dialog
+        * and the parent are moved to the bottom of the
+        * desktop stack.  The fix is to move the dialog
+        * to the bottom of the dialog window stack by
+        * moving behind the first dialog child.
+        */
+        Shell shell = getShell ();
+        if (this is shell && parent !is null) {
+            /*
+            * Bug in Windows.  For some reason, when GetWindow ()
+            * with GW_HWNDPREV is used to query the previous window
+            * in the z-order with the first child, Windows returns
+            * the first child instead of NULL.  The fix is to detect
+            * this case and do nothing because the control is already
+            * at the bottom.
+            */
+            int hwndParent = parent.handle, hwnd = hwndParent;
+            hwndAbove = OS.GetWindow (hwnd, OS.GW_HWNDPREV);
+            while (hwndAbove !is 0 && hwndAbove !is hwnd) {
+                if (OS.GetWindow (hwndAbove, OS.GW_OWNER) is hwndParent) break;
+                hwndAbove = OS.GetWindow (hwnd = hwndAbove, OS.GW_HWNDPREV);
+            }
+            if (hwndAbove is hwnd) return;
+        }
+    }
+    if (hwndAbove is 0 || hwndAbove is topHandle) return;
+    int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
+    SetWindowPos (topHandle, hwndAbove, 0, 0, 0, 0, flags);
+}
+
+Accessible new_Accessible (Control control) {
+    return Accessible.internal_new_Accessible (this);
+}
+
+GC new_GC (GCData data) {
+    return GC.win32_new (this, data);
+}
+
+/**
+ * Causes the receiver to be resized to its preferred size.
+ * For a composite, this involves computing the preferred size
+ * from its layout, if there is one.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #computeSize(int, int, bool)
+ */
+public void pack () {
+    checkWidget ();
+    pack (true);
+}
+
+/**
+ * Causes the receiver to be resized to its preferred size.
+ * For a composite, this involves computing the preferred size
+ * from its layout, if there is one.
+ * <p>
+ * If the changed flag is <code>true</code>, it indicates that the receiver's
+ * <em>contents</em> have changed, therefore any caches that a layout manager
+ * containing the control may have been keeping need to be flushed. When the
+ * control is resized, the changed flag will be <code>false</code>, so layout
+ * manager caches can be retained.
+ * </p>
+ *
+ * @param changed whether or not the receiver's contents have changed
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #computeSize(int, int, bool)
+ */
+public void pack (bool changed) {
+    checkWidget ();
+    setSize (computeSize (DWT.DEFAULT, DWT.DEFAULT, changed));
+}
+
+/**
+ * Causes the entire bounds of the receiver to be marked
+ * as needing to be redrawn. The next time a paint request
+ * is processed, the control will be completely painted,
+ * including the background.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #update()
+ * @see PaintListener
+ * @see DWT#Paint
+ * @see DWT#NO_BACKGROUND
+ * @see DWT#NO_REDRAW_RESIZE
+ * @see DWT#NO_MERGE_PAINTS
+ * @see DWT#DOUBLE_BUFFERED
+ */
+public void redraw () {
+    checkWidget ();
+    redraw (false);
+}
+
+void redraw (bool all) {
+//  checkWidget ();
+    if (!OS.IsWindowVisible (handle)) return;
+    if (OS.IsWinCE) {
+        OS.InvalidateRect (handle, null, true);
+    } else {
+        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
+        if (all) flags |= OS.RDW_ALLCHILDREN;
+        OS.RedrawWindow (handle, null, 0, flags);
+    }
+}
+/**
+ * Causes the rectangular area of the receiver specified by
+ * the arguments to be marked as needing to be redrawn.
+ * The next time a paint request is processed, that area of
+ * the receiver will be painted, including the background.
+ * If the <code>all</code> flag is <code>true</code>, any
+ * children of the receiver which intersect with the specified
+ * area will also paint their intersecting areas. If the
+ * <code>all</code> flag is <code>false</code>, the children
+ * will not be painted.
+ *
+ * @param x the x coordinate of the area to draw
+ * @param y the y coordinate of the area to draw
+ * @param width the width of the area to draw
+ * @param height the height of the area to draw
+ * @param all <code>true</code> if children should redraw, and <code>false</code> otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #update()
+ * @see PaintListener
+ * @see DWT#Paint
+ * @see DWT#NO_BACKGROUND
+ * @see DWT#NO_REDRAW_RESIZE
+ * @see DWT#NO_MERGE_PAINTS
+ * @see DWT#DOUBLE_BUFFERED
+ */
+public void redraw (int x, int y, int width, int height, bool all) {
+    checkWidget ();
+    if (width <= 0 || height <= 0) return;
+    if (!OS.IsWindowVisible (handle)) return;
+    RECT rect = new RECT ();
+    OS.SetRect (rect, x, y, x + width, y + height);
+    if (OS.IsWinCE) {
+        OS.InvalidateRect (handle, rect, true);
+    } else {
+        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
+        if (all) flags |= OS.RDW_ALLCHILDREN;
+        OS.RedrawWindow (handle, rect, 0, flags);
+    }
+}
+
+bool redrawChildren () {
+    if (!OS.IsWindowVisible (handle)) return false;
+    Control control = findBackgroundControl ();
+    if (control is null) {
+        if ((state & THEME_BACKGROUND) !is 0) {
+            if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                OS.InvalidateRect (handle, null, true);
+                return true;
+            }
+        }
+    } else {
+        if (control.backgroundImage !is null) {
+            OS.InvalidateRect (handle, null, true);
+            return true;
+        }
+    }
+    return false;
+}
+
+void register () {
+    display.addControl (handle, this);
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    handle = 0;
+    parent = null;
+}
+
+void releaseParent () {
+    parent.removeControl (this);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if (OS.IsDBLocale) {
+        OS.ImmAssociateContext (handle, 0);
+    }
+    if (toolTipText !is null) {
+        setToolTipText (getShell (), null);
+    }
+    toolTipText = null;
+    if (menu !is null && !menu.isDisposed ()) {
+        menu.dispose ();
+    }
+    backgroundImage = null;
+    menu = null;
+    cursor = null;
+    unsubclass ();
+    deregister ();
+    layoutData = null;
+    if (accessible !is null) {
+        accessible.internal_dispose_Accessible ();
+    }
+    accessible = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is moved or resized.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #addControlListener
+ */
+public void removeControlListener (ControlListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Move, listener);
+    eventTable.unhook (DWT.Resize, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when a drag gesture occurs.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DragDetectListener
+ * @see #addDragDetectListener
+ *
+ * @since 3.3
+ */
+public void removeDragDetectListener(DragDetectListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.DragDetect, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control gains or loses focus.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see FocusListener
+ * @see #addFocusListener
+ */
+public void removeFocusListener(FocusListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.FocusIn, listener);
+    eventTable.unhook (DWT.FocusOut, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the help events are generated for the control.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see HelpListener
+ * @see #addHelpListener
+ */
+public void removeHelpListener (HelpListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Help, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when keys are pressed and released on the system keyboard.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see KeyListener
+ * @see #addKeyListener
+ */
+public void removeKeyListener(KeyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.KeyUp, listener);
+    eventTable.unhook (DWT.KeyDown, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the platform-specific context menu trigger has
+ * occurred.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MenuDetectListener
+ * @see #addMenuDetectListener
+ *
+ * @since 3.3
+ */
+public void removeMenuDetectListener (MenuDetectListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.MenuDetect, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the mouse passes or hovers over controls.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MouseTrackListener
+ * @see #addMouseTrackListener
+ */
+public void removeMouseTrackListener(MouseTrackListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.MouseEnter, listener);
+    eventTable.unhook (DWT.MouseExit, listener);
+    eventTable.unhook (DWT.MouseHover, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when mouse buttons are pressed and released.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MouseListener
+ * @see #addMouseListener
+ */
+public void removeMouseListener (MouseListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.MouseDown, listener);
+    eventTable.unhook (DWT.MouseUp, listener);
+    eventTable.unhook (DWT.MouseDoubleClick, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the mouse moves.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MouseMoveListener
+ * @see #addMouseMoveListener
+ */
+public void removeMouseMoveListener(MouseMoveListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.MouseMove, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the mouse wheel is scrolled.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MouseWheelListener
+ * @see #addMouseWheelListener
+ *
+ * @since 3.3
+ */
+public void removeMouseWheelListener (MouseWheelListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.MouseWheel, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the receiver needs to be painted.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see PaintListener
+ * @see #addPaintListener
+ */
+public void removePaintListener(PaintListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook(DWT.Paint, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when traversal events occur.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see TraverseListener
+ * @see #addTraverseListener
+ */
+public void removeTraverseListener(TraverseListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Traverse, listener);
+}
+
+void showWidget (bool visible) {
+    int topHandle = topHandle ();
+    OS.ShowWindow (topHandle, visible ? OS.SW_SHOW : OS.SW_HIDE);
+    if (handle !is topHandle) OS.ShowWindow (handle, visible ? OS.SW_SHOW : OS.SW_HIDE);
+}
+
+bool sendFocusEvent (int type) {
+    Shell shell = getShell ();
+
+    /*
+    * Feature in Windows.  During the processing of WM_KILLFOCUS,
+    * when the focus window is queried using GetFocus(), it has
+    * already been assigned to the new window.  The fix is to
+    * remember the control that is losing or gaining focus and
+    * answer it during WM_KILLFOCUS.  If a WM_SETFOCUS occurs
+    * during WM_KILLFOCUS, the focus control needs to be updated
+    * to the current control.  At any other time, the focus
+    * control matches Windows.
+    */
+    Display display = this.display;
+    display.focusEvent = type;
+    display.focusControl = this;
+    sendEvent (type);
+    // widget could be disposed at this point
+    display.focusEvent = DWT.None;
+    display.focusControl = null;
+
+    /*
+    * It is possible that the shell may be
+    * disposed at this point.  If this happens
+    * don't send the activate and deactivate
+    * events.
+    */
+    if (!shell.isDisposed ()) {
+        switch (type) {
+            case DWT.FocusIn:
+                shell.setActiveControl (this);
+                break;
+            case DWT.FocusOut:
+                if (shell !is display.getActiveShell ()) {
+                    shell.setActiveControl (null);
+                }
+                break;
+        }
+    }
+    return true;
+}
+
+void setBackground () {
+    Control control = findBackgroundControl ();
+    if (control is null) control = this;
+    if (control.backgroundImage !is null) {
+        Shell shell = getShell ();
+        shell.releaseBrushes ();
+        setBackgroundImage (control.backgroundImage.handle);
+    } else {
+        setBackgroundPixel (control.background is -1 ? control.defaultBackground() : control.background);
+    }
+}
+
+/**
+ * Sets the receiver's background color to the color specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.
+ * <p>
+ * Note: This operation is a hint and may be overridden by the platform.
+ * For example, on Windows the background of a Button cannot be changed.
+ * </p>
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setBackground (Color color) {
+    checkWidget ();
+    int pixel = -1;
+    if (color !is null) {
+        if (color.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        pixel = color.handle;
+    }
+    if (pixel is background) return;
+    background = pixel;
+    updateBackgroundColor ();
+}
+
+/**
+ * Sets the receiver's background image to the image specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.  The background image is tiled to fill
+ * the available space.
+ * <p>
+ * Note: This operation is a hint and may be overridden by the platform.
+ * For example, on Windows the background of a Button cannot be changed.
+ * </p>
+ * @param image the new image (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument is not a bitmap</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setBackgroundImage (Image image) {
+    checkWidget ();
+    if (image !is null) {
+        if (image.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+        if (image.type !is DWT.BITMAP) error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    if (backgroundImage is image) return;
+    backgroundImage = image;
+    Shell shell = getShell ();
+    shell.releaseBrushes ();
+    updateBackgroundImage ();
+}
+
+void setBackgroundImage (int hBitmap) {
+    if (OS.IsWinCE) {
+        OS.InvalidateRect (handle, null, true);
+    } else {
+        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
+        OS.RedrawWindow (handle, null, 0, flags);
+    }
+}
+
+void setBackgroundPixel (int pixel) {
+    if (OS.IsWinCE) {
+        OS.InvalidateRect (handle, null, true);
+    } else {
+        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
+        OS.RedrawWindow (handle, null, 0, flags);
+    }
+}
+
+/**
+ * Sets the receiver's size and location to the rectangular
+ * area specified by the arguments. The <code>x</code> and
+ * <code>y</code> arguments are relative to the receiver's
+ * parent (or its display if its parent is null), unless
+ * the receiver is a shell. In this case, the <code>x</code>
+ * and <code>y</code> arguments are relative to the display.
+ * <p>
+ * Note: Attempting to set the width or height of the
+ * receiver to a negative number will cause that
+ * value to be set to zero instead.
+ * </p>
+ *
+ * @param x the new x coordinate for the receiver
+ * @param y the new y coordinate for the receiver
+ * @param width the new width for the receiver
+ * @param height the new height for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setBounds (int x, int y, int width, int height) {
+    checkWidget ();
+    int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE;
+    setBounds (x, y, Math.max (0, width), Math.max (0, height), flags);
+}
+
+void setBounds (int x, int y, int width, int height, int flags) {
+    setBounds (x, y, width, height, flags, true);
+}
+
+void setBounds (int x, int y, int width, int height, int flags, bool defer) {
+    int topHandle = topHandle ();
+    if (defer && parent !is null) {
+        forceResize ();
+        if (OS.GetWindow (handle, OS.GW_CHILD) is 0) {
+            if (findImageControl () !is null) {
+                flags |= OS.SWP_NOCOPYBITS;
+            } else {
+                if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                    if (findThemeControl () !is null) flags |= OS.SWP_NOCOPYBITS;
+                }
+            }
+        }
+        WINDOWPOS [] lpwp = parent.lpwp;
+        if (lpwp is null) {
+            SetWindowPos (topHandle, 0, x, y, width, height, flags);
+        } else {
+            int index = 0;
+            while (index < lpwp.length) {
+                if (lpwp [index] is null) break;
+                index ++;
+            }
+            if (index is lpwp.length) {
+                WINDOWPOS [] newLpwp = new WINDOWPOS [lpwp.length + 4];
+                System.arraycopy (lpwp, 0, newLpwp, 0, lpwp.length);
+                parent.lpwp = lpwp = newLpwp;
+            }
+            WINDOWPOS wp = new WINDOWPOS ();
+            wp.hwnd = topHandle;
+            wp.x = x;
+            wp.y = y;
+            wp.cx = width;
+            wp.cy = height;
+            wp.flags = flags;
+            lpwp [index] = wp;
+        }
+    } else {
+        SetWindowPos (topHandle, 0, x, y, width, height, flags);
+    }
+}
+
+/**
+ * Sets the receiver's size and location to the rectangular
+ * area specified by the argument. The <code>x</code> and
+ * <code>y</code> fields of the rectangle are relative to
+ * the receiver's parent (or its display if its parent is null).
+ * <p>
+ * Note: Attempting to set the width or height of the
+ * receiver to a negative number will cause that
+ * value to be set to zero instead.
+ * </p>
+ *
+ * @param rect the new bounds for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setBounds (Rectangle rect) {
+    checkWidget ();
+    if (rect is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setBounds (rect.x, rect.y, rect.width, rect.height);
+}
+
+/**
+ * If the argument is <code>true</code>, causes the receiver to have
+ * all mouse events delivered to it until the method is called with
+ * <code>false</code> as the argument.
+ *
+ * @param capture <code>true</code> to capture the mouse, and <code>false</code> to release it
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setCapture (bool capture) {
+    checkWidget ();
+    if (capture) {
+        OS.SetCapture (handle);
+    } else {
+        if (OS.GetCapture () is handle) {
+            OS.ReleaseCapture ();
+        }
+    }
+}
+
+void setCursor () {
+    int lParam = OS.HTCLIENT | (OS.WM_MOUSEMOVE << 16);
+    OS.SendMessage (handle, OS.WM_SETCURSOR, handle, lParam);
+}
+
+/**
+ * Sets the receiver's cursor to the cursor specified by the
+ * argument, or to the default cursor for that kind of control
+ * if the argument is null.
+ * <p>
+ * When the mouse pointer passes over a control its appearance
+ * is changed to match the control's cursor.
+ * </p>
+ *
+ * @param cursor the new cursor (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setCursor (Cursor cursor) {
+    checkWidget ();
+    if (cursor !is null && cursor.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    this.cursor = cursor;
+    if (OS.IsWinCE) {
+        int hCursor = cursor !is null ? cursor.handle : 0;
+        OS.SetCursor (hCursor);
+        return;
+    }
+    int hwndCursor = OS.GetCapture ();
+    if (hwndCursor is 0) {
+        POINT pt = new POINT ();
+        if (!OS.GetCursorPos (pt)) return;
+        int hwnd = hwndCursor = OS.WindowFromPoint (pt);
+        while (hwnd !is 0 && hwnd !is handle) {
+            hwnd = OS.GetParent (hwnd);
+        }
+        if (hwnd is 0) return;
+    }
+    Control control = display.getControl (hwndCursor);
+    if (control is null) control = this;
+    control.setCursor ();
+}
+
+void setDefaultFont () {
+    int hFont = display.getSystemFont ().handle;
+    OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
+}
+
+/**
+ * Sets the receiver's drag detect state. If the argument is
+ * <code>true</code>, the receiver will detect drag gestures,
+ * otherwise these gestures will be ignored.
+ *
+ * @param dragDetect the new drag detect state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public void setDragDetect (bool dragDetect) {
+    checkWidget ();
+    if (dragDetect) {
+        state |= DRAG_DETECT;
+    } else {
+        state &= ~DRAG_DETECT;
+    }
+    enableDrag (dragDetect);
+}
+
+/**
+ * Enables the receiver if the argument is <code>true</code>,
+ * and disables it otherwise. A disabled control is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ *
+ * @param enabled the new enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setEnabled (bool enabled) {
+    checkWidget ();
+    /*
+    * Feature in Windows.  If the receiver has focus, disabling
+    * the receiver causes no window to have focus.  The fix is
+    * to assign focus to the first ancestor window that takes
+    * focus.  If no window will take focus, set focus to the
+    * desktop.
+    */
+    Control control = null;
+    bool fixFocus = false;
+    if (!enabled) {
+        if (display.focusEvent !is DWT.FocusOut) {
+            control = display.getFocusControl ();
+            fixFocus = isFocusAncestor (control);
+        }
+    }
+    enableWidget (enabled);
+    if (fixFocus) fixFocus (control);
+}
+
+bool setFixedFocus () {
+    if ((style & DWT.NO_FOCUS) !is 0) return false;
+    return forceFocus ();
+}
+
+/**
+ * Causes the receiver to have the <em>keyboard focus</em>,
+ * such that all keyboard events will be delivered to it.  Focus
+ * reassignment will respect applicable platform constraints.
+ *
+ * @return <code>true</code> if the control got focus, and <code>false</code> if it was unable to.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #forceFocus
+ */
+public bool setFocus () {
+    checkWidget ();
+    if ((style & DWT.NO_FOCUS) !is 0) return false;
+    return forceFocus ();
+}
+
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * to the font specified by the argument, or to the default font for that
+ * kind of control if the argument is null.
+ *
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setFont (Font font) {
+    checkWidget ();
+    int hFont = 0;
+    if (font !is null) {
+        if (font.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        hFont = font.handle;
+    }
+    if (hFont is 0) hFont = defaultFont ();
+    OS.SendMessage (handle, OS.WM_SETFONT, hFont, 1);
+}
+
+/**
+ * Sets the receiver's foreground color to the color specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.
+ * <p>
+ * Note: This operation is a hint and may be overridden by the platform.
+ * </p>
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setForeground (Color color) {
+    checkWidget ();
+    int pixel = -1;
+    if (color !is null) {
+        if (color.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        pixel = color.handle;
+    }
+    if (pixel is foreground) return;
+    foreground = pixel;
+    setForegroundPixel (pixel);
+}
+
+void setForegroundPixel (int pixel) {
+    OS.InvalidateRect (handle, null, true);
+}
+
+/**
+ * Sets the layout data associated with the receiver to the argument.
+ *
+ * @param layoutData the new layout data for the receiver.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLayoutData (Object layoutData) {
+    checkWidget ();
+    this.layoutData = layoutData;
+}
+
+/**
+ * Sets the receiver's location to the point specified by
+ * the arguments which are relative to the receiver's
+ * parent (or its display if its parent is null), unless
+ * the receiver is a shell. In this case, the point is
+ * relative to the display.
+ *
+ * @param x the new x coordinate for the receiver
+ * @param y the new y coordinate for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLocation (int x, int y) {
+    checkWidget ();
+    int flags = OS.SWP_NOSIZE | OS.SWP_NOZORDER | OS.SWP_NOACTIVATE;
+    /*
+    * Feature in WinCE.  The SWP_DRAWFRAME flag for SetWindowPos()
+    * causes a WM_SIZE message to be sent even when the SWP_NOSIZE
+    * flag is specified.  The fix is to set SWP_DRAWFRAME only when
+    * not running on WinCE.
+    */
+    if (!OS.IsWinCE) flags |= OS.SWP_DRAWFRAME;
+    setBounds (x, y, 0, 0, flags);
+}
+
+/**
+ * Sets the receiver's location to the point specified by
+ * the arguments which are relative to the receiver's
+ * parent (or its display if its parent is null), unless
+ * the receiver is a shell. In this case, the point is
+ * relative to the display.
+ *
+ * @param location the new location for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLocation (Point location) {
+    checkWidget ();
+    if (location is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setLocation (location.x, location.y);
+}
+
+/**
+ * Sets the receiver's pop up menu to the argument.
+ * All controls may optionally have a pop up
+ * menu that is displayed when the user requests one for
+ * the control. The sequence of key strokes, button presses
+ * and/or button releases that are used to request a pop up
+ * menu is platform specific.
+ * <p>
+ * Note: Disposing of a control that has a pop up menu will
+ * dispose of the menu.  To avoid this behavior, set the
+ * menu to null before the control is disposed.
+ * </p>
+ *
+ * @param menu the new pop up menu
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_MENU_NOT_POP_UP - the menu is not a pop up menu</li>
+ *    <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMenu (Menu menu) {
+    checkWidget ();
+    if (menu !is null) {
+        if (menu.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        if ((menu.style & DWT.POP_UP) is 0) {
+            error (DWT.ERROR_MENU_NOT_POP_UP);
+        }
+        if (menu.parent !is menuShell ()) {
+            error (DWT.ERROR_INVALID_PARENT);
+        }
+    }
+    this.menu = menu;
+}
+
+bool setRadioFocus () {
+    return false;
+}
+
+bool setRadioSelection (bool value) {
+    return false;
+}
+
+/**
+ * If the argument is <code>false</code>, causes subsequent drawing
+ * operations in the receiver to be ignored. No drawing of any kind
+ * can occur in the receiver until the flag is set to true.
+ * Graphics operations that occurred while the flag was
+ * <code>false</code> are lost. When the flag is set to <code>true</code>,
+ * the entire widget is marked as needing to be redrawn.  Nested calls
+ * to this method are stacked.
+ * <p>
+ * Note: This operation is a hint and may not be supported on some
+ * platforms or for some widgets.
+ * </p>
+ *
+ * @param redraw the new redraw state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #redraw(int, int, int, int, bool)
+ * @see #update()
+ */
+public void setRedraw (bool redraw) {
+    checkWidget ();
+    /*
+     * Feature in Windows.  When WM_SETREDRAW is used to turn
+     * off drawing in a widget, it clears the WS_VISIBLE bits
+     * and then sets them when redraw is turned back on.  This
+     * means that WM_SETREDRAW will make a widget unexpectedly
+     * visible.  The fix is to track the visibility state while
+     * drawing is turned off and restore it when drawing is
+     * turned back on.
+     */
+    if (drawCount is 0) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if ((bits & OS.WS_VISIBLE) is 0) state |= HIDDEN;
+    }
+    if (redraw) {
+        if (--drawCount is 0) {
+            int topHandle = topHandle ();
+            OS.SendMessage (topHandle, OS.WM_SETREDRAW, 1, 0);
+            if (handle !is topHandle) OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+            if ((state & HIDDEN) !is 0) {
+                state &= ~HIDDEN;
+                OS.ShowWindow (topHandle, OS.SW_HIDE);
+                if (handle !is topHandle) OS.ShowWindow (handle, OS.SW_HIDE);
+            } else {
+                if (OS.IsWinCE) {
+                    OS.InvalidateRect (topHandle, null, true);
+                    if (handle !is topHandle) OS.InvalidateRect (handle, null, true);
+                } else {
+                    int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+                    OS.RedrawWindow (topHandle, null, 0, flags);
+                }
+            }
+        }
+    } else {
+        if (drawCount++ is 0) {
+            int topHandle = topHandle ();
+            OS.SendMessage (topHandle, OS.WM_SETREDRAW, 0, 0);
+            if (handle !is topHandle) OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+        }
+    }
+}
+
+bool setSavedFocus () {
+    return forceFocus ();
+}
+
+/**
+ * Sets the receiver's size to the point specified by the arguments.
+ * <p>
+ * Note: Attempting to set the width or height of the
+ * receiver to a negative number will cause that
+ * value to be set to zero instead.
+ * </p>
+ *
+ * @param width the new width for the receiver
+ * @param height the new height for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSize (int width, int height) {
+    checkWidget ();
+    int flags = OS.SWP_NOMOVE | OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE;
+    setBounds (0, 0, Math.max (0, width), Math.max (0, height), flags);
+}
+
+/**
+ * Sets the receiver's size to the point specified by the argument.
+ * <p>
+ * Note: Attempting to set the width or height of the
+ * receiver to a negative number will cause them to be
+ * set to zero instead.
+ * </p>
+ *
+ * @param size the new size for the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSize (Point size) {
+    checkWidget ();
+    if (size is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setSize (size.x, size.y);
+}
+
+bool setTabGroupFocus () {
+    return setTabItemFocus ();
+}
+
+bool setTabItemFocus () {
+    if (!isShowing ()) return false;
+    return forceFocus ();
+}
+
+/**
+ * Sets the receiver's tool tip text to the argument, which
+ * may be null indicating that no tool tip text should be shown.
+ *
+ * @param string the new tool tip text (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setToolTipText (String string) {
+    checkWidget ();
+    toolTipText = string;
+    setToolTipText (getShell (), string);
+}
+
+void setToolTipText (Shell shell, String string) {
+    shell.setToolTipText (handle, string);
+}
+
+/**
+ * Marks the receiver as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param visible the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setVisible (bool visible) {
+    checkWidget ();
+    if (drawCount !is 0) {
+        if (((state & HIDDEN) is 0) is visible) return;
+    } else {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if (((bits & OS.WS_VISIBLE) !is 0) is visible) return;
+    }
+    if (visible) {
+        sendEvent (DWT.Show);
+        if (isDisposed ()) return;
+    }
+
+    /*
+    * Feature in Windows.  If the receiver has focus, hiding
+    * the receiver causes no window to have focus.  The fix is
+    * to assign focus to the first ancestor window that takes
+    * focus.  If no window will take focus, set focus to the
+    * desktop.
+    */
+    Control control = null;
+    bool fixFocus = false;
+    if (!visible) {
+        if (display.focusEvent !is DWT.FocusOut) {
+            control = display.getFocusControl ();
+            fixFocus = isFocusAncestor (control);
+        }
+    }
+    if (drawCount !is 0) {
+        state = visible ? state & ~HIDDEN : state | HIDDEN;
+    } else {
+        showWidget (visible);
+        if (isDisposed ()) return;
+    }
+    if (!visible) {
+        sendEvent (DWT.Hide);
+        if (isDisposed ()) return;
+    }
+    if (fixFocus) fixFocus (control);
+}
+
+void sort (int [] items) {
+    /* Shell Sort from K&R, pg 108 */
+    int length = items.length;
+    for (int gap=length/2; gap>0; gap/=2) {
+        for (int i=gap; i<length; i++) {
+            for (int j=i-gap; j>=0; j-=gap) {
+                if (items [j] <= items [j + gap]) {
+                    int swap = items [j];
+                    items [j] = items [j + gap];
+                    items [j + gap] = swap;
+                }
+            }
+        }
+    }
+}
+
+void subclass () {
+    int oldProc = windowProc ();
+    int newProc = display.windowProc;
+    if (oldProc is newProc) return;
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, newProc);
+}
+
+/**
+ * Returns a point which is the result of converting the
+ * argument, which is specified in display relative coordinates,
+ * to coordinates relative to the receiver.
+ * <p>
+ * @param x the x coordinate to be translated
+ * @param y the y coordinate to be translated
+ * @return the translated coordinates
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public Point toControl (int x, int y) {
+    checkWidget ();
+    POINT pt = new POINT ();
+    pt.x = x;  pt.y = y;
+    OS.ScreenToClient (handle, pt);
+    return new Point (pt.x, pt.y);
+}
+
+/**
+ * Returns a point which is the result of converting the
+ * argument, which is specified in display relative coordinates,
+ * to coordinates relative to the receiver.
+ * <p>
+ * @param point the point to be translated (must not be null)
+ * @return the translated coordinates
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point toControl (Point point) {
+    checkWidget ();
+    if (point is null) error (DWT.ERROR_NULL_ARGUMENT);
+    return toControl (point.x, point.y);
+}
+
+/**
+ * Returns a point which is the result of converting the
+ * argument, which is specified in coordinates relative to
+ * the receiver, to display relative coordinates.
+ * <p>
+ * @param x the x coordinate to be translated
+ * @param y the y coordinate to be translated
+ * @return the translated coordinates
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public Point toDisplay (int x, int y) {
+    checkWidget ();
+    POINT pt = new POINT ();
+    pt.x = x;  pt.y = y;
+    OS.ClientToScreen (handle, pt);
+    return new Point (pt.x, pt.y);
+}
+
+/**
+ * Returns a point which is the result of converting the
+ * argument, which is specified in coordinates relative to
+ * the receiver, to display relative coordinates.
+ * <p>
+ * @param point the point to be translated (must not be null)
+ * @return the translated coordinates
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point toDisplay (Point point) {
+    checkWidget ();
+    if (point is null) error (DWT.ERROR_NULL_ARGUMENT);
+    return toDisplay (point.x, point.y);
+}
+
+int topHandle () {
+    return handle;
+}
+
+bool translateAccelerator (MSG msg) {
+    return menuShell ().translateAccelerator (msg);
+}
+
+bool translateMnemonic (Event event, Control control) {
+    if (control is this) return false;
+    if (!isVisible () || !isEnabled ()) return false;
+    event.doit = mnemonicMatch (event.character);
+    return traverse (event);
+}
+
+bool translateMnemonic (MSG msg) {
+    if (msg.wParam < 0x20) return false;
+    int hwnd = msg.hwnd;
+    if (OS.GetKeyState (OS.VK_MENU) >= 0) {
+        int code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0);
+        if ((code & OS.DLGC_WANTALLKEYS) !is 0) return false;
+        if ((code & OS.DLGC_BUTTON) is 0) return false;
+    }
+    Decorations shell = menuShell ();
+    if (shell.isVisible () && shell.isEnabled ()) {
+        display.lastAscii = msg.wParam;
+        display.lastNull = display.lastDead = false;
+        Event event = new Event ();
+        event.detail = DWT.TRAVERSE_MNEMONIC;
+        if (setKeyState (event, DWT.Traverse, msg.wParam, msg.lParam)) {
+            return translateMnemonic (event, null) || shell.translateMnemonic (event, this);
+        }
+    }
+    return false;
+}
+
+bool translateTraversal (MSG msg) {
+    int hwnd = msg.hwnd;
+    int key = msg.wParam;
+    if (key is OS.VK_MENU) {
+        OS.SendMessage (hwnd, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
+        return false;
+    }
+    int detail = DWT.TRAVERSE_NONE;
+    bool doit = true, all = false;
+    bool lastVirtual = false;
+    int lastKey = key, lastAscii = 0;
+    switch (key) {
+        case OS.VK_ESCAPE: {
+            all = true;
+            lastAscii = 27;
+            int code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0);
+            if ((code & OS.DLGC_WANTALLKEYS) !is 0) {
+                /*
+                * Use DLGC_HASSETSEL to determine that the control
+                * is a text widget.  A text widget normally wants
+                * all keys except VK_ESCAPE.  If this bit is not
+                * set, then assume the control wants all keys,
+                * including VK_ESCAPE.
+                */
+                if ((code & OS.DLGC_HASSETSEL) is 0) doit = false;
+            }
+            detail = DWT.TRAVERSE_ESCAPE;
+            break;
+        }
+        case OS.VK_RETURN: {
+            all = true;
+            lastAscii = '\r';
+            int code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0);
+            if ((code & OS.DLGC_WANTALLKEYS) !is 0) doit = false;
+            detail = DWT.TRAVERSE_RETURN;
+            break;
+        }
+        case OS.VK_TAB: {
+            lastAscii = '\t';
+            bool next = OS.GetKeyState (OS.VK_SHIFT) >= 0;
+            int code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0);
+            if ((code & (OS.DLGC_WANTTAB | OS.DLGC_WANTALLKEYS)) !is 0) {
+                /*
+                * Use DLGC_HASSETSEL to determine that the control is a
+                * text widget.  If the control is a text widget, then
+                * Ctrl+Tab and Shift+Tab should traverse out of the widget.
+                * If the control is not a text widget, the correct behavior
+                * is to give every character, including Tab, Ctrl+Tab and
+                * Shift+Tab to the control.
+                */
+                if ((code & OS.DLGC_HASSETSEL) !is 0) {
+                    if (next && OS.GetKeyState (OS.VK_CONTROL) >= 0) {
+                        doit = false;
+                    }
+                } else {
+                    doit = false;
+                }
+            }
+            if (parent !is null && (parent.style & DWT.MIRRORED) !is 0) {
+                if (key is OS.VK_LEFT || key is OS.VK_RIGHT) next = !next;
+            }
+            detail = next ? DWT.TRAVERSE_TAB_NEXT : DWT.TRAVERSE_TAB_PREVIOUS;
+            break;
+        }
+        case OS.VK_UP:
+        case OS.VK_LEFT:
+        case OS.VK_DOWN:
+        case OS.VK_RIGHT: {
+            /*
+            * On WinCE SP there is no tab key.  Focus is assigned
+            * using the VK_UP and VK_DOWN keys, not with VK_LEFT
+            * or VK_RIGHT.
+            */
+            if (OS.IsSP) {
+                if (key is OS.VK_LEFT || key is OS.VK_RIGHT) return false;
+            }
+            lastVirtual = true;
+            int code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0);
+            if ((code & (OS.DLGC_WANTARROWS /*| OS.DLGC_WANTALLKEYS*/)) !is 0) doit = false;
+            bool next = key is OS.VK_DOWN || key is OS.VK_RIGHT;
+            detail = next ? DWT.TRAVERSE_ARROW_NEXT : DWT.TRAVERSE_ARROW_PREVIOUS;
+            break;
+        }
+        case OS.VK_PRIOR:
+        case OS.VK_NEXT: {
+            all = true;
+            lastVirtual = true;
+            if (OS.GetKeyState (OS.VK_CONTROL) >= 0) return false;
+            int code = OS.SendMessage (hwnd, OS.WM_GETDLGCODE, 0, 0);
+            if ((code & OS.DLGC_WANTALLKEYS) !is 0) {
+                /*
+                * Use DLGC_HASSETSEL to determine that the control is a
+                * text widget.  If the control is a text widget, then
+                * Ctrl+PgUp and Ctrl+PgDn should traverse out of the widget.
+                */
+                if ((code & OS.DLGC_HASSETSEL) is 0) doit = false;
+            }
+            detail = key is OS.VK_PRIOR ? DWT.TRAVERSE_PAGE_PREVIOUS : DWT.TRAVERSE_PAGE_NEXT;
+            break;
+        }
+        default:
+            return false;
+    }
+    Event event = new Event ();
+    event.doit = doit;
+    event.detail = detail;
+    display.lastKey = lastKey;
+    display.lastAscii = lastAscii;
+    display.lastVirtual = lastVirtual;
+    display.lastNull = display.lastDead = false;
+    if (!setKeyState (event, DWT.Traverse, msg.wParam, msg.lParam)) return false;
+    Shell shell = getShell ();
+    Control control = this;
+    do {
+        if (control.traverse (event)) {
+            OS.SendMessage (hwnd, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
+            return true;
+        }
+        if (!event.doit && control.hooks (DWT.Traverse)) return false;
+        if (control is shell) return false;
+        control = control.parent;
+    } while (all && control !is null);
+    return false;
+}
+
+bool traverse (Event event) {
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in the traverse
+    * event.  If this happens, return true to stop further
+    * event processing.
+    */
+    sendEvent (DWT.Traverse, event);
+    if (isDisposed ()) return true;
+    if (!event.doit) return false;
+    switch (event.detail) {
+        case DWT.TRAVERSE_NONE:         return true;
+        case DWT.TRAVERSE_ESCAPE:           return traverseEscape ();
+        case DWT.TRAVERSE_RETURN:           return traverseReturn ();
+        case DWT.TRAVERSE_TAB_NEXT:     return traverseGroup (true);
+        case DWT.TRAVERSE_TAB_PREVIOUS: return traverseGroup (false);
+        case DWT.TRAVERSE_ARROW_NEXT:       return traverseItem (true);
+        case DWT.TRAVERSE_ARROW_PREVIOUS:   return traverseItem (false);
+        case DWT.TRAVERSE_MNEMONIC:     return traverseMnemonic (event.character);
+        case DWT.TRAVERSE_PAGE_NEXT:        return traversePage (true);
+        case DWT.TRAVERSE_PAGE_PREVIOUS:    return traversePage (false);
+    }
+    return false;
+}
+
+/**
+ * Based on the argument, perform one of the expected platform
+ * traversal action. The argument should be one of the constants:
+ * <code>DWT.TRAVERSE_ESCAPE</code>, <code>DWT.TRAVERSE_RETURN</code>,
+ * <code>DWT.TRAVERSE_TAB_NEXT</code>, <code>DWT.TRAVERSE_TAB_PREVIOUS</code>,
+ * <code>DWT.TRAVERSE_ARROW_NEXT</code> and <code>DWT.TRAVERSE_ARROW_PREVIOUS</code>.
+ *
+ * @param traversal the type of traversal
+ * @return true if the traversal succeeded
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool traverse (int traversal) {
+    checkWidget ();
+    Event event = new Event ();
+    event.doit = true;
+    event.detail = traversal;
+    return traverse (event);
+}
+
+bool traverseEscape () {
+    return false;
+}
+
+bool traverseGroup (bool next) {
+    Control root = computeTabRoot ();
+    Control group = computeTabGroup ();
+    Control [] list = root.computeTabList ();
+    int length = list.length;
+    int index = 0;
+    while (index < length) {
+        if (list [index] is group) break;
+        index++;
+    }
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in focus in
+    * or out events.  Ensure that a disposed widget is
+    * not accessed.
+    */
+    if (index is length) return false;
+    int start = index, offset = (next) ? 1 : -1;
+    while ((index = ((index + offset + length) % length)) !is start) {
+        Control control = list [index];
+        if (!control.isDisposed () && control.setTabGroupFocus ()) {
+            return true;
+        }
+    }
+    if (group.isDisposed ()) return false;
+    return group.setTabGroupFocus ();
+}
+
+bool traverseItem (bool next) {
+    Control [] children = parent._getChildren ();
+    int length = children.length;
+    int index = 0;
+    while (index < length) {
+        if (children [index] is this) break;
+        index++;
+    }
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in focus in
+    * or out events.  Ensure that a disposed widget is
+    * not accessed.
+    */
+    if (index is length) return false;
+    int start = index, offset = (next) ? 1 : -1;
+    while ((index = (index + offset + length) % length) !is start) {
+        Control child = children [index];
+        if (!child.isDisposed () && child.isTabItem ()) {
+            if (child.setTabItemFocus ()) return true;
+        }
+    }
+    return false;
+}
+
+bool traverseMnemonic (char key) {
+    if (mnemonicHit (key)) {
+        OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
+        return true;
+    }
+    return false;
+}
+
+bool traversePage (bool next) {
+    return false;
+}
+
+bool traverseReturn () {
+    return false;
+}
+
+void unsubclass () {
+    int newProc = windowProc ();
+    int oldProc = display.windowProc;
+    if (oldProc is newProc) return;
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, newProc);
+}
+
+/**
+ * Forces all outstanding paint requests for the widget
+ * to be processed before this method returns. If there
+ * are no outstanding paint request, this method does
+ * nothing.
+ * <p>
+ * Note: This method does not cause a redraw.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #redraw()
+ * @see #redraw(int, int, int, int, bool)
+ * @see PaintListener
+ * @see DWT#Paint
+ */
+public void update () {
+    checkWidget ();
+    update (false);
+}
+
+void update (bool all) {
+//  checkWidget ();
+    if (OS.IsWinCE) {
+        OS.UpdateWindow (handle);
+    } else {
+        int flags = OS.RDW_UPDATENOW;
+        if (all) flags |= OS.RDW_ALLCHILDREN;
+        OS.RedrawWindow (handle, null, 0, flags);
+    }
+}
+
+void updateBackgroundColor () {
+    Control control = findBackgroundControl ();
+    if (control is null) control = this;
+    setBackgroundPixel (control.background);
+}
+
+void updateBackgroundImage () {
+    Control control = findBackgroundControl ();
+    Image image = control !is null ? control.backgroundImage : backgroundImage;
+    setBackgroundImage (image !is null ? image.handle : 0);
+}
+
+void updateBackgroundMode () {
+    int oldState = state & PARENT_BACKGROUND;
+    checkBackground ();
+    if (oldState !is (state & PARENT_BACKGROUND)) {
+        setBackground ();
+    }
+}
+
+void updateFont (Font oldFont, Font newFont) {
+    if (getFont ().equals (oldFont)) setFont (newFont);
+}
+
+void updateImages () {
+    /* Do nothing */
+}
+
+void updateLayout (bool resize, bool all) {
+    /* Do nothing */
+}
+
+CREATESTRUCT widgetCreateStruct () {
+    return null;
+}
+
+int widgetExtStyle () {
+    int bits = 0;
+    if (!OS.IsPPC) {
+        if ((style & DWT.BORDER) !is 0) bits |= OS.WS_EX_CLIENTEDGE;
+    }
+//  if ((style & DWT.BORDER) !is 0) {
+//      if ((style & DWT.FLAT) is 0) bits |= OS.WS_EX_CLIENTEDGE;
+//  }
+    /*
+    * Feature in Windows NT.  When CreateWindowEx() is called with
+    * WS_EX_LAYOUTRTL or WS_EX_NOINHERITLAYOUT, CreateWindowEx()
+    * fails to create the HWND. The fix is to not use these bits.
+    */
+    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) {
+        return bits;
+    }
+    bits |= OS.WS_EX_NOINHERITLAYOUT;
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) bits |= OS.WS_EX_LAYOUTRTL;
+    return bits;
+}
+
+int widgetParent () {
+    return parent.handle;
+}
+
+int widgetStyle () {
+    /* Force clipping of siblings by setting WS_CLIPSIBLINGS */
+    int bits = OS.WS_CHILD | OS.WS_VISIBLE | OS.WS_CLIPSIBLINGS;
+//  if ((style & DWT.BORDER) !is 0) {
+//      if ((style & DWT.FLAT) !is 0) bits |= OS.WS_BORDER;
+//  }
+    if (OS.IsPPC) {
+        if ((style & DWT.BORDER) !is 0) bits |= OS.WS_BORDER;
+    }
+    return bits;
+
+    /*
+    * This code is intentionally commented.  When clipping
+    * of both siblings and children is not enforced, it is
+    * possible for application code to draw outside of the
+    * control.
+    */
+//  int bits = OS.WS_CHILD | OS.WS_VISIBLE;
+//  if ((style & DWT.CLIP_SIBLINGS) !is 0) bits |= OS.WS_CLIPSIBLINGS;
+//  if ((style & DWT.CLIP_CHILDREN) !is 0) bits |= OS.WS_CLIPCHILDREN;
+//  return bits;
+}
+
+/**
+ * Changes the parent of the widget to be the one provided if
+ * the underlying operating system supports this feature.
+ * Returns <code>true</code> if the parent is successfully changed.
+ *
+ * @param parent the new parent for the control.
+ * @return <code>true</code> if the parent is changed and <code>false</code> otherwise.
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is <code>null</code></li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *  </ul>
+ */
+public bool setParent (Composite parent) {
+    checkWidget ();
+    if (parent is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (parent.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+    if (this.parent is parent) return true;
+    if (!isReparentable ()) return false;
+    releaseParent ();
+    Shell newShell = parent.getShell (), oldShell = getShell ();
+    Decorations newDecorations = parent.menuShell (), oldDecorations = menuShell ();
+    if (oldShell !is newShell || oldDecorations !is newDecorations) {
+        Menu [] menus = oldShell.findMenus (this);
+        fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus);
+    }
+    int topHandle = topHandle ();
+    if (OS.SetParent (topHandle, parent.handle) is 0) return false;
+    this.parent = parent;
+    int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
+    SetWindowPos (topHandle, OS.HWND_BOTTOM, 0, 0, 0, 0, flags);
+    return true;
+}
+
+abstract TCHAR windowClass ();
+
+abstract int windowProc ();
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    LRESULT result = null;
+    switch (msg) {
+        case OS.WM_ACTIVATE:            result = WM_ACTIVATE (wParam, lParam); break;
+        case OS.WM_CAPTURECHANGED:      result = WM_CAPTURECHANGED (wParam, lParam); break;
+        case OS.WM_CHAR:                result = WM_CHAR (wParam, lParam); break;
+        case OS.WM_CLEAR:               result = WM_CLEAR (wParam, lParam); break;
+        case OS.WM_CLOSE:               result = WM_CLOSE (wParam, lParam); break;
+        case OS.WM_COMMAND:             result = WM_COMMAND (wParam, lParam); break;
+        case OS.WM_CONTEXTMENU:         result = WM_CONTEXTMENU (wParam, lParam); break;
+        case OS.WM_CTLCOLORBTN:
+        case OS.WM_CTLCOLORDLG:
+        case OS.WM_CTLCOLOREDIT:
+        case OS.WM_CTLCOLORLISTBOX:
+        case OS.WM_CTLCOLORMSGBOX:
+        case OS.WM_CTLCOLORSCROLLBAR:
+        case OS.WM_CTLCOLORSTATIC:      result = WM_CTLCOLOR (wParam, lParam); break;
+        case OS.WM_CUT:                 result = WM_CUT (wParam, lParam); break;
+        case OS.WM_DESTROY:             result = WM_DESTROY (wParam, lParam); break;
+        case OS.WM_DRAWITEM:            result = WM_DRAWITEM (wParam, lParam); break;
+        case OS.WM_ENDSESSION:          result = WM_ENDSESSION (wParam, lParam); break;
+        case OS.WM_ENTERIDLE:           result = WM_ENTERIDLE (wParam, lParam); break;
+        case OS.WM_ERASEBKGND:          result = WM_ERASEBKGND (wParam, lParam); break;
+        case OS.WM_GETDLGCODE:          result = WM_GETDLGCODE (wParam, lParam); break;
+        case OS.WM_GETFONT:             result = WM_GETFONT (wParam, lParam); break;
+        case OS.WM_GETOBJECT:           result = WM_GETOBJECT (wParam, lParam); break;
+        case OS.WM_GETMINMAXINFO:       result = WM_GETMINMAXINFO (wParam, lParam); break;
+        case OS.WM_HELP:                result = WM_HELP (wParam, lParam); break;
+        case OS.WM_HSCROLL:             result = WM_HSCROLL (wParam, lParam); break;
+        case OS.WM_IME_CHAR:            result = WM_IME_CHAR (wParam, lParam); break;
+        case OS.WM_IME_COMPOSITION:     result = WM_IME_COMPOSITION (wParam, lParam); break;
+        case OS.WM_INITMENUPOPUP:       result = WM_INITMENUPOPUP (wParam, lParam); break;
+        case OS.WM_INPUTLANGCHANGE:     result = WM_INPUTLANGCHANGE (wParam, lParam); break;
+        case OS.WM_HOTKEY:              result = WM_HOTKEY (wParam, lParam); break;
+        case OS.WM_KEYDOWN:             result = WM_KEYDOWN (wParam, lParam); break;
+        case OS.WM_KEYUP:               result = WM_KEYUP (wParam, lParam); break;
+        case OS.WM_KILLFOCUS:           result = WM_KILLFOCUS (wParam, lParam); break;
+        case OS.WM_LBUTTONDBLCLK:       result = WM_LBUTTONDBLCLK (wParam, lParam); break;
+        case OS.WM_LBUTTONDOWN:         result = WM_LBUTTONDOWN (wParam, lParam); break;
+        case OS.WM_LBUTTONUP:           result = WM_LBUTTONUP (wParam, lParam); break;
+        case OS.WM_MBUTTONDBLCLK:       result = WM_MBUTTONDBLCLK (wParam, lParam); break;
+        case OS.WM_MBUTTONDOWN:         result = WM_MBUTTONDOWN (wParam, lParam); break;
+        case OS.WM_MBUTTONUP:           result = WM_MBUTTONUP (wParam, lParam); break;
+        case OS.WM_MEASUREITEM:         result = WM_MEASUREITEM (wParam, lParam); break;
+        case OS.WM_MENUCHAR:            result = WM_MENUCHAR (wParam, lParam); break;
+        case OS.WM_MENUSELECT:          result = WM_MENUSELECT (wParam, lParam); break;
+        case OS.WM_MOUSEACTIVATE:       result = WM_MOUSEACTIVATE (wParam, lParam); break;
+        case OS.WM_MOUSEHOVER:          result = WM_MOUSEHOVER (wParam, lParam); break;
+        case OS.WM_MOUSELEAVE:          result = WM_MOUSELEAVE (wParam, lParam); break;
+        case OS.WM_MOUSEMOVE:           result = WM_MOUSEMOVE (wParam, lParam); break;
+        case OS.WM_MOUSEWHEEL:          result = WM_MOUSEWHEEL (wParam, lParam); break;
+        case OS.WM_MOVE:                result = WM_MOVE (wParam, lParam); break;
+        case OS.WM_NCACTIVATE:          result = WM_NCACTIVATE (wParam, lParam); break;
+        case OS.WM_NCCALCSIZE:          result = WM_NCCALCSIZE (wParam, lParam); break;
+        case OS.WM_NCHITTEST:           result = WM_NCHITTEST (wParam, lParam); break;
+        case OS.WM_NCLBUTTONDOWN:       result = WM_NCLBUTTONDOWN (wParam, lParam); break;
+        case OS.WM_NCPAINT:             result = WM_NCPAINT (wParam, lParam); break;
+        case OS.WM_NOTIFY:              result = WM_NOTIFY (wParam, lParam); break;
+        case OS.WM_PAINT:               result = WM_PAINT (wParam, lParam); break;
+        case OS.WM_PALETTECHANGED:      result = WM_PALETTECHANGED (wParam, lParam); break;
+        case OS.WM_PARENTNOTIFY:        result = WM_PARENTNOTIFY (wParam, lParam); break;
+        case OS.WM_PASTE:               result = WM_PASTE (wParam, lParam); break;
+        case OS.WM_PRINT:               result = WM_PRINT (wParam, lParam); break;
+        case OS.WM_PRINTCLIENT:         result = WM_PRINTCLIENT (wParam, lParam); break;
+        case OS.WM_QUERYENDSESSION:     result = WM_QUERYENDSESSION (wParam, lParam); break;
+        case OS.WM_QUERYNEWPALETTE:     result = WM_QUERYNEWPALETTE (wParam, lParam); break;
+        case OS.WM_QUERYOPEN:           result = WM_QUERYOPEN (wParam, lParam); break;
+        case OS.WM_RBUTTONDBLCLK:       result = WM_RBUTTONDBLCLK (wParam, lParam); break;
+        case OS.WM_RBUTTONDOWN:         result = WM_RBUTTONDOWN (wParam, lParam); break;
+        case OS.WM_RBUTTONUP:           result = WM_RBUTTONUP (wParam, lParam); break;
+        case OS.WM_SETCURSOR:           result = WM_SETCURSOR (wParam, lParam); break;
+        case OS.WM_SETFOCUS:            result = WM_SETFOCUS (wParam, lParam); break;
+        case OS.WM_SETFONT:             result = WM_SETFONT (wParam, lParam); break;
+        case OS.WM_SETTINGCHANGE:       result = WM_SETTINGCHANGE (wParam, lParam); break;
+        case OS.WM_SETREDRAW:           result = WM_SETREDRAW (wParam, lParam); break;
+        case OS.WM_SHOWWINDOW:          result = WM_SHOWWINDOW (wParam, lParam); break;
+        case OS.WM_SIZE:                result = WM_SIZE (wParam, lParam); break;
+        case OS.WM_SYSCHAR:             result = WM_SYSCHAR (wParam, lParam); break;
+        case OS.WM_SYSCOLORCHANGE:      result = WM_SYSCOLORCHANGE (wParam, lParam); break;
+        case OS.WM_SYSCOMMAND:          result = WM_SYSCOMMAND (wParam, lParam); break;
+        case OS.WM_SYSKEYDOWN:          result = WM_SYSKEYDOWN (wParam, lParam); break;
+        case OS.WM_SYSKEYUP:            result = WM_SYSKEYUP (wParam, lParam); break;
+        case OS.WM_TIMER:               result = WM_TIMER (wParam, lParam); break;
+        case OS.WM_UNDO:                result = WM_UNDO (wParam, lParam); break;
+        case OS.WM_UPDATEUISTATE:       result = WM_UPDATEUISTATE (wParam, lParam); break;
+        case OS.WM_VSCROLL:             result = WM_VSCROLL (wParam, lParam); break;
+        case OS.WM_WINDOWPOSCHANGED:    result = WM_WINDOWPOSCHANGED (wParam, lParam); break;
+        case OS.WM_WINDOWPOSCHANGING:   result = WM_WINDOWPOSCHANGING (wParam, lParam); break;
+        case OS.WM_XBUTTONDBLCLK:       result = WM_XBUTTONDBLCLK (wParam, lParam); break;
+        case OS.WM_XBUTTONDOWN:         result = WM_XBUTTONDOWN (wParam, lParam); break;
+        case OS.WM_XBUTTONUP:           result = WM_XBUTTONUP (wParam, lParam); break;
+    }
+    if (result !is null) return result.value;
+    return callWindowProc (hwnd, msg, wParam, lParam);
+}
+
+LRESULT WM_ACTIVATE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_CAPTURECHANGED (int wParam, int lParam) {
+    return wmCaptureChanged (handle, wParam, lParam);
+}
+
+LRESULT WM_CHAR (int wParam, int lParam) {
+    return wmChar (handle, wParam, lParam);
+}
+
+LRESULT WM_CLEAR (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_CLOSE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_COMMAND (int wParam, int lParam) {
+    /*
+    * When the WM_COMMAND message is sent from a
+    * menu, the HWND parameter in LPARAM is zero.
+    */
+    if (lParam is 0) {
+        Decorations shell = menuShell ();
+        if (shell.isEnabled ()) {
+            int id = wParam & 0xFFFF;
+            MenuItem item = display.getMenuItem (id);
+            if (item !is null && item.isEnabled ()) {
+                return item.wmCommandChild (wParam, lParam);
+            }
+        }
+        return null;
+    }
+    Control control = display.getControl (lParam);
+    if (control is null) return null;
+    return control.wmCommandChild (wParam, lParam);
+}
+
+LRESULT WM_CONTEXTMENU (int wParam, int lParam) {
+    return wmContextMenu (handle, wParam, lParam);
+}
+
+LRESULT WM_CTLCOLOR (int wParam, int lParam) {
+    int hPalette = display.hPalette;
+    if (hPalette !is 0) {
+        OS.SelectPalette (wParam, hPalette, false);
+        OS.RealizePalette (wParam);
+    }
+    Control control = display.getControl (lParam);
+    if (control is null) return null;
+    return control.wmColorChild (wParam, lParam);
+}
+
+LRESULT WM_CUT (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_DESTROY (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_DRAWITEM (int wParam, int lParam) {
+    DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
+    OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof);
+    if (struct.CtlType is OS.ODT_MENU) {
+        MenuItem item = display.getMenuItem (struct.itemID);
+        if (item is null) return null;
+        return item.wmDrawChild (wParam, lParam);
+    }
+    Control control = display.getControl (struct.hwndItem);
+    if (control is null) return null;
+    return control.wmDrawChild (wParam, lParam);
+}
+
+LRESULT WM_ENDSESSION (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_ENTERIDLE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    if ((state & DRAW_BACKGROUND) !is 0) {
+        if (findImageControl () !is null) return LRESULT.ONE;
+    }
+    if ((state & THEME_BACKGROUND) !is 0) {
+        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+            if (findThemeControl () !is null) return LRESULT.ONE;
+        }
+    }
+    return null;
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_GETFONT (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_GETOBJECT (int wParam, int lParam) {
+    if (accessible !is null) {
+        int result = accessible.internal_WM_GETOBJECT (wParam, lParam);
+        if (result !is 0) return new LRESULT (result);
+    }
+    return null;
+}
+
+LRESULT WM_GETMINMAXINFO (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_HOTKEY (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_HELP (int wParam, int lParam) {
+    if (OS.IsWinCE) return null;
+    HELPINFO lphi = new HELPINFO ();
+    OS.MoveMemory (lphi, lParam, HELPINFO.sizeof);
+    Decorations shell = menuShell ();
+    if (!shell.isEnabled ()) return null;
+    if (lphi.iContextType is OS.HELPINFO_MENUITEM) {
+        MenuItem item = display.getMenuItem (lphi.iCtrlId);
+        if (item !is null && item.isEnabled ()) {
+            Widget widget = null;
+            if (item.hooks (DWT.Help)) {
+                widget = item;
+            } else {
+                Menu menu = item.parent;
+                if (menu.hooks (DWT.Help)) widget = menu;
+            }
+            if (widget !is null) {
+                int hwndShell = shell.handle;
+                OS.SendMessage (hwndShell, OS.WM_CANCELMODE, 0, 0);
+                widget.postEvent (DWT.Help);
+                return LRESULT.ONE;
+            }
+        }
+        return null;
+    }
+    if (hooks (DWT.Help)) {
+        postEvent (DWT.Help);
+        return LRESULT.ONE;
+    }
+    return null;
+}
+
+LRESULT WM_HSCROLL (int wParam, int lParam) {
+    Control control = display.getControl (lParam);
+    if (control is null) return null;
+    return control.wmScrollChild (wParam, lParam);
+}
+
+LRESULT WM_IME_CHAR (int wParam, int lParam) {
+    return wmIMEChar (handle, wParam, lParam);
+}
+
+LRESULT WM_IME_COMPOSITION (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_INITMENUPOPUP (int wParam, int lParam) {
+
+    /* Ignore WM_INITMENUPOPUP for an accelerator */
+    if (display.accelKeyHit) return null;
+
+    /*
+    * If the high order word of LPARAM is non-zero,
+    * the menu is the system menu and we can ignore
+    * WPARAM.  Otherwise, use WPARAM to find the menu.
+    */
+    Shell shell = getShell ();
+    Menu oldMenu = shell.activeMenu, newMenu = null;
+    if ((lParam >> 16) is 0) {
+        newMenu = menuShell ().findMenu (wParam);
+        if (newMenu !is null) newMenu.update ();
+    }
+    Menu menu = newMenu;
+    while (menu !is null && menu !is oldMenu) {
+        menu = menu.getParentMenu ();
+    }
+    if (menu is null) {
+        menu = shell.activeMenu;
+        while (menu !is null) {
+            /*
+            * It is possible (but unlikely), that application
+            * code could have disposed the widget in the hide
+            * event.  If this happens, stop searching up the
+            * ancestor list because there is no longer a link
+            * to follow.
+            */
+            menu.sendEvent (DWT.Hide);
+            if (menu.isDisposed ()) break;
+            menu = menu.getParentMenu ();
+            Menu ancestor = newMenu;
+            while (ancestor !is null && ancestor !is menu) {
+                ancestor = ancestor.getParentMenu ();
+            }
+            if (ancestor !is null) break;
+        }
+    }
+
+    /*
+    * The shell and the new menu may be disposed because of
+    * sending the hide event to the ancestor menus but setting
+    * a field to null in a disposed shell is not harmful.
+    */
+    if (newMenu !is null && newMenu.isDisposed ()) newMenu = null;
+    shell.activeMenu = newMenu;
+
+    /* Send the show event */
+    if (newMenu !is null && newMenu !is oldMenu) {
+        newMenu.sendEvent (DWT.Show);
+        // widget could be disposed at this point
+    }
+    return null;
+}
+
+LRESULT WM_INPUTLANGCHANGE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_KEYDOWN (int wParam, int lParam) {
+    return wmKeyDown (handle, wParam, lParam);
+}
+
+LRESULT WM_KEYUP (int wParam, int lParam) {
+    return wmKeyUp (handle, wParam, lParam);
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    return wmKillFocus (handle, wParam, lParam);
+}
+
+LRESULT WM_LBUTTONDBLCLK (int wParam, int lParam) {
+    return wmLButtonDblClk (handle, wParam, lParam);
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    return wmLButtonDown (handle, wParam, lParam);
+}
+
+LRESULT WM_LBUTTONUP (int wParam, int lParam) {
+    return wmLButtonUp (handle, wParam, lParam);
+}
+
+LRESULT WM_MBUTTONDBLCLK (int wParam, int lParam) {
+    return wmMButtonDblClk (handle, wParam, lParam);
+}
+
+LRESULT WM_MBUTTONDOWN (int wParam, int lParam) {
+    return wmMButtonDown (handle, wParam, lParam);
+}
+
+LRESULT WM_MBUTTONUP (int wParam, int lParam) {
+    return wmMButtonUp (handle, wParam, lParam);
+}
+
+LRESULT WM_MEASUREITEM (int wParam, int lParam) {
+    MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT ();
+    OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof);
+    if (struct.CtlType is OS.ODT_MENU) {
+        MenuItem item = display.getMenuItem (struct.itemID);
+        if (item is null) return null;
+        return item.wmMeasureChild (wParam, lParam);
+    }
+    int hwnd = OS.GetDlgItem (handle, struct.CtlID);
+    Control control = display.getControl (hwnd);
+    if (control is null) return null;
+    return control.wmMeasureChild (wParam, lParam);
+}
+
+LRESULT WM_MENUCHAR (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When the user types Alt+<key>
+    * and <key> does not match a mnemonic in the System
+    * menu or the menu bar, Windows beeps.  This beep is
+    * unexpected and unwanted by applications that look
+    * for Alt+<key>.  The fix is to detect the case and
+    * stop Windows from beeping by closing the menu.
+    */
+    int type = wParam >> 16;
+    if (type is 0 || type is OS.MF_SYSMENU) {
+        display.mnemonicKeyHit = false;
+        return new LRESULT (OS.MNC_CLOSE << 16);
+    }
+    return null;
+}
+
+LRESULT WM_MENUSELECT (int wParam, int lParam) {
+    int code = wParam >> 16;
+    Shell shell = getShell ();
+    if (code is -1 && lParam is 0) {
+        Menu menu = shell.activeMenu;
+        while (menu !is null) {
+            /*
+            * When the user cancels any menu that is not the
+            * menu bar, assume a mnemonic key was pressed to open
+            * the menu from WM_SYSCHAR.  When the menu was invoked
+            * using the mouse, this assumption is wrong but not
+            * harmful.  This variable is only used in WM_SYSCHAR
+            * and WM_SYSCHAR is only sent after the user has pressed
+            * a mnemonic.
+            */
+            display.mnemonicKeyHit = true;
+            /*
+            * It is possible (but unlikely), that application
+            * code could have disposed the widget in the hide
+            * event.  If this happens, stop searching up the
+            * parent list because there is no longer a link
+            * to follow.
+            */
+            menu.sendEvent (DWT.Hide);
+            if (menu.isDisposed ()) break;
+            menu = menu.getParentMenu ();
+        }
+        /*
+        * The shell may be disposed because of sending the hide
+        * event to the last active menu menu but setting a field
+        * to null in a destroyed widget is not harmful.
+        */
+        shell.activeMenu = null;
+        return null;
+    }
+    if ((code & OS.MF_SYSMENU) !is 0) return null;
+    if ((code & OS.MF_HILITE) !is 0) {
+        MenuItem item = null;
+        Decorations menuShell = menuShell ();
+        if ((code & OS.MF_POPUP) !is 0) {
+            int index = wParam & 0xFFFF;
+            MENUITEMINFO info = new MENUITEMINFO ();
+            info.cbSize = MENUITEMINFO.sizeof;
+            info.fMask = OS.MIIM_SUBMENU;
+            if (OS.GetMenuItemInfo (lParam, index, true, info)) {
+                Menu newMenu = menuShell.findMenu (info.hSubMenu);
+                if (newMenu !is null) item = newMenu.cascade;
+            }
+        } else {
+            Menu newMenu = menuShell.findMenu (lParam);
+            if (newMenu !is null) {
+                int id = wParam & 0xFFFF;
+                item = display.getMenuItem (id);
+            }
+            Menu oldMenu = shell.activeMenu;
+            if (oldMenu !is null) {
+                Menu ancestor = oldMenu;
+                while (ancestor !is null && ancestor !is newMenu) {
+                    ancestor = ancestor.getParentMenu ();
+                }
+                if (ancestor is newMenu) {
+                    ancestor = oldMenu;
+                    while (ancestor !is newMenu) {
+                        /*
+                        * It is possible (but unlikely), that application
+                        * code could have disposed the widget in the hide
+                        * event or the item about to be armed.  If this
+                        * happens, stop searching up the ancestor list
+                        * because there is no longer a link to follow.
+                        */
+                        ancestor.sendEvent (DWT.Hide);
+                        if (ancestor.isDisposed ()) break;
+                        ancestor = ancestor.getParentMenu ();
+                    }
+                    /*
+                    * The shell and/or the item could be disposed when
+                    * processing hide events from above.  If this happens,
+                    * ensure that the shell is not accessed and that no
+                    * arm event is sent to the item.
+                    */
+                    if (!shell.isDisposed ()) {
+                        if (newMenu !is null && newMenu.isDisposed ()) {
+                            newMenu = null;
+                        }
+                        shell.activeMenu = newMenu;
+                    }
+                    if (item !is null && item.isDisposed ()) item = null;
+                }
+            }
+        }
+        if (item !is null) item.sendEvent (DWT.Arm);
+    }
+    return null;
+}
+
+LRESULT WM_MOUSEACTIVATE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_MOUSEHOVER (int wParam, int lParam) {
+    return wmMouseHover (handle, wParam, lParam);
+}
+
+LRESULT WM_MOUSELEAVE (int wParam, int lParam) {
+    if (OS.COMCTL32_MAJOR >= 6) getShell ().fixToolTip ();
+    return wmMouseLeave (handle, wParam, lParam);
+}
+
+LRESULT WM_MOUSEMOVE (int wParam, int lParam) {
+    return wmMouseMove (handle, wParam, lParam);
+}
+
+LRESULT WM_MOUSEWHEEL (int wParam, int lParam) {
+    return wmMouseWheel (handle, wParam, lParam);
+}
+
+LRESULT WM_MOVE (int wParam, int lParam) {
+    if (findImageControl () !is null) {
+        if (this !is getShell ()) redrawChildren ();
+    } else {
+        if ((state & THEME_BACKGROUND) !is 0) {
+            if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                if (OS.IsWindowVisible (handle)) {
+                    if (findThemeControl () !is null) redrawChildren ();
+                }
+            }
+        }
+    }
+    sendEvent (DWT.Move);
+    // widget could be disposed at this point
+    return null;
+}
+
+LRESULT WM_NCACTIVATE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_NCCALCSIZE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_NCHITTEST (int wParam, int lParam) {
+    if (!OS.IsWindowEnabled (handle)) return null;
+    if (!isActive ()) return new LRESULT (OS.HTTRANSPARENT);
+    return null;
+}
+
+LRESULT WM_NCLBUTTONDOWN (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_NCPAINT (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_NOTIFY (int wParam, int lParam) {
+    NMHDR hdr = new NMHDR ();
+    OS.MoveMemory (hdr, lParam, NMHDR.sizeof);
+    return wmNotify (hdr, wParam, lParam);
+}
+
+LRESULT WM_PAINT (int wParam, int lParam) {
+    return super.wmPaint (handle, wParam, lParam);
+}
+
+LRESULT WM_PALETTECHANGED (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_PARENTNOTIFY (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_PASTE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_PRINT (int wParam, int lParam) {
+    return wmPrint (handle, wParam, lParam);
+}
+
+LRESULT WM_PRINTCLIENT (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_QUERYENDSESSION (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_QUERYNEWPALETTE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_QUERYOPEN (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_RBUTTONDBLCLK (int wParam, int lParam) {
+    return wmRButtonDblClk (handle, wParam, lParam);
+}
+
+LRESULT WM_RBUTTONDOWN (int wParam, int lParam) {
+    return wmRButtonDown (handle, wParam, lParam);
+}
+
+LRESULT WM_RBUTTONUP (int wParam, int lParam) {
+    return wmRButtonUp (handle, wParam, lParam);
+}
+
+LRESULT WM_SETCURSOR (int wParam, int lParam) {
+    int hitTest = (short) (lParam & 0xFFFF);
+    if (hitTest is OS.HTCLIENT) {
+        Control control = display.getControl (wParam);
+        if (control is null) return null;
+        Cursor cursor = control.findCursor ();
+        if (cursor !is null) {
+            OS.SetCursor (cursor.handle);
+            return LRESULT.ONE;
+        }
+    }
+    return null;
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    return wmSetFocus (handle, wParam, lParam);
+}
+
+LRESULT WM_SETTINGCHANGE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_SETFONT (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_SETREDRAW (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_SHOWWINDOW (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    sendEvent (DWT.Resize);
+    // widget could be disposed at this point
+    return null;
+}
+
+LRESULT WM_SYSCHAR (int wParam, int lParam) {
+    return wmSysChar (handle, wParam, lParam);
+}
+
+LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_SYSCOMMAND (int wParam, int lParam) {
+    /*
+    * Check to see if the command is a system command or
+    * a user menu item that was added to the System menu.
+    * When a user item is added to the System menu,
+    * WM_SYSCOMMAND must always return zero.
+    */
+    if ((wParam & 0xF000) is 0) {
+        Decorations shell = menuShell ();
+        if (shell.isEnabled ()) {
+            MenuItem item = display.getMenuItem (wParam & 0xFFFF);
+            if (item !is null) item.wmCommandChild (wParam, lParam);
+        }
+        return LRESULT.ZERO;
+    }
+
+    /* Process the System Command */
+    int cmd = wParam & 0xFFF0;
+    switch (cmd) {
+        case OS.SC_CLOSE:
+            int hwndShell = menuShell ().handle;
+            int bits = OS.GetWindowLong (hwndShell, OS.GWL_STYLE);
+            if ((bits & OS.WS_SYSMENU) is 0) return LRESULT.ZERO;
+            break;
+        case OS.SC_KEYMENU:
+            /*
+            * When lParam is zero, one of F10, Shift+F10, Ctrl+F10 or
+            * Ctrl+Shift+F10 was pressed.  If there is no menu bar and
+            * the focus control is interested in keystrokes, give the
+            * key to the focus control.  Normally, F10 with no menu bar
+            * moves focus to the System menu but this can be achieved
+            * using Alt+Space.  To allow the application to see F10,
+            * avoid running the default window proc.
+            *
+            * NOTE:  When F10 is pressed, WM_SYSCOMMAND is sent to the
+            * shell, not the focus control.  This is undocumented Windows
+            * behavior.
+            */
+            if (lParam is 0) {
+                Decorations shell = menuShell ();
+                Menu menu = shell.getMenuBar ();
+                if (menu is null) {
+                    Control control = display._getFocusControl ();
+                    if (control !is null) {
+                        if (control.hooks (DWT.KeyDown) || control.hooks (DWT.KeyUp)) {
+                            display.mnemonicKeyHit = false;
+                            return LRESULT.ZERO;
+                        }
+                    }
+                }
+            } else {
+                /*
+                * When lParam is not zero, Alt+<key> was pressed.  If the
+                * application is interested in keystrokes and there is a
+                * menu bar, check to see whether the key that was pressed
+                * matches a mnemonic on the menu bar.  Normally, Windows
+                * matches the first character of a menu item as well as
+                * matching the mnemonic character.  To allow the application
+                * to see the keystrokes in this case, avoid running the default
+                * window proc.
+                *
+                * NOTE: When the user types Alt+Space, the System menu is
+                * activated.  In this case the application should not see
+                * the keystroke.
+                */
+                if (hooks (DWT.KeyDown) || hooks (DWT.KeyUp)) {
+                    if (lParam !is ' ') {
+                        Decorations shell = menuShell ();
+                        Menu menu = shell.getMenuBar ();
+                        if (menu !is null) {
+                            char key = Display.mbcsToWcs (lParam);
+                            if (key !is 0) {
+                                key = Character.toUpperCase (key);
+                                MenuItem [] items = menu.getItems ();
+                                for (int i=0; i<items.length; i++) {
+                                    MenuItem item = items [i];
+                                    String text = item.getText ();
+                                    char mnemonic = findMnemonic (text);
+                                    if (text.length () > 0 && mnemonic is 0) {
+                                        char ch = text.charAt (0);
+                                        if (Character.toUpperCase (ch) is key) {
+                                            display.mnemonicKeyHit = false;
+                                            return LRESULT.ZERO;
+                                        }
+                                    }
+                                }
+                            }
+                        } else {
+                            display.mnemonicKeyHit = false;
+                        }
+                    }
+                }
+            }
+            // FALL THROUGH
+        case OS.SC_HSCROLL:
+        case OS.SC_VSCROLL:
+            /*
+            * Do not allow keyboard traversal of the menu bar
+            * or scrolling when the shell is not enabled.
+            */
+            Decorations shell = menuShell ();
+            if (!shell.isEnabled () || !shell.isActive ()) {
+                return LRESULT.ZERO;
+            }
+            break;
+        case OS.SC_MINIMIZE:
+            /* Save the focus widget when the shell is minimized */
+            menuShell ().saveFocus ();
+            break;
+    }
+    return null;
+}
+
+LRESULT WM_SYSKEYDOWN (int wParam, int lParam) {
+    return wmSysKeyDown (handle, wParam, lParam);
+}
+
+LRESULT WM_SYSKEYUP (int wParam, int lParam) {
+    return wmSysKeyUp (handle, wParam, lParam);
+}
+
+LRESULT WM_TIMER (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_UNDO (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_UPDATEUISTATE (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_VSCROLL (int wParam, int lParam) {
+    Control control = display.getControl (lParam);
+    if (control is null) return null;
+    return control.wmScrollChild (wParam, lParam);
+}
+
+LRESULT WM_WINDOWPOSCHANGED (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_WINDOWPOSCHANGING (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  When WM_SETREDRAW is used to turn off drawing
+    * for a control and the control is moved or resized, Windows does
+    * not redraw the area where the control once was in the parent.
+    * The fix is to detect this case and redraw the area.
+    */
+    if (drawCount !is 0) {
+        Shell shell = getShell ();
+        if (shell !is this) {
+            WINDOWPOS lpwp = new WINDOWPOS ();
+            OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
+            if ((lpwp.flags & OS.SWP_NOMOVE) is 0 || (lpwp.flags & OS.SWP_NOSIZE) is 0) {
+                RECT rect = new RECT ();
+                OS.GetWindowRect (topHandle (), rect);
+                int hwndParent = parent is null ? 0 : parent.handle;
+                OS.MapWindowPoints (0, hwndParent, rect, 2);
+                OS.InvalidateRect (hwndParent, rect, true);
+            }
+        }
+    }
+    return null;
+}
+
+LRESULT WM_XBUTTONDBLCLK (int wParam, int lParam) {
+    return wmXButtonDblClk (handle, wParam, lParam);
+}
+
+LRESULT WM_XBUTTONDOWN (int wParam, int lParam) {
+    return wmXButtonDown (handle, wParam, lParam);
+}
+
+LRESULT WM_XBUTTONUP (int wParam, int lParam) {
+    return wmXButtonUp (handle, wParam, lParam);
+}
+
+LRESULT wmColorChild (int wParam, int lParam) {
+    Control control = findBackgroundControl ();
+    if (control is null) {
+        if ((state & THEME_BACKGROUND) !is 0) {
+            if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                control = findThemeControl ();
+                if (control !is null) {
+                    RECT rect = new RECT ();
+                    OS.GetClientRect (handle, rect);
+                    OS.SetTextColor (wParam, getForegroundPixel ());
+                    OS.SetBkColor (wParam, getBackgroundPixel ());
+                    fillThemeBackground (wParam, control, rect);
+                    OS.SetBkMode (wParam, OS.TRANSPARENT);
+                    return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH));
+                }
+            }
+        }
+        if (foreground is -1) return null;
+    }
+    if (control is null) control = this;
+    int forePixel = getForegroundPixel ();
+    int backPixel = control.getBackgroundPixel ();
+    OS.SetTextColor (wParam, forePixel);
+    OS.SetBkColor (wParam, backPixel);
+    if (control.backgroundImage !is null) {
+        RECT rect = new RECT ();
+        OS.GetClientRect (handle, rect);
+        int hwnd = control.handle;
+        int hBitmap = control.backgroundImage.handle;
+        OS.MapWindowPoints (handle, hwnd, rect, 2);
+        POINT lpPoint = new POINT ();
+        OS.GetWindowOrgEx (wParam, lpPoint);
+        OS.SetBrushOrgEx (wParam, -rect.left - lpPoint.x, -rect.top - lpPoint.y, lpPoint);
+        int hBrush = findBrush (hBitmap, OS.BS_PATTERN);
+        if ((state & DRAW_BACKGROUND) !is 0) {
+            int hOldBrush = OS.SelectObject (wParam, hBrush);
+            OS.MapWindowPoints (hwnd, handle, rect, 2);
+            OS.PatBlt (wParam, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
+            OS.SelectObject (wParam, hOldBrush);
+        }
+        OS.SetBkMode (wParam, OS.TRANSPARENT);
+        return new LRESULT (hBrush);
+    }
+    int hBrush = findBrush (backPixel, OS.BS_SOLID);
+    if ((state & DRAW_BACKGROUND) !is 0) {
+        RECT rect = new RECT ();
+        OS.GetClientRect (handle, rect);
+        int hOldBrush = OS.SelectObject (wParam, hBrush);
+        OS.PatBlt (wParam, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
+        OS.SelectObject (wParam, hOldBrush);
+    }
+    return new LRESULT (hBrush);
+}
+
+LRESULT wmCommandChild (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT wmDrawChild (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT wmMeasureChild (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT wmNotify (NMHDR hdr, int wParam, int lParam) {
+    Control control = display.getControl (hdr.hwndFrom);
+    if (control is null) return null;
+    return control.wmNotifyChild (hdr, wParam, lParam);
+}
+
+LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
+    return null;
+}
+
+LRESULT wmScrollChild (int wParam, int lParam) {
+    return null;
+}
+
+}
+
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/CoolBar.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1209 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.CoolBar;
+
+import dwt.widgets.Composite;
+
+class CoolBar : Composite {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.INITCOMMONCONTROLSEX;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MARGINS;
+import dwt.internal.win32.NMCUSTOMDRAW;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.NMREBARCHEVRON;
+import dwt.internal.win32.NMREBARCHILDSIZE;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.REBARBANDINFO;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class provide an area for dynamically
+ * positioning the items they contain.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>CoolItem</code>.
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to add <code>Control</code> children to it,
+ * or set a layout on it.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>FLAT, HORIZONTAL, VERTICAL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class CoolBar extends Composite {
+    CoolItem [] items;
+    CoolItem [] originalItems;
+    bool locked;
+    bool ignoreResize;
+    static final int ReBarProc;
+    static final TCHAR ReBarClass = new TCHAR (0, OS.REBARCLASSNAME, true);
+    static {
+        INITCOMMONCONTROLSEX icex = new INITCOMMONCONTROLSEX ();
+        icex.dwSize = INITCOMMONCONTROLSEX.sizeof;
+        icex.dwICC = OS.ICC_COOL_CLASSES;
+        OS.InitCommonControlsEx (icex);
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, ReBarClass, lpWndClass);
+        ReBarProc = lpWndClass.lpfnWndProc;
+    }
+    static final int SEPARATOR_WIDTH = 2;
+    static final int MAX_WIDTH = 0x7FFF;
+    static final int DEFAULT_COOLBAR_WIDTH = 0;
+    static final int DEFAULT_COOLBAR_HEIGHT = 0;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public CoolBar (Composite parent, int style) {
+    super (parent, checkStyle (style));
+    /*
+    * Ensure that either of HORIZONTAL or VERTICAL is set.
+    * NOTE: HORIZONTAL and VERTICAL have the same values
+    * as H_SCROLL and V_SCROLL so it is necessary to first
+    * clear these bits to avoid scroll bars and then reset
+    * the bits using the original style supplied by the
+    * programmer.
+    *
+    * NOTE: The CCS_VERT style cannot be applied when the
+    * widget is created because of this conflict.
+    */
+    if ((style & DWT.VERTICAL) !is 0) {
+        this.style |= DWT.VERTICAL;
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.CCS_VERT);
+    } else {
+        this.style |= DWT.HORIZONTAL;
+    }
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.CallWindowProc (ReBarProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    style |= DWT.NO_FOCUS;
+    /*
+    * Even though it is legal to create this widget
+    * with scroll bars, they serve no useful purpose
+    * because they do not automatically scroll the
+    * widget's client area.  The fix is to clear
+    * the DWT style.
+    */
+    return style & ~(DWT.H_SCROLL | DWT.V_SCROLL);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = 0, height = 0;
+    int border = getBorderWidth ();
+    int newWidth = wHint is DWT.DEFAULT ? 0x3FFF : wHint + (border * 2);
+    int newHeight = hHint is DWT.DEFAULT ? 0x3FFF : hHint + (border * 2);
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    if (count !is 0) {
+        ignoreResize = true;
+        bool redraw = false;
+        if (OS.IsWindowVisible (handle)) {
+            if (OS.COMCTL32_MAJOR >= 6) {
+                redraw = true;
+                OS.UpdateWindow (handle);
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+            } else {
+                redraw = drawCount is 0;
+                if (redraw) {
+                    OS.UpdateWindow (handle);
+                    OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+                }
+            }
+        }
+        RECT oldRect = new RECT ();
+        OS.GetWindowRect (handle, oldRect);
+        int oldWidth = oldRect.right - oldRect.left;
+        int oldHeight = oldRect.bottom - oldRect.top;
+        int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER;
+        SetWindowPos (handle, 0, 0, 0, newWidth, newHeight, flags);
+        RECT rect = new RECT ();
+        OS.SendMessage (handle, OS.RB_GETRECT, count - 1, rect);
+        height = Math.max (height, rect.bottom);
+        SetWindowPos (handle, 0, 0, 0, oldWidth, oldHeight, flags);
+        REBARBANDINFO rbBand = new REBARBANDINFO ();
+        rbBand.cbSize = REBARBANDINFO.sizeof;
+        rbBand.fMask = OS.RBBIM_IDEALSIZE | OS.RBBIM_STYLE;
+        int rowWidth = 0;
+        for (int i = 0; i < count; i++) {
+            OS.SendMessage(handle, OS.RB_GETBANDINFO, i, rbBand);
+            if ((rbBand.fStyle & OS.RBBS_BREAK) !is 0) {
+                width = Math.max(width, rowWidth);
+                rowWidth = 0;
+            }
+            rowWidth += rbBand.cxIdeal + getMargin (i);
+        }
+        width = Math.max(width, rowWidth);
+        if (redraw) {
+            if (OS.COMCTL32_MAJOR >= 6) {
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+            } else {
+                OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+            }
+        }
+        ignoreResize = false;
+    }
+    if (width is 0) width = DEFAULT_COOLBAR_WIDTH;
+    if (height is 0) height = DEFAULT_COOLBAR_HEIGHT;
+    if ((style & DWT.VERTICAL) !is 0) {
+        int tmp = width;
+        width = height;
+        height = tmp;
+    }
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    height += border * 2;
+    width += border * 2;
+    return new Point (width, height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state &= ~(CANVAS | THEME_BACKGROUND);
+
+    /*
+    * Feature in Windows.  When the control is created,
+    * it does not use the default system font.  A new HFONT
+    * is created and destroyed when the control is destroyed.
+    * This means that a program that queries the font from
+    * this control, uses the font in another control and then
+    * destroys this control will have the font unexpectedly
+    * destroyed in the other control.  The fix is to assign
+    * the font ourselves each time the control is created.
+    * The control will not destroy a font that it did not
+    * create.
+    */
+    int hFont = OS.GetStockObject (OS.SYSTEM_FONT);
+    OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
+}
+
+void createItem (CoolItem item, int index) {
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    if (!(0 <= index && index <= count)) error (DWT.ERROR_INVALID_RANGE);
+    int id = 0;
+    while (id < items.length && items [id] !is null) id++;
+    if (id is items.length) {
+        CoolItem [] newItems = new CoolItem [items.length + 4];
+        System.arraycopy (items, 0, newItems, 0, items.length);
+        items = newItems;
+    }
+    int hHeap = OS.GetProcessHeap ();
+    int lpText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_TEXT | OS.RBBIM_STYLE | OS.RBBIM_ID;
+    rbBand.fStyle = OS.RBBS_VARIABLEHEIGHT | OS.RBBS_GRIPPERALWAYS;
+    if ((item.style & DWT.DROP_DOWN) !is 0) {
+        rbBand.fStyle |= OS.RBBS_USECHEVRON;
+    }
+    rbBand.lpText = lpText;
+    rbBand.wID = id;
+
+    /*
+    * Feature in Windows.  When inserting an item at end of a row,
+    * sometimes, Windows will begin to place the item on the right
+    * side of the cool bar.  The fix is to resize the new items to
+    * the maximum size and then resize the next to last item to the
+    * ideal size.
+    */
+    int lastIndex = getLastIndexOfRow (index - 1);
+    bool fixLast = index is lastIndex + 1;
+    if (fixLast) {
+        rbBand.fMask |= OS.RBBIM_SIZE;
+        rbBand.cx = MAX_WIDTH;
+    }
+
+    /*
+    * Feature in Windows. Is possible that the item at index zero
+    * has the RBBS_BREAK flag set. When a new item is inserted at
+    * position zero, the previous item at position zero moves to
+    * a new line.  The fix is to detect this case and clear the
+    * RBBS_BREAK flag on the previous item before inserting the
+    * new item.
+    */
+    if (index is 0 && count > 0) {
+        getItem (0).setWrap (false);
+    }
+
+    /* Insert the item */
+    if (OS.SendMessage (handle, OS.RB_INSERTBAND, index, rbBand) is 0) {
+        error (DWT.ERROR_ITEM_NOT_ADDED);
+    }
+
+    /* Resize the next to last item to the ideal size */
+    if (fixLast) {
+        resizeToPreferredWidth (lastIndex);
+    }
+
+    OS.HeapFree (hHeap, 0, lpText);
+    items [item.id = id] = item;
+    int length = originalItems.length;
+    CoolItem [] newOriginals = new CoolItem [length + 1];
+    System.arraycopy (originalItems, 0, newOriginals, 0, index);
+    System.arraycopy (originalItems, index, newOriginals, index + 1, length - index);
+    newOriginals [index] = item;
+    originalItems = newOriginals;
+}
+
+void createWidget () {
+    super.createWidget ();
+    items = new CoolItem [4];
+    originalItems = new CoolItem [0];
+}
+
+void destroyItem (CoolItem item) {
+    int index = OS.SendMessage (handle, OS.RB_IDTOINDEX, item.id, 0);
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    if (count !is 0) {
+        int lastIndex = getLastIndexOfRow (index);
+        if (index is lastIndex) {
+            /*
+            * Feature in Windows.  If the last item in a row is
+            * given its ideal size, it will be placed at the far
+            * right hand edge of the coolbar.  It is preferred
+            * that the last item appear next to the second last
+            * item.  The fix is to size the last item of each row
+            * so that it occupies all the available space to the
+            * right in the row.
+            */
+            resizeToMaximumWidth (lastIndex - 1);
+        }
+    }
+
+    /*
+    * Feature in Windows.  When Windows removed a rebar
+    * band, it makes the band child invisible.  The fix
+    * is to show the child.
+    */
+    Control control = item.control;
+    bool wasVisible = control !is null && !control.isDisposed() && control.getVisible ();
+
+    /*
+    * When a wrapped item is being deleted, make the next
+    * item in the row wrapped in order to preserve the row.
+    * In order to avoid an unnecessary layout, temporarily
+    * ignore WM_SIZE.  If the next item is wrapped then a
+    * row will be deleted and the WM_SIZE is necessary.
+    */
+    CoolItem nextItem = null;
+    if (item.getWrap ()) {
+        if (index + 1 < count) {
+            nextItem = getItem (index + 1);
+            ignoreResize = !nextItem.getWrap ();
+        }
+    }
+    if (OS.SendMessage (handle, OS.RB_DELETEBAND, index, 0) is 0) {
+        error (DWT.ERROR_ITEM_NOT_REMOVED);
+    }
+    items [item.id] = null;
+    item.id = -1;
+    if (ignoreResize) {
+        nextItem.setWrap (true);
+        ignoreResize = false;
+    }
+
+    /* Restore the visible state of the control */
+    if (wasVisible) control.setVisible (true);
+
+    index = 0;
+    while (index < originalItems.length) {
+        if (originalItems [index] is item) break;
+        index++;
+    }
+    int length = originalItems.length - 1;
+    CoolItem [] newOriginals = new CoolItem [length];
+    System.arraycopy (originalItems, 0, newOriginals, 0, index);
+    System.arraycopy (originalItems, index + 1, newOriginals, index, length - index);
+    originalItems = newOriginals;
+}
+
+void drawThemeBackground (int hDC, int hwnd, RECT rect) {
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        if (background is -1 && (style & DWT.FLAT) !is 0) {
+            Control control = findBackgroundControl ();
+            if (control !is null && control.backgroundImage !is null) {
+                fillBackground (hDC, control.getBackgroundPixel (), rect);
+                return;
+            }
+        }
+    }
+    RECT rect2 = new RECT ();
+    OS.GetClientRect (handle, rect2);
+    OS.MapWindowPoints (handle, hwnd, rect2, 2);
+    POINT lpPoint = new POINT ();
+    OS.SetWindowOrgEx (hDC, -rect2.left, -rect2.top, lpPoint);
+    OS.SendMessage (handle, OS.WM_PRINT, hDC, OS.PRF_CLIENT | OS.PRF_ERASEBKGND);
+    OS.SetWindowOrgEx (hDC, lpPoint.x, lpPoint.y, null);
+}
+
+Control findThemeControl () {
+    if ((style & DWT.FLAT) !is 0) return this;
+    return background is -1 && backgroundImage is null ? this : super.findThemeControl ();
+}
+
+int getMargin (int index) {
+    int margin = 0;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        MARGINS margins = new MARGINS ();
+        OS.SendMessage (handle, OS.RB_GETBANDMARGINS, 0, margins);
+        margin += margins.cxLeftWidth + margins.cxRightWidth;
+    }
+    RECT rect = new RECT ();
+    OS.SendMessage (handle, OS.RB_GETBANDBORDERS, index, rect);
+    if ((style & DWT.FLAT) !is 0) {
+        /*
+        * Bug in Windows.  When the style bit  RBS_BANDBORDERS is not set
+        * the rectangle returned by RBS_BANDBORDERS is four pixels too small.
+        * The fix is to add four pixels to the result.
+        */
+        if ((style & DWT.VERTICAL) !is 0) {
+            margin += rect.top + 4;
+        } else {
+            margin += rect.left + 4;
+        }
+    } else {
+        if ((style & DWT.VERTICAL) !is 0) {
+            margin += rect.top + rect.bottom;
+        } else {
+            margin += rect.left + rect.right;
+        }
+    }
+    if ((style & DWT.FLAT) is 0) {
+        if (!isLastItemOfRow (index)) {
+            margin += CoolBar.SEPARATOR_WIDTH;
+        }
+    }
+    return margin;
+}
+
+/**
+ * Returns the item that is currently displayed at the given,
+ * zero-relative index. Throws an exception if the index is
+ * out of range.
+ *
+ * @param index the visual index of the item to return
+ * @return the item at the given visual index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public CoolItem getItem (int index) {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    if (!(0 <= index && index < count)) error (DWT.ERROR_INVALID_RANGE);
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_ID;
+    OS.SendMessage (handle, OS.RB_GETBANDINFO, index, rbBand);
+    return items [rbBand.wID];
+}
+
+/**
+ * Returns the number of items contained in the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+}
+
+/**
+ * Returns an array of zero-relative ints that map
+ * the creation order of the receiver's items to the
+ * order in which they are currently being displayed.
+ * <p>
+ * Specifically, the indices of the returned array represent
+ * the current visual order of the items, and the contents
+ * of the array represent the creation order of the items.
+ * </p><p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the current visual order of the receiver's items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int [] getItemOrder () {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    int [] indices = new int [count];
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_ID;
+    for (int i=0; i<count; i++) {
+        OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
+        CoolItem item = items [rbBand.wID];
+        int index = 0;
+        while (index<originalItems.length) {
+            if (originalItems [index] is item) break;
+            index++;
+        }
+        if (index is originalItems.length) error (DWT.ERROR_CANNOT_GET_ITEM);
+        indices [i] = index;
+    }
+    return indices;
+}
+
+/**
+ * Returns an array of <code>CoolItem</code>s in the order
+ * in which they are currently being displayed.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the receiver's items in their current visual order
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public CoolItem [] getItems () {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    CoolItem [] result = new CoolItem [count];
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_ID;
+    for (int i=0; i<count; i++) {
+        OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
+        result [i] = items [rbBand.wID];
+    }
+    return result;
+}
+
+/**
+ * Returns an array of points whose x and y coordinates describe
+ * the widths and heights (respectively) of the items in the receiver
+ * in the order in which they are currently being displayed.
+ *
+ * @return the receiver's item sizes in their current visual order
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point [] getItemSizes () {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    Point [] sizes = new Point [count];
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_CHILDSIZE;
+    int separator = (style & DWT.FLAT) is 0 ? SEPARATOR_WIDTH : 0;
+    MARGINS margins = new MARGINS ();
+    for (int i=0; i<count; i++) {
+        RECT rect = new RECT ();
+        OS.SendMessage (handle, OS.RB_GETRECT, i, rect);
+        OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
+        if (OS.COMCTL32_MAJOR >= 6) {
+            OS.SendMessage (handle, OS.RB_GETBANDMARGINS, 0, margins);
+            rect.left -= margins.cxLeftWidth;
+            rect.right += margins.cxRightWidth;
+        }
+        if (!isLastItemOfRow(i)) rect.right += separator;
+        if ((style & DWT.VERTICAL) !is 0) {
+            sizes [i] = new Point (rbBand.cyChild, rect.right - rect.left);
+        } else {
+            sizes [i] = new Point (rect.right - rect.left, rbBand.cyChild);
+        }
+    }
+    return sizes;
+}
+
+int getLastIndexOfRow (int index) {
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    if (count is 0) return -1;
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_STYLE;
+    for (int i=index + 1; i<count; i++) {
+        OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
+        if ((rbBand.fStyle & OS.RBBS_BREAK) !is 0) {
+            return i - 1;
+        }
+    }
+    return count - 1;
+}
+
+bool isLastItemOfRow (int index) {
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    if (index + 1 is count) return true;
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_STYLE;
+    OS.SendMessage (handle, OS.RB_GETBANDINFO, index + 1, rbBand);
+    return (rbBand.fStyle & OS.RBBS_BREAK) !is 0;
+}
+
+/**
+ * Returns whether or not the receiver is 'locked'. When a coolbar
+ * is locked, its items cannot be repositioned.
+ *
+ * @return true if the coolbar is locked, false otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public bool getLocked () {
+    checkWidget ();
+    return locked;
+}
+
+/**
+ * Returns an array of ints that describe the zero-relative
+ * indices of any item(s) in the receiver that will begin on
+ * a new row. The 0th visible item always begins the first row,
+ * therefore it does not count as a wrap index.
+ *
+ * @return an array containing the receiver's wrap indices, or an empty array if all items are in one row
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int [] getWrapIndices () {
+    checkWidget ();
+    CoolItem [] items = getItems ();
+    int [] indices = new int [items.length];
+    int count = 0;
+    for (int i=0; i<items.length; i++) {
+        if (items [i].getWrap ()) indices [count++] = i;
+    }
+    int [] result = new int [count];
+    System.arraycopy (indices, 0, result, 0, count);
+    return result;
+}
+
+/**
+ * Searches the receiver's items in the order they are currently
+ * being displayed, starting at the first item (index 0), until
+ * an item is found that is equal to the argument, and returns
+ * the index of that item. If no item is found, returns -1.
+ *
+ * @param item the search item
+ * @return the visual order index of the search item, or -1 if the item is not found
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item is disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (CoolItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (item.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT);
+    return OS.SendMessage (handle, OS.RB_IDTOINDEX, item.id, 0);
+}
+
+void resizeToPreferredWidth (int index) {
+    /*
+    * Bug in Windows.  When RB_GETBANDBORDERS is sent
+    * with an index out of range, Windows GP's.  The
+    * fix is to ensure the index is in range.
+    */
+    int count = OS.SendMessage(handle, OS.RB_GETBANDCOUNT, 0, 0);
+    if (0 <= index && index < count) {
+        REBARBANDINFO rbBand = new REBARBANDINFO();
+        rbBand.cbSize = REBARBANDINFO.sizeof;
+        rbBand.fMask = OS.RBBIM_IDEALSIZE;
+        OS.SendMessage (handle, OS.RB_GETBANDINFO, index, rbBand);
+        RECT rect = new RECT ();
+        OS.SendMessage (handle, OS.RB_GETBANDBORDERS, index, rect);
+        rbBand.cx = rbBand.cxIdeal + rect.left;
+        if ((style & DWT.FLAT) is 0) rbBand.cx += rect.right;
+        rbBand.fMask = OS.RBBIM_SIZE;
+        OS.SendMessage (handle, OS.RB_SETBANDINFO, index, rbBand);
+    }
+}
+
+void resizeToMaximumWidth (int index) {
+    REBARBANDINFO rbBand = new REBARBANDINFO();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_SIZE;
+    rbBand.cx = MAX_WIDTH;
+    OS.SendMessage (handle, OS.RB_SETBANDINFO, index, rbBand);
+}
+
+void releaseChildren (bool destroy) {
+    if (items !is null) {
+        for (int i=0; i<items.length; i++) {
+            CoolItem item = items [i];
+            if (item !is null && !item.isDisposed ()) {
+                item.release (false);
+            }
+        }
+        items = null;
+    }
+    super.releaseChildren (destroy);
+}
+
+void removeControl (Control control) {
+    super.removeControl (control);
+    for (int i=0; i<items.length; i++) {
+        CoolItem item = items [i];
+        if (item !is null && item.control is control) {
+            item.setControl (null);
+        }
+    }
+}
+
+void setBackgroundPixel (int pixel) {
+    if (pixel is -1) pixel = defaultBackground ();
+    OS.SendMessage (handle, OS.RB_SETBKCOLOR, 0, pixel);
+    setItemColors (OS.SendMessage (handle, OS.RB_GETTEXTCOLOR, 0, 0), pixel);
+    /*
+    * Feature in Windows.  For some reason, Windows
+    * does not fully erase the coolbar area and coolbar
+    * items when you set the background.  The fix is
+    * to invalidate the coolbar area.
+    */
+    if (!OS.IsWindowVisible (handle)) return;
+    if (OS.IsWinCE) {
+        OS.InvalidateRect (handle, null, true);
+    } else {
+        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+        OS.RedrawWindow (handle, null, 0, flags);
+    }
+}
+
+void setForegroundPixel (int pixel) {
+    if (pixel is -1) pixel = defaultForeground ();
+    OS.SendMessage (handle, OS.RB_SETTEXTCOLOR, 0, pixel);
+    setItemColors (pixel, OS.SendMessage (handle, OS.RB_GETBKCOLOR, 0, 0));
+}
+
+void setItemColors (int foreColor, int backColor) {
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_COLORS;
+    rbBand.clrFore = foreColor;
+    rbBand.clrBack = backColor;
+    for (int i=0; i<count; i++) {
+        OS.SendMessage (handle, OS.RB_SETBANDINFO, i, rbBand);
+    }
+}
+
+/**
+ * Sets the receiver's item order, wrap indices, and item sizes
+ * all at once. This method is typically used to restore the
+ * displayed state of the receiver to a previously stored state.
+ * <p>
+ * The item order is the order in which the items in the receiver
+ * should be displayed, given in terms of the zero-relative ordering
+ * of when the items were added.
+ * </p><p>
+ * The wrap indices are the indices of all item(s) in the receiver
+ * that will begin on a new row. The indices are given in the order
+ * specified by the item order. The 0th item always begins the first
+ * row, therefore it does not count as a wrap index. If wrap indices
+ * is null or empty, the items will be placed on one line.
+ * </p><p>
+ * The sizes are specified in an array of points whose x and y
+ * coordinates describe the new widths and heights (respectively)
+ * of the receiver's items in the order specified by the item order.
+ * </p>
+ *
+ * @param itemOrder an array of indices that describe the new order to display the items in
+ * @param wrapIndices an array of wrap indices, or null
+ * @param sizes an array containing the new sizes for each of the receiver's items in visual order
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if item order or sizes is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if item order or sizes is not the same length as the number of items</li>
+ * </ul>
+ */
+public void setItemLayout (int [] itemOrder, int [] wrapIndices, Point [] sizes) {
+    checkWidget ();
+    setRedraw (false);
+    setItemOrder (itemOrder);
+    setWrapIndices (wrapIndices);
+    setItemSizes (sizes);
+    setRedraw (true);
+}
+
+/*
+ * Sets the order that the items in the receiver should
+ * be displayed in to the given argument which is described
+ * in terms of the zero-relative ordering of when the items
+ * were added.
+ *
+ * @param itemOrder the new order to display the items in
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
+ * </ul>
+ */
+void setItemOrder (int [] itemOrder) {
+    if (itemOrder is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int itemCount = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    if (itemOrder.length !is itemCount) error (DWT.ERROR_INVALID_ARGUMENT);
+
+    /* Ensure that itemOrder does not contain any duplicates. */
+    bool [] set = new bool [itemCount];
+    for (int i=0; i<itemOrder.length; i++) {
+        int index = itemOrder [i];
+        if (index < 0 || index >= itemCount) error (DWT.ERROR_INVALID_RANGE);
+        if (set [index]) error (DWT.ERROR_INVALID_ARGUMENT);
+        set [index] = true;
+    }
+
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    for (int i=0; i<itemOrder.length; i++) {
+        int id = originalItems [itemOrder [i]].id;
+        int index = OS.SendMessage (handle, OS.RB_IDTOINDEX, id, 0);
+        if (index !is i) {
+            int lastItemSrcRow = getLastIndexOfRow (index);
+            int lastItemDstRow = getLastIndexOfRow (i);
+            if (index is lastItemSrcRow) {
+                resizeToPreferredWidth (index);
+            }
+            if (i is lastItemDstRow) {
+                resizeToPreferredWidth (i);
+            }
+
+            /* Move the item */
+            OS.SendMessage (handle, OS.RB_MOVEBAND, index, i);
+
+            if (index is lastItemSrcRow && index - 1 >= 0) {
+                resizeToMaximumWidth (index - 1);
+            }
+            if (i is lastItemDstRow) {
+                resizeToMaximumWidth (i);
+            }
+        }
+    }
+}
+
+/*
+ * Sets the width and height of the receiver's items to the ones
+ * specified by the argument, which is an array of points whose x
+ * and y coordinates describe the widths and heights (respectively)
+ * in the order in which the items are currently being displayed.
+ *
+ * @param sizes an array containing the new sizes for each of the receiver's items in visual order
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of sizes is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the array of sizes is not the same length as the number of items</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+void setItemSizes (Point [] sizes) {
+    if (sizes is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    if (sizes.length !is count) error (DWT.ERROR_INVALID_ARGUMENT);
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_ID;
+    for (int i=0; i<count; i++) {
+        OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
+        items [rbBand.wID].setSize (sizes [i].x, sizes [i].y);
+    }
+}
+
+/**
+ * Sets whether or not the receiver is 'locked'. When a coolbar
+ * is locked, its items cannot be repositioned.
+ *
+ * @param locked lock the coolbar if true, otherwise unlock the coolbar
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public void setLocked (bool locked) {
+    checkWidget ();
+    this.locked = locked;
+    int count = OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_STYLE;
+    for (int i=0; i<count; i++) {
+        OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
+        if (locked) {
+            rbBand.fStyle |= OS.RBBS_NOGRIPPER;
+        } else {
+            rbBand.fStyle &= ~OS.RBBS_NOGRIPPER;
+        }
+        OS.SendMessage (handle, OS.RB_SETBANDINFO, i, rbBand);
+    }
+}
+
+/**
+ * Sets the indices of all item(s) in the receiver that will
+ * begin on a new row. The indices are given in the order in
+ * which they are currently being displayed. The 0th item
+ * always begins the first row, therefore it does not count
+ * as a wrap index. If indices is null or empty, the items
+ * will be placed on one line.
+ *
+ * @param indices an array of wrap indices, or null
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setWrapIndices (int [] indices) {
+    checkWidget ();
+    if (indices is null) indices = new int [0];
+    int count = getItemCount ();
+    for (int i=0; i<indices.length; i++) {
+        if (indices [i] < 0 || indices [i] >= count) {
+            error (DWT.ERROR_INVALID_RANGE);
+        }
+    }
+    setRedraw (false);
+    CoolItem [] items = getItems ();
+    for (int i=0; i<items.length; i++) {
+        CoolItem item = items [i];
+        if (item.getWrap ()) {
+            resizeToPreferredWidth (i - 1);
+            item.setWrap (false);
+        }
+    }
+    resizeToMaximumWidth (count - 1);
+    for (int i=0; i<indices.length; i++) {
+        int index = indices [i];
+        if (0 <= index && index < items.length) {
+            CoolItem item = items [index];
+            item.setWrap (true);
+            resizeToMaximumWidth (index - 1);
+        }
+    }
+    setRedraw (true);
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.CCS_NODIVIDER | OS.CCS_NORESIZE;
+    bits |= OS.RBS_VARHEIGHT | OS.RBS_DBLCLKTOGGLE;
+    if ((style & DWT.FLAT) is 0) bits |= OS.RBS_BANDBORDERS;
+    return bits;
+}
+
+TCHAR windowClass () {
+    return ReBarClass;
+}
+
+int windowProc () {
+    return ReBarProc;
+}
+
+LRESULT WM_COMMAND (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When the coolbar window
+    * proc processes WM_COMMAND, it forwards this
+    * message to its parent.  This is done so that
+    * children of this control that send this message
+    * type to their parent will notify not only
+    * this control but also the parent of this control,
+    * which is typically the application window and
+    * the window that is looking for the message.
+    * If the control did not forward the message,
+    * applications would have to subclass the control
+    * window to see the message. Because the control
+    * window is subclassed by DWT, the message
+    * is delivered twice, once by DWT and once when
+    * the message is forwarded by the window proc.
+    * The fix is to avoid calling the window proc
+    * for this control.
+    */
+    LRESULT result = super.WM_COMMAND (wParam, lParam);
+    if (result !is null) return result;
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    /*
+    * Feature in Windows.  For some reason, Windows
+    * does not fully erase the area that the cool bar
+    * occupies when the size of the cool bar is larger
+    * than the space occupied by the cool bar items.
+    * The fix is to erase the cool bar background.
+    *
+    * NOTE: On versions of Windows prior to XP, for
+    * some reason, the cool bar draws separators in
+    * WM_ERASEBKGND.  Therefore it is essential to run
+    * the cool bar window proc after the background has
+    * been erased.
+    */
+    if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+        drawBackground (wParam);
+        return null;
+    }
+    return result;
+}
+
+LRESULT WM_NOTIFY (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When the cool bar window
+    * proc processes WM_NOTIFY, it forwards this
+    * message to its parent.  This is done so that
+    * children of this control that send this message
+    * type to their parent will notify not only
+    * this control but also the parent of this control,
+    * which is typically the application window and
+    * the window that is looking for the message.
+    * If the control did not forward the message,
+    * applications would have to subclass the control
+    * window to see the message. Because the control
+    * window is subclassed by DWT, the message
+    * is delivered twice, once by DWT and once when
+    * the message is forwarded by the window proc.
+    * The fix is to avoid calling the window proc
+    * for this control.
+    */
+    LRESULT result = super.WM_NOTIFY (wParam, lParam);
+    if (result !is null) return result;
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_SETREDRAW (int wParam, int lParam) {
+    LRESULT result = super.WM_SETREDRAW (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  When redraw is turned off, the rebar
+    * control does not call the default window proc.  This means
+    * that the rebar will redraw and children of the rebar will
+    * also redraw.  The fix is to call both the rebar window proc
+    * and the default window proc.
+    *
+    * NOTE: The rebar control can resize itself in WM_SETREDRAW.
+    * When redraw is turned off by the default window proc, this
+    * can leave pixel corruption in the parent.  The fix is to
+    * detect the size change and damage the previous area in the
+    * parent.
+    *
+    * NOTE:  In version 6.00 of COMCTL32.DLL, when WM_SETREDRAW
+    * is off, we cannot detect that the size has changed causing
+    * pixel corruption.  The fix is to disallow WM_SETREDRAW by
+    * not running the default window proc or the rebar window
+    * proc.
+    */
+    if (OS.COMCTL32_MAJOR >= 6) return LRESULT.ZERO;
+    Rectangle rect = getBounds ();
+    int code = callWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
+    OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
+    if (!rect.equals (getBounds ())) {
+        parent.redraw (rect.x, rect.y, rect.width, rect.height, true);
+    }
+    return new LRESULT (code);
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    if (ignoreResize) {
+        int code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam);
+        if (code is 0) return LRESULT.ZERO;
+        return new LRESULT (code);
+    }
+    //TEMPORARY CODE
+//  if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+//      if (background is -1 && (style & DWT.FLAT) is 0) {
+//          OS.InvalidateRect (handle, null, true);
+//      }
+//  }
+    return super.WM_SIZE (wParam, lParam);
+}
+
+LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
+    switch (hdr.code) {
+        case OS.RBN_BEGINDRAG: {
+            int pos = OS.GetMessagePos ();
+            POINT pt = new POINT ();
+            pt.x = (short) (pos & 0xFFFF);
+            pt.y = (short) (pos >> 16);
+            OS.ScreenToClient (handle, pt);
+            int button = display.lastButton !is 0 ? display.lastButton : 1;
+            if (!sendDragEvent (button, pt.x, pt.y)) return LRESULT.ONE;
+            break;
+        }
+        case OS.RBN_CHILDSIZE: {
+            /*
+            * Bug in Windows.  When Windows sets the size of the rebar band
+            * child and the child is a combo box, the size of the drop down
+            * portion of the combo box is resized to zero.  The fix is to set
+            * the size of the control to the current size after the rebar has
+            * already resized it.  If the control is not a combo, this does
+            * nothing.  If the control is a combo, the drop down portion is
+            * recalculated.
+            */
+            NMREBARCHILDSIZE lprbcs  = new NMREBARCHILDSIZE ();
+            OS.MoveMemory (lprbcs, lParam, NMREBARCHILDSIZE.sizeof);
+            if (lprbcs.uBand !is -1) {
+                CoolItem item = items [lprbcs.wID];
+                Control control = item.control;
+                if (control !is null) {
+                    int width = lprbcs.rcChild_right - lprbcs.rcChild_left;
+                    int height = lprbcs.rcChild_bottom - lprbcs.rcChild_top;
+                    control.setBounds (lprbcs.rcChild_left, lprbcs.rcChild_top, width, height);
+                }
+            }
+            break;
+        }
+        case OS.RBN_HEIGHTCHANGE: {
+            if (!ignoreResize) {
+                Point size = getSize ();
+                int border = getBorderWidth ();
+                int barHeight = OS.SendMessage (handle, OS.RB_GETBARHEIGHT, 0, 0);
+                if ((style & DWT.VERTICAL) !is 0) {
+                    setSize (barHeight + 2 * border, size.y);
+                } else {
+                    setSize (size.x, barHeight + 2 * border);
+                }
+            }
+            break;
+        }
+        case OS.RBN_CHEVRONPUSHED: {
+            NMREBARCHEVRON lpnm = new NMREBARCHEVRON ();
+            OS.MoveMemory (lpnm, lParam, NMREBARCHEVRON.sizeof);
+            CoolItem item = items [lpnm.wID];
+            if (item !is null) {
+                Event event = new Event();
+                event.detail = DWT.ARROW;
+                if ((style & DWT.VERTICAL) !is 0) {
+                    event.x = lpnm.right;
+                    event.y = lpnm.top;
+                } else {
+                    event.x = lpnm.left;
+                    event.y = lpnm.bottom;
+                }
+                item.postEvent (DWT.Selection, event);
+            }
+            break;
+        }
+        case OS.NM_CUSTOMDRAW: {
+            /*
+            * Bug in Windows.  On versions of Windows prior to XP,
+            * drawing the background color in NM_CUSTOMDRAW erases
+            * the separators.  The fix is to draw the background
+            * in WM_ERASEBKGND.
+            */
+            if (OS.COMCTL32_MAJOR < 6) break;
+            if (findBackgroundControl () !is null || (style & DWT.FLAT) !is 0) {
+                NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW ();
+                OS.MoveMemory (nmcd, lParam, NMCUSTOMDRAW.sizeof);
+                switch (nmcd.dwDrawStage) {
+                    case OS.CDDS_PREERASE:
+                        return new LRESULT (OS.CDRF_SKIPDEFAULT | OS.CDRF_NOTIFYPOSTERASE);
+                    case OS.CDDS_POSTERASE:
+                        drawBackground (nmcd.hdc);
+                        break;
+                }
+            }
+            break;
+        }
+    }
+    return super.wmNotifyChild (hdr, wParam, lParam);
+}
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/CoolItem.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,731 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.CoolItem;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+class CoolItem : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.MARGINS;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.REBARBANDINFO;
+import dwt.internal.win32.RECT;
+
+/**
+ * Instances of this class are selectable user interface
+ * objects that represent the dynamically positionable
+ * areas of a <code>CoolBar</code>.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>DROP_DOWN</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class CoolItem extends Item {
+    CoolBar parent;
+    Control control;
+    int id;
+    bool ideal, minimum;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>CoolBar</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#DROP_DOWN
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public CoolItem (CoolBar parent, int style) {
+    super (parent, style);
+    this.parent = parent;
+    parent.createItem (this, parent.getItemCount ());
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>CoolBar</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index at which to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#DROP_DOWN
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public CoolItem (CoolBar parent, int style, int index) {
+    super (parent, style);
+    this.parent = parent;
+    parent.createItem (this, index);
+}
+
+/**
+ * Adds the listener to the collection of listeners that will
+ * be notified when the control is selected by the user, by sending it one
+ * of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * If <code>widgetSelected</code> is called when the mouse is over
+ * the drop-down arrow (or 'chevron') portion of the cool item,
+ * the event object detail field contains the value <code>DWT.ARROW</code>,
+ * and the x and y fields in the event object represent the point at
+ * the bottom left of the chevron, where the menu should be popped up.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the control is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ *
+ * @since 2.0
+ */
+public void addSelectionListener(SelectionListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+/**
+ * Returns the preferred size of the receiver.
+ * <p>
+ * The <em>preferred size</em> of a <code>CoolItem</code> is the size that
+ * it would best be displayed at. The width hint and height hint arguments
+ * allow the caller to ask the instance questions such as "Given a particular
+ * width, how high does it need to be to show all of the contents?"
+ * To indicate that the caller does not wish to constrain a particular
+ * dimension, the constant <code>DWT.DEFAULT</code> is passed for the hint.
+ * </p>
+ *
+ * @param wHint the width hint (can be <code>DWT.DEFAULT</code>)
+ * @param hHint the height hint (can be <code>DWT.DEFAULT</code>)
+ * @return the preferred size
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Layout
+ * @see #getBounds
+ * @see #getSize
+ * @see Control#getBorderWidth
+ * @see Scrollable#computeTrim
+ * @see Scrollable#getClientArea
+ */
+public Point computeSize (int wHint, int hHint) {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return new Point (0, 0);
+    int width = wHint, height = hHint;
+    if (wHint is DWT.DEFAULT) width = 32;
+    if (hHint is DWT.DEFAULT) height = 32;
+    if ((parent.style & DWT.VERTICAL) !is 0) {
+        height += parent.getMargin (index);
+    } else {
+        width += parent.getMargin (index);
+    }
+    return new Point (width, height);
+}
+
+void destroyWidget () {
+    parent.destroyItem (this);
+    releaseHandle ();
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent.
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds () {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return new Rectangle (0, 0, 0, 0);
+    int hwnd = parent.handle;
+    RECT rect = new RECT ();
+    OS.SendMessage (hwnd, OS.RB_GETRECT, index, rect);
+    if (OS.COMCTL32_MAJOR >= 6) {
+        MARGINS margins = new MARGINS ();
+        OS.SendMessage (hwnd, OS.RB_GETBANDMARGINS, 0, margins);
+        rect.left -= margins.cxLeftWidth;
+        rect.right += margins.cxRightWidth;
+    }
+    if (!parent.isLastItemOfRow (index)) {
+        rect.right += (parent.style & DWT.FLAT) is 0 ? CoolBar.SEPARATOR_WIDTH : 0;
+    }
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+    if ((parent.style & DWT.VERTICAL) !is 0) {
+        return new Rectangle (rect.top, rect.left, height, width);
+    }
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+Rectangle getClientArea () {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return new Rectangle (0, 0, 0, 0);
+    int hwnd = parent.handle;
+    RECT insetRect = new RECT ();
+    OS.SendMessage (hwnd, OS.RB_GETBANDBORDERS, index, insetRect);
+    RECT rect = new RECT ();
+    OS.SendMessage (hwnd, OS.RB_GETRECT, index, rect);
+    int x = rect.left + insetRect.left;
+    int y = rect.top;
+    int width = rect.right - rect.left - insetRect.left;
+    int height = rect.bottom - rect.top;
+    if ((parent.style & DWT.FLAT) is 0) {
+        y += insetRect.top;
+        width -= insetRect.right;
+        height -= insetRect.top + insetRect.bottom;
+    }
+    if (index is 0) {
+        REBARBANDINFO rbBand = new REBARBANDINFO ();
+        rbBand.cbSize = REBARBANDINFO.sizeof;
+        rbBand.fMask = OS.RBBIM_HEADERSIZE;
+        OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand);
+        width = width - rbBand.cxHeader + 1;
+    }
+    return new Rectangle (x, y, Math.max (0, width), Math.max (0, height));
+}
+
+/**
+ * Returns the control that is associated with the receiver.
+ *
+ * @return the control that is contained by the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Control getControl () {
+    checkWidget ();
+    return control;
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>CoolBar</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public CoolBar getParent () {
+    checkWidget ();
+    return parent;
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+    id = -1;
+    control = null;
+}
+
+/**
+ * Sets the control that is associated with the receiver
+ * to the argument.
+ *
+ * @param control the new control that will be contained by the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setControl (Control control) {
+    checkWidget ();
+    if (control !is null) {
+        if (control.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT);
+        if (control.parent !is parent) error (DWT.ERROR_INVALID_PARENT);
+    }
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    if (this.control !is null && this.control.isDisposed ()) {
+        this.control = null;
+    }
+    Control oldControl = this.control, newControl = control;
+    int hwnd = parent.handle;
+    int hwndChild = newControl !is null ? control.topHandle () : 0;
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_CHILD;
+    rbBand.hwndChild = hwndChild;
+    this.control = newControl;
+
+    /*
+    * Feature in Windows.  When Windows sets the rebar band child,
+    * it makes the new child visible and hides the old child and
+    * moves the new child to the top of the Z-order.  The fix is
+    * to save and restore the visibility and Z-order.
+    */
+    int hwndAbove = 0;
+    if (newControl !is null) {
+        hwndAbove = OS.GetWindow (hwndChild, OS.GW_HWNDPREV);
+    }
+    bool hideNew = newControl !is null && !newControl.getVisible ();
+    bool showOld = oldControl !is null && oldControl.getVisible ();
+    OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand);
+    if (hideNew) newControl.setVisible (false);
+    if (showOld) oldControl.setVisible (true);
+    if (hwndAbove !is 0 && hwndAbove !is hwndChild) {
+        int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
+        SetWindowPos (hwndChild, hwndAbove, 0, 0, 0, 0, flags);
+    }
+}
+
+/**
+ * Returns a point describing the receiver's ideal size.
+ * The x coordinate of the result is the ideal width of the receiver.
+ * The y coordinate of the result is the ideal height of the receiver.
+ *
+ * @return the receiver's ideal size
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getPreferredSize () {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return new Point (0, 0);
+    int hwnd = parent.handle;
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_CHILDSIZE | OS.RBBIM_IDEALSIZE;
+    OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand);
+    int width = rbBand.cxIdeal + parent.getMargin (index);
+    if ((parent.style & DWT.VERTICAL) !is 0) {
+        return new Point (rbBand.cyMaxChild, width);
+    }
+    return new Point (width, rbBand.cyMaxChild);
+}
+
+/**
+ * Sets the receiver's ideal size to the point specified by the arguments.
+ *
+ * @param width the new ideal width for the receiver
+ * @param height the new ideal height for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setPreferredSize (int width, int height) {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    width = Math.max (0, width);
+    height = Math.max (0, height);
+    ideal = true;
+    int hwnd = parent.handle;
+    int cxIdeal, cyMaxChild;
+    if ((parent.style & DWT.VERTICAL) !is 0) {
+        cxIdeal = Math.max (0, height - parent.getMargin (index));
+        cyMaxChild = width;
+    } else {
+        cxIdeal = Math.max (0, width - parent.getMargin (index));
+        cyMaxChild = height;
+    }
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+
+    /* Get the child size fields first so we don't overwrite them. */
+    rbBand.fMask = OS.RBBIM_CHILDSIZE;
+    OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand);
+
+    /* Set the size fields we are currently modifying. */
+    rbBand.fMask = OS.RBBIM_CHILDSIZE | OS.RBBIM_IDEALSIZE;
+    rbBand.cxIdeal = cxIdeal;
+    rbBand.cyMaxChild = cyMaxChild;
+    if (!minimum) rbBand.cyMinChild = cyMaxChild;
+    OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand);
+}
+
+/**
+ * Sets the receiver's ideal size to the point specified by the argument.
+ *
+ * @param size the new ideal size for the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setPreferredSize (Point size) {
+    checkWidget ();
+    if (size is null) error(DWT.ERROR_NULL_ARGUMENT);
+    setPreferredSize (size.x, size.y);
+}
+
+/**
+ * Returns a point describing the receiver's size. The
+ * x coordinate of the result is the width of the receiver.
+ * The y coordinate of the result is the height of the
+ * receiver.
+ *
+ * @return the receiver's size
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getSize() {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) new Point (0, 0);
+    int hwnd = parent.handle;
+    RECT rect = new RECT ();
+    OS.SendMessage (hwnd, OS.RB_GETRECT, index, rect);
+    if (OS.COMCTL32_MAJOR >= 6) {
+        MARGINS margins = new MARGINS ();
+        OS.SendMessage (hwnd, OS.RB_GETBANDMARGINS, 0, margins);
+        rect.left -= margins.cxLeftWidth;
+        rect.right += margins.cxRightWidth;
+    }
+    if (!parent.isLastItemOfRow (index)) {
+        rect.right += (parent.style & DWT.FLAT) is 0 ? CoolBar.SEPARATOR_WIDTH : 0;
+    }
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+    if ((parent.style & DWT.VERTICAL) !is 0) {
+        return new Point (height, width);
+    }
+    return new Point (width, height);
+}
+
+/**
+ * Sets the receiver's size to the point specified by the arguments.
+ * <p>
+ * Note: Attempting to set the width or height of the
+ * receiver to a negative number will cause that
+ * value to be set to zero instead.
+ * </p>
+ *
+ * @param width the new width for the receiver
+ * @param height the new height for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSize (int width, int height) {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    width = Math.max (0, width);
+    height = Math.max (0, height);
+    int hwnd = parent.handle;
+    int cx, cyChild, cxIdeal;
+    if ((parent.style & DWT.VERTICAL) !is 0) {
+        cx = height;
+        cyChild = width;
+        cxIdeal = Math.max (0, height - parent.getMargin (index));
+    } else {
+        cx = width;
+        cyChild = height;
+        cxIdeal = Math.max (0, width - parent.getMargin (index));
+    }
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+
+    /* Get the child size fields first so we don't overwrite them. */
+    rbBand.fMask = OS.RBBIM_CHILDSIZE | OS.RBBIM_IDEALSIZE;
+    OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand);
+
+    /* Set the size fields we are currently modifying. */
+    if (!ideal) rbBand.cxIdeal = cxIdeal;
+    if (!minimum) rbBand.cyMinChild = cyChild;
+    rbBand.cyChild = cyChild;
+
+    /*
+    * Do not set the size for the last item on the row.
+    */
+    if (!parent.isLastItemOfRow (index)) {
+        if (OS.COMCTL32_MAJOR >= 6) {
+            MARGINS margins = new MARGINS ();
+            OS.SendMessage (hwnd, OS.RB_GETBANDMARGINS, 0, margins);
+            cx -= margins.cxLeftWidth + margins.cxRightWidth;
+        }
+        int separator = (parent.style & DWT.FLAT) is 0 ? CoolBar.SEPARATOR_WIDTH : 0;
+        rbBand.cx = cx - separator;
+        rbBand.fMask |= OS.RBBIM_SIZE;
+    }
+    OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand);
+}
+
+/**
+ * Sets the receiver's size to the point specified by the argument.
+ * <p>
+ * Note: Attempting to set the width or height of the
+ * receiver to a negative number will cause them to be
+ * set to zero instead.
+ * </p>
+ *
+ * @param size the new size for the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSize (Point size) {
+    if (size is null) error(DWT.ERROR_NULL_ARGUMENT);
+    setSize (size.x, size.y);
+}
+
+/**
+ * Returns the minimum size that the cool item can
+ * be resized to using the cool item's gripper.
+ *
+ * @return a point containing the minimum width and height of the cool item, in pixels
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public Point getMinimumSize () {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return new Point (0, 0);
+    int hwnd = parent.handle;
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_CHILDSIZE;
+    OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand);
+    if ((parent.style & DWT.VERTICAL) !is 0) {
+        return new Point (rbBand.cyMinChild, rbBand.cxMinChild);
+    }
+    return new Point (rbBand.cxMinChild, rbBand.cyMinChild);
+}
+
+/**
+ * Sets the minimum size that the cool item can be resized to
+ * using the cool item's gripper, to the point specified by the arguments.
+ *
+ * @param width the minimum width of the cool item, in pixels
+ * @param height the minimum height of the cool item, in pixels
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public void setMinimumSize (int width, int height) {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    width = Math.max (0, width);
+    height = Math.max (0, height);
+    minimum = true;
+    int hwnd = parent.handle;
+    int cxMinChild, cyMinChild;
+    if ((parent.style & DWT.VERTICAL) !is 0) {
+        cxMinChild = height;
+        cyMinChild = width;
+    } else {
+        cxMinChild = width;
+        cyMinChild = height;
+    }
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+
+    /* Get the child size fields first so we don't overwrite them. */
+    rbBand.fMask = OS.RBBIM_CHILDSIZE;
+    OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand);
+
+    /* Set the size fields we are currently modifying. */
+    rbBand.cxMinChild = cxMinChild;
+    rbBand.cyMinChild = cyMinChild;
+    OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand);
+}
+
+/**
+ * Sets the minimum size that the cool item can be resized to
+ * using the cool item's gripper, to the point specified by the argument.
+ *
+ * @param size a point representing the minimum width and height of the cool item, in pixels
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public void setMinimumSize (Point size) {
+    checkWidget ();
+    if (size is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setMinimumSize (size.x, size.y);
+}
+
+bool getWrap() {
+    int index = parent.indexOf (this);
+    int hwnd = parent.handle;
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_STYLE;
+    OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand);
+    return (rbBand.fStyle & OS.RBBS_BREAK) !is 0;
+}
+
+void setWrap(bool wrap) {
+    int index = parent.indexOf (this);
+    int hwnd = parent.handle;
+    REBARBANDINFO rbBand = new REBARBANDINFO ();
+    rbBand.cbSize = REBARBANDINFO.sizeof;
+    rbBand.fMask = OS.RBBIM_STYLE;
+    OS.SendMessage (hwnd, OS.RB_GETBANDINFO, index, rbBand);
+    if (wrap) {
+        rbBand.fStyle |= OS.RBBS_BREAK;
+    } else {
+        rbBand.fStyle &= ~OS.RBBS_BREAK;
+    }
+    OS.SendMessage (hwnd, OS.RB_SETBANDINFO, index, rbBand);
+}
+
+/**
+ * Removes the listener from the collection of listeners that
+ * will be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ *
+ * @since 2.0
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/DateTime.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,796 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.DateTime;
+
+import dwt.widgets.Composite;
+
+class DateTime : Composite {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Point;
+import dwt.internal.win32.INITCOMMONCONTROLSEX;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SYSTEMTIME;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WNDCLASS;
+
+//TODO - features not yet implemented: read-only, drop-down calendar for date
+//TODO - font, colors, background image not yet implemented (works on some platforms)
+
+/**
+ * Instances of this class are selectable user interface
+ * objects that allow the user to enter and modify date
+ * or time values.
+ * <p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to add children to it, or set a layout on it.
+ * </p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>DATE, TIME, CALENDAR, SHORT, MEDIUM, LONG</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles DATE, TIME, or CALENDAR may be specified,
+ * and only one of the styles SHORT, MEDIUM, or LONG may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @since 3.3
+ */
+
+public class DateTime extends Composite {
+    static final int DateTimeProc;
+    static final TCHAR DateTimeClass = new TCHAR (0, OS.DATETIMEPICK_CLASS, true);
+    static final int CalendarProc;
+    static final TCHAR CalendarClass = new TCHAR (0, OS.MONTHCAL_CLASS, true);
+    static {
+        INITCOMMONCONTROLSEX icex = new INITCOMMONCONTROLSEX ();
+        icex.dwSize = INITCOMMONCONTROLSEX.sizeof;
+        icex.dwICC = OS.ICC_DATE_CLASSES;
+        OS.InitCommonControlsEx (icex);
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, DateTimeClass, lpWndClass);
+        DateTimeProc = lpWndClass.lpfnWndProc;
+        OS.GetClassInfo (0, CalendarClass, lpWndClass);
+        CalendarProc = lpWndClass.lpfnWndProc;
+    }
+    static final int MARGIN = 4;
+    static final int MAX_DIGIT = 9;
+    static final int MAX_DAY = 31;
+    static final int MAX_12HOUR = 12;
+    static final int MAX_24HOUR = 24;
+    static final int MAX_MINUTE = 60;
+    static final int MONTH_DAY_YEAR = 0;
+    static final int DAY_MONTH_YEAR = 1;
+    static final int YEAR_MONTH_DAY = 2;
+    static final char SINGLE_QUOTE = '\''; //$NON-NLS-1$ short date format may include quoted text
+    static final char DAY_FORMAT_CONSTANT = 'd'; //$NON-NLS-1$ 1-4 lowercase 'd's represent day
+    static final char MONTH_FORMAT_CONSTANT = 'M'; //$NON-NLS-1$ 1-4 uppercase 'M's represent month
+    static final char YEAR_FORMAT_CONSTANT = 'y'; //$NON-NLS-1$ 1-5 lowercase 'y's represent year
+    static final char HOURS_FORMAT_CONSTANT = 'h'; //$NON-NLS-1$ 1-2 upper or lowercase 'h's represent hours
+    static final char MINUTES_FORMAT_CONSTANT = 'm'; //$NON-NLS-1$ 1-2 lowercase 'm's represent minutes
+    static final char SECONDS_FORMAT_CONSTANT = 's'; //$NON-NLS-1$ 1-2 lowercase 's's represent seconds
+    static final char AMPM_FORMAT_CONSTANT = 't'; //$NON-NLS-1$ 1-2 lowercase 't's represent am/pm
+    static final int[] MONTH_NAMES = new int[] {OS.LOCALE_SMONTHNAME1, OS.LOCALE_SMONTHNAME2, OS.LOCALE_SMONTHNAME3, OS.LOCALE_SMONTHNAME4, OS.LOCALE_SMONTHNAME5, OS.LOCALE_SMONTHNAME6, OS.LOCALE_SMONTHNAME7, OS.LOCALE_SMONTHNAME8, OS.LOCALE_SMONTHNAME9, OS.LOCALE_SMONTHNAME10, OS.LOCALE_SMONTHNAME11, OS.LOCALE_SMONTHNAME12};
+
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#DATE
+ * @see DWT#TIME
+ * @see DWT#CALENDAR
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public DateTime (Composite parent, int style) {
+    super (parent, checkStyle (style));
+    if ((this.style & DWT.SHORT) !is 0) {
+        String buffer = ((this.style & DWT.DATE) !is 0) ? getCustomShortDateFormat() : getCustomShortTimeFormat();
+        TCHAR lpszFormat = new TCHAR (0, buffer, true);
+        OS.SendMessage (handle, OS.DTM_SETFORMAT, 0, lpszFormat);
+    }
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the user changes the control's value.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection, typedListener);
+    addListener (DWT.DefaultSelection, typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.CallWindowProc (windowProc (), hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    /*
+    * Even though it is legal to create this widget
+    * with scroll bars, they serve no useful purpose
+    * because they do not automatically scroll the
+    * widget's client area.  The fix is to clear
+    * the DWT style.
+    */
+    style &= ~(DWT.H_SCROLL | DWT.V_SCROLL);
+    style = checkBits (style, DWT.DATE, DWT.TIME, DWT.CALENDAR, 0, 0, 0);
+    return checkBits (style, DWT.MEDIUM, DWT.SHORT, DWT.LONG, 0, 0, 0);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = 0, height = 0;
+    if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) {
+        if ((style & DWT.CALENDAR) !is 0) {
+            RECT rect = new RECT ();
+            OS.SendMessage(handle, OS.MCM_GETMINREQRECT, 0, rect);
+            width = rect.right;
+            height = rect.bottom;
+        } else {
+            TCHAR buffer = new TCHAR (getCodePage (), 128);
+            int newFont, oldFont = 0;
+            int hDC = OS.GetDC (handle);
+            newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+            if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+            RECT rect = new RECT ();
+            int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_NOPREFIX;
+            SYSTEMTIME systime = new SYSTEMTIME ();
+            if ((style & DWT.DATE) !is 0) {
+                /* Determine the widest/tallest year string. */
+                systime.wMonth = 1;
+                systime.wDay = 1;
+                int widest = 0, secondWidest = 0, thirdWidest = 0;
+                for (int i = 0; i <= MAX_DIGIT; i++) {
+                    systime.wYear = (short) (2000 + i); // year 2000 + i is guaranteed to exist
+                    int size = OS.GetDateFormat(OS.LOCALE_USER_DEFAULT, OS.DATE_SHORTDATE, systime, null, buffer, buffer.length ());
+                    if (size is 0) {
+                        buffer = new TCHAR (getCodePage (), size);
+                        OS.GetDateFormat(OS.LOCALE_USER_DEFAULT, OS.DATE_SHORTDATE, systime, null, buffer, buffer.length ());
+                    }
+                    rect.left = rect.top = rect.right = rect.bottom = 0;
+                    OS.DrawText (hDC, buffer, size, rect, flags);
+                    if (rect.right - rect.left >= width) {
+                        width = rect.right - rect.left;
+                        thirdWidest = secondWidest;
+                        secondWidest = widest;
+                        widest = i;
+                    }
+                    height = Math.max(height, rect.bottom - rect.top);
+                }
+                if (widest > 1) widest = widest * 1000 + widest * 100 + widest * 10 + widest;
+                else if (secondWidest > 1) widest = secondWidest * 1000 + widest * 100 + widest * 10 + widest;
+                else widest = thirdWidest * 1000 + widest * 100 + widest * 10 + widest;
+                systime.wYear = (short) widest;
+
+                /* Determine the widest/tallest month name string. */
+                width = widest = 0;
+                for (short i = 0; i < MONTH_NAMES.length; i++) {
+                    int name = MONTH_NAMES [i];
+                    int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, name, buffer, buffer.length ());
+                    if (size is 0) {
+                        buffer = new TCHAR (getCodePage (), size);
+                        OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, name, buffer, buffer.length ());
+                    }
+                    rect.left = rect.top = rect.right = rect.bottom = 0;
+                    OS.DrawText (hDC, buffer, size, rect, flags);
+                    if (rect.right - rect.left > width) {
+                        width = rect.right - rect.left;
+                        widest = i;
+                    }
+                    height = Math.max(height, rect.bottom - rect.top);
+                }
+                systime.wMonth = (short) (widest + 1);
+
+                /* Determine the widest/tallest date string in the widest month of the widest year. */
+                int dwFlags = ((style & DWT.MEDIUM) !is 0) ? OS.DATE_SHORTDATE : ((style & DWT.SHORT) !is 0) ? OS.DATE_YEARMONTH : OS.DATE_LONGDATE;
+                width = 0;
+                for (short i = 1; i <= MAX_DAY; i++) {
+                    systime.wDay = i;
+                    int size = OS.GetDateFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ());
+                    if (size is 0) {
+                        buffer = new TCHAR (getCodePage (), size);
+                        OS.GetDateFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ());
+                    }
+                    rect.left = rect.top = rect.right = rect.bottom = 0;
+                    OS.DrawText (hDC, buffer, size, rect, flags);
+                    width = Math.max(width, rect.right - rect.left);
+                    height = Math.max(height, rect.bottom - rect.top);
+                    if ((style & DWT.SHORT) !is 0) break;
+                }
+            } else if ((style & DWT.TIME) !is 0) {
+                /* Determine the widest/tallest hour string. This code allows for the possibility of ligatures. */
+                int dwFlags = ((style & DWT.SHORT) !is 0) ? OS.TIME_NOSECONDS : 0;
+                short widest = 0;
+                int max = is24HourTime () ? MAX_24HOUR : MAX_12HOUR;
+                for (short i = 0; i < max; i++) {
+                    systime.wHour = i;
+                    int size = OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ());
+                    if (size is 0) {
+                        buffer = new TCHAR (getCodePage (), size);
+                        OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ());
+                    }
+                    rect.left = rect.top = rect.right = rect.bottom = 0;
+                    OS.DrawText (hDC, buffer, size, rect, flags);
+                    if (rect.right - rect.left > width) {
+                        width = rect.right - rect.left;
+                        widest = i;
+                    }
+                    height = Math.max(height, rect.bottom - rect.top);
+                }
+                systime.wHour = widest;
+
+                /* Determine the widest/tallest minute and second string. */
+                width = widest = 0;
+                for (short i = 0; i < MAX_MINUTE; i++) {
+                    systime.wMinute = i;
+                    int size = OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ());
+                    if (size is 0) {
+                        buffer = new TCHAR (getCodePage (), size);
+                        OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ());
+                    }
+                    rect.left = rect.top = rect.right = rect.bottom = 0;
+                    OS.DrawText (hDC, buffer, size, rect, flags);
+                    if (rect.right - rect.left > width) {
+                        width = rect.right - rect.left;
+                        widest = i;
+                    }
+                    height = Math.max(height, rect.bottom - rect.top);
+                }
+                systime.wMinute = widest;
+                systime.wSecond = widest;
+
+                /* Determine the widest/tallest time string for the widest hour, widest minute, and if applicable, widest second. */
+                int size = OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ());
+                if (size is 0) {
+                    buffer = new TCHAR (getCodePage (), size);
+                    OS.GetTimeFormat(OS.LOCALE_USER_DEFAULT, dwFlags, systime, null, buffer, buffer.length ());
+                }
+                rect.left = rect.top = rect.right = rect.bottom = 0;
+                OS.DrawText (hDC, buffer, size, rect, flags);
+                width = rect.right - rect.left;
+                height = Math.max(height, rect.bottom - rect.top);
+            }
+            if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+            OS.ReleaseDC (handle, hDC);
+            int upDownWidth = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+            width += upDownWidth + MARGIN;
+            int upDownHeight = OS.GetSystemMetrics (OS.SM_CYVSCROLL);
+            // TODO: On Vista, can send DTM_GETDATETIMEPICKERINFO to ask the Edit control what its margins are
+            if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) upDownHeight += 7;
+            height = Math.max (height, upDownHeight);
+        }
+    }
+    if (width is 0) width = DEFAULT_WIDTH;
+    if (height is 0) height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    int border = getBorderWidth ();
+    width += border * 2;
+    height += border * 2;
+    return new Point (width, height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state &= ~(CANVAS | THEME_BACKGROUND);
+}
+
+int defaultBackground () {
+    return OS.GetSysColor (OS.COLOR_WINDOW);
+}
+
+String getComputeSizeString () {
+    // TODO: Not currently used but might need for WinCE
+    if ((style & DWT.DATE) !is 0) {
+        if ((style & DWT.SHORT) !is 0) return getCustomShortDateFormat ();
+        if ((style & DWT.MEDIUM) !is 0) return getShortDateFormat ();
+        if ((style & DWT.LONG) !is 0) return getLongDateFormat ();
+    }
+    if ((style & DWT.TIME) !is 0) {
+        if ((style & DWT.SHORT) !is 0) return getCustomShortTimeFormat ();
+        return getTimeFormat ();
+    }
+    return "";
+}
+
+String getCustomShortDateFormat () {
+    if (true) {
+        TCHAR tchar = new TCHAR (getCodePage (), 80);
+        int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_SYEARMONTH, tchar, 80);
+        return size !is 0 ? tchar.toString (0, size - 1) : "M/yyyy"; //$NON-NLS-1$
+    }
+
+    //TODO: Not currently used, but may need for WinCE (or if numeric short date is required)
+    StringBuffer buffer = new StringBuffer (getShortDateFormat ());
+    int length = buffer.length ();
+    bool inQuotes = false;
+    int start = 0, end = 0;
+    while (start < length) {
+        char ch = buffer.charAt (start);
+        if (ch is SINGLE_QUOTE) inQuotes = !inQuotes;
+        else if (ch is DAY_FORMAT_CONSTANT && !inQuotes) {
+            end = start + 1;
+            while (end < length && buffer.charAt (end) is DAY_FORMAT_CONSTANT) end++;
+            int ordering = getShortDateFormatOrdering ();
+            switch (ordering) {
+            case MONTH_DAY_YEAR:
+                // skip the following separator
+                while (end < length && buffer.charAt (end) !is YEAR_FORMAT_CONSTANT) end++;
+                break;
+            case DAY_MONTH_YEAR:
+                // skip the following separator
+                while (end < length && buffer.charAt (end) !is MONTH_FORMAT_CONSTANT) end++;
+                break;
+            case YEAR_MONTH_DAY:
+                // skip the preceding separator
+                while (start > 0 && buffer.charAt (start) !is MONTH_FORMAT_CONSTANT) start--;
+                break;
+            }
+            break;
+        }
+        start++;
+    }
+    if (start < end) buffer.delete (start, end);
+    return buffer.toString ();
+}
+
+String getCustomShortTimeFormat () {
+    StringBuffer buffer = new StringBuffer (getTimeFormat ());
+    int length = buffer.length ();
+    bool inQuotes = false;
+    int start = 0, end = 0;
+    while (start < length) {
+        char ch = buffer.charAt (start);
+        if (ch is SINGLE_QUOTE) inQuotes = !inQuotes;
+        else if (ch is SECONDS_FORMAT_CONSTANT && !inQuotes) {
+            end = start + 1;
+            while (end < length && buffer.charAt (end) is SECONDS_FORMAT_CONSTANT) end++;
+            // skip the preceding separator
+            while (start > 0 && buffer.charAt (start) !is MINUTES_FORMAT_CONSTANT) start--;
+            start++;
+            break;
+        }
+        start++;
+    }
+    if (start < end) buffer.delete (start, end);
+    return buffer.toString ();
+}
+
+String getLongDateFormat () {
+    //TODO: Not currently used, but may need for WinCE
+    TCHAR tchar = new TCHAR (getCodePage (), 80);
+    int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_SLONGDATE, tchar, 80);
+    return size > 0 ? tchar.toString (0, size - 1) : "dddd, MMMM dd, yyyy"; //$NON-NLS-1$
+}
+
+String getShortDateFormat () {
+    //TODO: Not currently used, but may need for WinCE
+    TCHAR tchar = new TCHAR (getCodePage (), 80);
+    //TODO: May need to OR with LOCALE_ICENTURY
+    int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_SSHORTDATE, tchar, 80);
+    return size > 0 ? tchar.toString (0, size - 1) : "M/d/yyyy"; //$NON-NLS-1$
+}
+
+int getShortDateFormatOrdering () {
+    //TODO: Not currently used, but may need for WinCE
+    TCHAR tchar = new TCHAR (getCodePage (), 4);
+    int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_IDATE, tchar, 4);
+    if (size > 0) {
+        String number = tchar.toString (0, size - 1);
+        return Integer.parseInt (number);
+    }
+    return 0;
+}
+
+String getTimeFormat () {
+    TCHAR tchar = new TCHAR (getCodePage (), 80);
+    int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_STIMEFORMAT, tchar, 80);
+    return size > 0 ? tchar.toString (0, size - 1) : "h:mm:ss tt"; //$NON-NLS-1$
+}
+
+bool is24HourTime () {
+    TCHAR tchar = new TCHAR (getCodePage (), 4);
+    int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_ITIME, tchar, 4);
+    if (size > 0) {
+        String number = tchar.toString (0, size - 1);
+        return Integer.parseInt (number) !is 0;
+    }
+    return true;
+}
+
+/**
+ * Returns the receiver's date, or day of the month.
+ * <p>
+ * The first day of the month is 1, and the last day depends on the month and year.
+ * </p>
+ *
+ * @return a positive integer beginning with 1
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getDay () {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    return systime.wDay;
+}
+
+/**
+ * Returns the receiver's hours.
+ * <p>
+ * Hours is an integer between 0 and 23.
+ * </p>
+ *
+ * @return an integer between 0 and 23
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getHours () {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    return systime.wHour;
+}
+
+/**
+ * Returns the receiver's minutes.
+ * <p>
+ * Minutes is an integer between 0 and 59.
+ * </p>
+ *
+ * @return an integer between 0 and 59
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMinutes () {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    return systime.wMinute;
+}
+
+/**
+ * Returns the receiver's month.
+ * <p>
+ * The first month of the year is 0, and the last month is 11.
+ * </p>
+ *
+ * @return an integer between 0 and 11
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMonth () {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    return systime.wMonth - 1;
+}
+
+String getNameText () {
+    return "DateTime";
+}
+
+/**
+ * Returns the receiver's seconds.
+ * <p>
+ * Seconds is an integer between 0 and 59.
+ * </p>
+ *
+ * @return an integer between 0 and 59
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSeconds () {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    return systime.wSecond;
+}
+
+/**
+ * Returns the receiver's year.
+ * <p>
+ * The first year is 1752 and the last year is 9999.
+ * </p>
+ *
+ * @return an integer between 1752 and 9999
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getYear () {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    return systime.wYear;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection, listener);
+}
+
+/**
+ * Sets the receiver's date, or day of the month, to the specified day.
+ * <p>
+ * The first day of the month is 1, and the last day depends on the month and year.
+ * </p>
+ *
+ * @param day a positive integer beginning with 1
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setDay (int day) {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
+    systime.wDay = (short)day;
+    OS.SendMessage (handle, msg, 0, systime);
+}
+
+/**
+ * Sets the receiver's hours.
+ * <p>
+ * Hours is an integer between 0 and 23.
+ * </p>
+ *
+ * @param hours an integer between 0 and 23
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setHours (int hours) {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
+    systime.wHour = (short)hours;
+    OS.SendMessage (handle, msg, 0, systime);
+}
+
+/**
+ * Sets the receiver's minutes.
+ * <p>
+ * Minutes is an integer between 0 and 59.
+ * </p>
+ *
+ * @param minutes an integer between 0 and 59
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinutes (int minutes) {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
+    systime.wMinute = (short)minutes;
+    OS.SendMessage (handle, msg, 0, systime);
+}
+
+/**
+ * Sets the receiver's month.
+ * <p>
+ * The first month of the year is 0, and the last month is 11.
+ * </p>
+ *
+ * @param month an integer between 0 and 11
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMonth (int month) {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
+    systime.wMonth = (short)(month + 1);
+    OS.SendMessage (handle, msg, 0, systime);
+}
+
+/**
+ * Sets the receiver's seconds.
+ * <p>
+ * Seconds is an integer between 0 and 59.
+ * </p>
+ *
+ * @param seconds an integer between 0 and 59
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSeconds (int seconds) {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
+    systime.wSecond = (short)seconds;
+    OS.SendMessage (handle, msg, 0, systime);
+}
+
+/**
+ * Sets the receiver's year.
+ * <p>
+ * The first year is 1752 and the last year is 9999.
+ * </p>
+ *
+ * @param year an integer between 1752 and 9999
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setYear (int year) {
+    checkWidget ();
+    SYSTEMTIME systime = new SYSTEMTIME ();
+    int msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
+    OS.SendMessage (handle, msg, 0, systime);
+    msg = (style & DWT.CALENDAR) !is 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
+    systime.wYear = (short)year;
+    OS.SendMessage (handle, msg, 0, systime);
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.WS_TABSTOP;
+    if ((style & DWT.CALENDAR) !is 0) return bits | OS.MCS_NOTODAY;
+    /*
+    * Bug in Windows: When WS_CLIPCHILDREN is set in a
+    * Date and Time Picker, the widget draws on top of
+    * the updown control. The fix is to clear the bits.
+    */
+    bits &= ~OS.WS_CLIPCHILDREN;
+    if ((style & DWT.TIME) !is 0) bits |= OS.DTS_TIMEFORMAT;
+    if ((style & DWT.DATE) !is 0) bits |= ((style & DWT.MEDIUM) !is 0 ? OS.DTS_SHORTDATECENTURYFORMAT : OS.DTS_LONGDATEFORMAT) | OS.DTS_UPDOWN;
+    return bits;
+}
+
+TCHAR windowClass () {
+    return (style & DWT.CALENDAR) !is 0 ? CalendarClass : DateTimeClass;
+}
+
+int windowProc () {
+    return (style & DWT.CALENDAR) !is 0 ? CalendarProc : DateTimeProc;
+}
+
+LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
+    switch (hdr.code) {
+        case OS.MCN_SELCHANGE: //SENT WHEN YOU SET IT?
+        case OS.DTN_DATETIMECHANGE:
+            sendEvent (DWT.Selection);
+            break;
+    }
+    return super.wmNotifyChild (hdr, wParam, lParam);
+}
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Decorations.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1824 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Decorations;
+
+import dwt.widgets.Canvas;
+class Decorations : Canvas {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Image;
+import dwt.graphics.ImageData;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.ACCEL;
+import dwt.internal.win32.CREATESTRUCT;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MENUITEMINFO;
+import dwt.internal.win32.MSG;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.STARTUPINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WINDOWPLACEMENT;
+import dwt.internal.win32.WINDOWPOS;
+
+/**
+ * Instances of this class provide the appearance and
+ * behavior of <code>Shells</code>, but are not top
+ * level shells or dialogs. Class <code>Shell</code>
+ * shares a significant amount of code with this class,
+ * and is a subclass.
+ * <p>
+ * IMPORTANT: This class was intended to be abstract and
+ * should <em>never</em> be referenced or instantiated.
+ * Instead, the class <code>Shell</code> should be used.
+ * </p>
+ * <p>
+ * Instances are always displayed in one of the maximized,
+ * minimized or normal states:
+ * <ul>
+ * <li>
+ * When an instance is marked as <em>maximized</em>, the
+ * window manager will typically resize it to fill the
+ * entire visible area of the display, and the instance
+ * is usually put in a state where it can not be resized
+ * (even if it has style <code>RESIZE</code>) until it is
+ * no longer maximized.
+ * </li><li>
+ * When an instance is in the <em>normal</em> state (neither
+ * maximized or minimized), its appearance is controlled by
+ * the style constants which were specified when it was created
+ * and the restrictions of the window manager (see below).
+ * </li><li>
+ * When an instance has been marked as <em>minimized</em>,
+ * its contents (client area) will usually not be visible,
+ * and depending on the window manager, it may be
+ * "iconified" (that is, replaced on the desktop by a small
+ * simplified representation of itself), relocated to a
+ * distinguished area of the screen, or hidden. Combinations
+ * of these changes are also possible.
+ * </li>
+ * </ul>
+ * </p>
+ * Note: The styles supported by this class must be treated
+ * as <em>HINT</em>s, since the window manager for the
+ * desktop on which the instance is visible has ultimate
+ * control over the appearance and behavior of decorations.
+ * For example, some window managers only support resizable
+ * windows and will always assume the RESIZE style, even if
+ * it is not set.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>BORDER, CLOSE, MIN, MAX, NO_TRIM, RESIZE, TITLE, ON_TOP, TOOL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * Class <code>DWT</code> provides two "convenience constants"
+ * for the most commonly required style combinations:
+ * <dl>
+ * <dt><code>SHELL_TRIM</code></dt>
+ * <dd>
+ * the result of combining the constants which are required
+ * to produce a typical application top level shell: (that
+ * is, <code>CLOSE | TITLE | MIN | MAX | RESIZE</code>)
+ * </dd>
+ * <dt><code>DIALOG_TRIM</code></dt>
+ * <dd>
+ * the result of combining the constants which are required
+ * to produce a typical application dialog shell: (that
+ * is, <code>TITLE | CLOSE | BORDER</code>)
+ * </dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ *
+ * @see #getMinimized
+ * @see #getMaximized
+ * @see Shell
+ * @see DWT
+ */
+
+public class Decorations extends Canvas {
+    Image image, smallImage, largeImage;
+    Image [] images;
+    Menu menuBar;
+    Menu [] menus;
+    Control savedFocus;
+    Button defaultButton, saveDefault;
+    int swFlags, hAccel, nAccel;
+    bool moved, resized, opened;
+    int oldX = OS.CW_USEDEFAULT, oldY = OS.CW_USEDEFAULT;
+    int oldWidth = OS.CW_USEDEFAULT, oldHeight = OS.CW_USEDEFAULT;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+Decorations () {
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#BORDER
+ * @see DWT#CLOSE
+ * @see DWT#MIN
+ * @see DWT#MAX
+ * @see DWT#RESIZE
+ * @see DWT#TITLE
+ * @see DWT#NO_TRIM
+ * @see DWT#SHELL_TRIM
+ * @see DWT#DIALOG_TRIM
+ * @see DWT#ON_TOP
+ * @see DWT#TOOL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Decorations (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+void _setMaximized (bool maximized) {
+    swFlags = maximized ? OS.SW_SHOWMAXIMIZED : OS.SW_RESTORE;
+    if (OS.IsWinCE) {
+        /*
+        * Note: WinCE does not support SW_SHOWMAXIMIZED and SW_RESTORE. The
+        * workaround is to resize the window to fit the parent client area.
+        */
+        if (maximized) {
+            RECT rect = new RECT ();
+            OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0);
+            int width = rect.right - rect.left, height = rect.bottom - rect.top;
+            if (OS.IsPPC) {
+                /* Leave space for the menu bar */
+                if (menuBar !is null) {
+                    int hwndCB = menuBar.hwndCB;
+                    RECT rectCB = new RECT ();
+                    OS.GetWindowRect (hwndCB, rectCB);
+                    height -= rectCB.bottom - rectCB.top;
+                }
+            }
+            int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE;
+            SetWindowPos (handle, 0, rect.left, rect.top, width, height, flags);
+        }
+    } else {
+        if (!OS.IsWindowVisible (handle)) return;
+        if (maximized is OS.IsZoomed (handle)) return;
+        OS.ShowWindow (handle, swFlags);
+        OS.UpdateWindow (handle);
+    }
+}
+
+void _setMinimized (bool minimized) {
+    if (OS.IsWinCE) return;
+    swFlags = minimized ? OS.SW_SHOWMINNOACTIVE : OS.SW_RESTORE;
+    if (!OS.IsWindowVisible (handle)) return;
+    if (minimized is OS.IsIconic (handle)) return;
+    int flags = swFlags;
+    if (flags is OS.SW_SHOWMINNOACTIVE && handle is OS.GetActiveWindow ()) {
+        flags = OS.SW_MINIMIZE;
+    }
+    OS.ShowWindow (handle, flags);
+    OS.UpdateWindow (handle);
+}
+
+void addMenu (Menu menu) {
+    if (menus is null) menus = new Menu [4];
+    for (int i=0; i<menus.length; i++) {
+        if (menus [i] is null) {
+            menus [i] = menu;
+            return;
+        }
+    }
+    Menu [] newMenus = new Menu [menus.length + 4];
+    newMenus [menus.length] = menu;
+    System.arraycopy (menus, 0, newMenus, 0, menus.length);
+    menus = newMenus;
+}
+
+void bringToTop () {
+    /*
+    * This code is intentionally commented.  On some platforms,
+    * the ON_TOP style creates a shell that will stay on top
+    * of every other shell on the desktop.  Using SetWindowPos ()
+    * with HWND_TOP caused problems on Windows 98 so this code is
+    * commented out until this functionality is specified and
+    * the problems are fixed.
+    */
+//  if ((style & DWT.ON_TOP) !is 0) {
+//      int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
+//      OS.SetWindowPos (handle, OS.HWND_TOP, 0, 0, 0, 0, flags);
+//  } else {
+        OS.BringWindowToTop (handle);
+        // widget could be disposed at this point
+//  }
+}
+
+static int checkStyle (int style) {
+    if ((style & DWT.NO_TRIM) !is 0) {
+        style &= ~(DWT.CLOSE | DWT.TITLE | DWT.MIN | DWT.MAX | DWT.RESIZE | DWT.BORDER);
+    }
+    if (OS.IsWinCE) {
+        /*
+        * Feature in WinCE PPC.  WS_MINIMIZEBOX or WS_MAXIMIZEBOX
+        * are not supposed to be used.  If they are, the result
+        * is a button which does not repaint correctly.  The fix
+        * is to remove this style.
+        */
+        if ((style & DWT.MIN) !is 0) style &= ~DWT.MIN;
+        if ((style & DWT.MAX) !is 0) style &= ~DWT.MAX;
+        return style;
+    }
+    if ((style & (DWT.MENU | DWT.MIN | DWT.MAX | DWT.CLOSE)) !is 0) {
+        style |= DWT.TITLE;
+    }
+
+    /*
+    * If either WS_MINIMIZEBOX or WS_MAXIMIZEBOX are set,
+    * we must also set WS_SYSMENU or the buttons will not
+    * appear.
+    */
+    if ((style & (DWT.MIN | DWT.MAX)) !is 0) style |= DWT.CLOSE;
+
+    /*
+    * Both WS_SYSMENU and WS_CAPTION must be set in order
+    * to for the system menu to appear.
+    */
+    if ((style & DWT.CLOSE) !is 0) style |= DWT.TITLE;
+
+    /*
+    * Bug in Windows.  The WS_CAPTION style must be
+    * set when the window is resizable or it does not
+    * draw properly.
+    */
+    /*
+    * This code is intentionally commented.  It seems
+    * that this problem originally in Windows 3.11,
+    * has been fixed in later versions.  Because the
+    * exact nature of the drawing problem is unknown,
+    * keep the commented code around in case it comes
+    * back.
+    */
+//  if ((style & DWT.RESIZE) !is 0) style |= DWT.TITLE;
+
+    return style;
+}
+
+void checkBorder () {
+    /* Do nothing */
+}
+
+void checkOpened () {
+    if (!opened) resized = false;
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.DefMDIChildProc (hwnd, msg, wParam, lParam);
+}
+
+void closeWidget () {
+    Event event = new Event ();
+    sendEvent (DWT.Close, event);
+    if (event.doit && !isDisposed ()) dispose ();
+}
+
+int compare (ImageData data1, ImageData data2, int width, int height, int depth) {
+    int value1 = Math.abs (data1.width - width), value2 = Math.abs (data2.width - width);
+    if (value1 is value2) {
+        int transparent1 = data1.getTransparencyType ();
+        int transparent2 = data2.getTransparencyType ();
+        if (transparent1 is transparent2) {
+            if (data1.depth is data2.depth) return 0;
+            return data1.depth > data2.depth && data1.depth <= depth ? -1 : 1;
+        }
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) {
+            if (transparent1 is DWT.TRANSPARENCY_ALPHA) return -1;
+            if (transparent2 is DWT.TRANSPARENCY_ALPHA) return 1;
+        }
+        if (transparent1 is DWT.TRANSPARENCY_MASK) return -1;
+        if (transparent2 is DWT.TRANSPARENCY_MASK) return 1;
+        if (transparent1 is DWT.TRANSPARENCY_PIXEL) return -1;
+        if (transparent2 is DWT.TRANSPARENCY_PIXEL) return 1;
+        return 0;
+    }
+    return value1 < value2 ? -1 : 1;
+}
+
+Control computeTabGroup () {
+    return this;
+}
+
+Control computeTabRoot () {
+    return this;
+}
+
+public Rectangle computeTrim (int x, int y, int width, int height) {
+    checkWidget ();
+
+    /* Get the size of the trimmings */
+    RECT rect = new RECT ();
+    OS.SetRect (rect, x, y, x + width, y + height);
+    int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+    bool hasMenu = OS.IsWinCE ? false : OS.GetMenu (handle) !is 0;
+    OS.AdjustWindowRectEx (rect, bits1, hasMenu, bits2);
+
+    /* Get the size of the scroll bars */
+    if (horizontalBar !is null) rect.bottom += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    if (verticalBar !is null) rect.right += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+
+    /* Compute the height of the menu bar */
+    if (hasMenu) {
+        RECT testRect = new RECT ();
+        OS.SetRect (testRect, 0, 0, rect.right - rect.left, rect.bottom - rect.top);
+        OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, testRect);
+        while ((testRect.bottom - testRect.top) < height) {
+            if (testRect.bottom - testRect.top is 0) break;
+            rect.top -= OS.GetSystemMetrics (OS.SM_CYMENU) - OS.GetSystemMetrics (OS.SM_CYBORDER);
+            OS.SetRect (testRect, 0, 0, rect.right - rect.left, rect.bottom - rect.top);
+            OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, testRect);
+        }
+    }
+    return new Rectangle (rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+}
+
+void createAccelerators () {
+    hAccel = nAccel = 0;
+    int maxAccel = 0;
+    MenuItem [] items = display.items;
+    if (menuBar is null || items is null) {
+        if (!OS.IsPPC) return;
+        maxAccel = 1;
+    } else {
+        maxAccel = OS.IsPPC ? items.length + 1 : items.length;
+    }
+    ACCEL accel = new ACCEL ();
+    byte [] buffer1 = new byte [ACCEL.sizeof];
+    byte [] buffer2 = new byte [maxAccel * ACCEL.sizeof];
+    if (menuBar !is null && items !is null) {
+        for (int i=0; i<items.length; i++) {
+            MenuItem item = items [i];
+            if (item !is null && item.accelerator !is 0) {
+                Menu menu = item.parent;
+                if (menu.parent is this) {
+                    while (menu !is null && menu !is menuBar) {
+                        menu = menu.getParentMenu ();
+                    }
+                    if (menu is menuBar) {
+                        item.fillAccel (accel);
+                        OS.MoveMemory (buffer1, accel, ACCEL.sizeof);
+                        System.arraycopy (buffer1, 0, buffer2, nAccel * ACCEL.sizeof, ACCEL.sizeof);
+                        nAccel++;
+                    }
+                }
+            }
+        }
+    }
+    if (OS.IsPPC) {
+        /*
+        * Note on WinCE PPC.  Close the shell when user taps CTRL-Q.
+        * IDOK represents the "Done Button" which also closes the shell.
+        */
+        accel.fVirt = (byte) (OS.FVIRTKEY | OS.FCONTROL);
+        accel.key = (short) 'Q';
+        accel.cmd = (short) OS.IDOK;
+        OS.MoveMemory (buffer1, accel, ACCEL.sizeof);
+        System.arraycopy (buffer1, 0, buffer2, nAccel * ACCEL.sizeof, ACCEL.sizeof);
+        nAccel++;
+    }
+    if (nAccel !is 0) hAccel = OS.CreateAcceleratorTable (buffer2, nAccel);
+}
+
+void createHandle () {
+    super.createHandle ();
+    if (parent !is null || ((style & DWT.TOOL) !is 0)) {
+        setParent ();
+        setSystemMenu ();
+    }
+    /*
+    * Set the default icon for the shell to IDI_APPLICATION.
+    * This is not necessary for native applications but later
+    * versions of Java set the icon in javaw.exe instead of
+    * leaving the default.
+    *
+    * NOTE:  The icon is not leaked.  It is shared within
+    * the process by all threads and is released when the
+    * process exits.
+    */
+    if ((state & FOREIGN_HANDLE) is 0) {
+        if (!OS.IsWinCE) {
+            int hIcon = OS.LoadIcon (0, OS.IDI_APPLICATION);
+            OS.SendMessage (handle, OS.WM_SETICON, OS.ICON_SMALL, hIcon);
+        }
+    }
+}
+
+void createWidget () {
+    super.createWidget ();
+    swFlags = OS.IsWinCE ? OS.SW_SHOWMAXIMIZED : OS.SW_SHOWNOACTIVATE;
+    hAccel = -1;
+}
+
+void destroyAccelerators () {
+    if (hAccel !is 0 && hAccel !is -1) OS.DestroyAcceleratorTable (hAccel);
+    hAccel = -1;
+}
+
+public void dispose () {
+    if (isDisposed()) return;
+    if (!isValidThread ()) error (DWT.ERROR_THREAD_INVALID_ACCESS);
+    if (!(this instanceof Shell)) {
+        if (!traverseDecorations (true)) {
+            Shell shell = getShell ();
+            shell.setFocus ();
+        }
+        setVisible (false);
+    }
+    super.dispose ();
+}
+
+Menu findMenu (int hMenu) {
+    if (menus is null) return null;
+    for (int i=0; i<menus.length; i++) {
+        Menu menu = menus [i];
+        if (menu !is null && hMenu is menu.handle) return menu;
+    }
+    return null;
+}
+
+void fixDecorations (Decorations newDecorations, Control control, Menu [] menus) {
+    if (this is newDecorations) return;
+    if (control is savedFocus) savedFocus = null;
+    if (control is defaultButton) defaultButton = null;
+    if (control is saveDefault) saveDefault = null;
+    if (menus is null) return;
+    Menu menu = control.menu;
+    if (menu !is null) {
+        int index = 0;
+        while (index <menus.length) {
+            if (menus [index] is menu) {
+                control.setMenu (null);
+                return;
+            }
+            index++;
+        }
+        menu.fixMenus (newDecorations);
+        destroyAccelerators ();
+        newDecorations.destroyAccelerators ();
+    }
+}
+
+public Rectangle getBounds () {
+    checkWidget ();
+    if (!OS.IsWinCE) {
+        if (OS.IsIconic (handle)) {
+            WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT ();
+            lpwndpl.length = WINDOWPLACEMENT.sizeof;
+            OS.GetWindowPlacement (handle, lpwndpl);
+            int width = lpwndpl.right - lpwndpl.left;
+            int height = lpwndpl.bottom - lpwndpl.top;
+            return new Rectangle (lpwndpl.left, lpwndpl.top, width, height);
+        }
+    }
+    return super.getBounds ();
+}
+
+public Rectangle getClientArea () {
+    checkWidget ();
+    /*
+    * Note: The CommandBar is part of the client area,
+    * not the trim.  Applications don't expect this so
+    * subtract the height of the CommandBar.
+    */
+    if (OS.IsHPC) {
+        Rectangle rect = super.getClientArea ();
+        if (menuBar !is null) {
+            int hwndCB = menuBar.hwndCB;
+            int height = OS.CommandBar_Height (hwndCB);
+            rect.y += height;
+            rect.height = Math.max (0, rect.height - height);
+        }
+        return rect;
+    }
+    if (!OS.IsWinCE) {
+        if (OS.IsIconic (handle)) {
+            WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT ();
+            lpwndpl.length = WINDOWPLACEMENT.sizeof;
+            OS.GetWindowPlacement (handle, lpwndpl);
+            int width = lpwndpl.right - lpwndpl.left;
+            int height = lpwndpl.bottom - lpwndpl.top;
+            /*
+            * Feature in Windows.  For some reason WM_NCCALCSIZE does
+            * not compute the client area when the window is minimized.
+            * The fix is to compute it using AdjustWindowRectEx() and
+            * GetSystemMetrics().
+            *
+            * NOTE: This code fails to compute the correct client area
+            * for a minimized window where the menu bar would wrap were
+            * the window restored.  There is no fix for this problem at
+            * this time.
+            */
+            if (horizontalBar !is null) width -= OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+            if (verticalBar !is null) height -= OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+            RECT rect = new RECT ();
+            int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+            bool hasMenu = OS.IsWinCE ? false : OS.GetMenu (handle) !is 0;
+            OS.AdjustWindowRectEx (rect, bits1, hasMenu, bits2);
+            width = Math.max (0, width - (rect.right - rect.left));
+            height = Math.max (0, height - (rect.bottom - rect.top));
+            return new Rectangle (0, 0, width, height);
+        }
+    }
+    return super.getClientArea ();
+}
+
+/**
+ * Returns the receiver's default button if one had
+ * previously been set, otherwise returns null.
+ *
+ * @return the default button or null
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setDefaultButton(Button)
+ */
+public Button getDefaultButton () {
+    checkWidget ();
+    return defaultButton;
+}
+
+/**
+ * Returns the receiver's image if it had previously been
+ * set using <code>setImage()</code>. The image is typically
+ * displayed by the window manager when the instance is
+ * marked as iconified, and may also be displayed somewhere
+ * in the trim when the instance is in normal or maximized
+ * states.
+ * <p>
+ * Note: This method will return null if called before
+ * <code>setImage()</code> is called. It does not provide
+ * access to a window manager provided, "default" image
+ * even if one exists.
+ * </p>
+ *
+ * @return the image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getImage () {
+    checkWidget ();
+    return image;
+}
+
+/**
+ * Returns the receiver's images if they had previously been
+ * set using <code>setImages()</code>. Images are typically
+ * displayed by the window manager when the instance is
+ * marked as iconified, and may also be displayed somewhere
+ * in the trim when the instance is in normal or maximized
+ * states. Depending where the icon is displayed, the platform
+ * chooses the icon with the "best" attributes.  It is expected
+ * that the array will contain the same icon rendered at different
+ * sizes, with different depth and transparency attributes.
+ *
+ * <p>
+ * Note: This method will return an empty array if called before
+ * <code>setImages()</code> is called. It does not provide
+ * access to a window manager provided, "default" image
+ * even if one exists.
+ * </p>
+ *
+ * @return the images
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Image [] getImages () {
+    checkWidget ();
+    if (images is null) return new Image [0];
+    Image [] result = new Image [images.length];
+    System.arraycopy (images, 0, result, 0, images.length);
+    return result;
+}
+
+public Point getLocation () {
+    checkWidget ();
+    if (!OS.IsWinCE) {
+        if (OS.IsIconic (handle)) {
+            WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT ();
+            lpwndpl.length = WINDOWPLACEMENT.sizeof;
+            OS.GetWindowPlacement (handle, lpwndpl);
+            return new Point (lpwndpl.left, lpwndpl.top);
+        }
+    }
+    return super.getLocation ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is currently
+ * maximized, and false otherwise.
+ * <p>
+ *
+ * @return the maximized state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setMaximized
+ */
+public bool getMaximized () {
+    checkWidget ();
+    if (OS.IsWinCE) return swFlags is OS.SW_SHOWMAXIMIZED;
+    if (OS.IsWindowVisible (handle)) return OS.IsZoomed (handle);
+    return swFlags is OS.SW_SHOWMAXIMIZED;
+}
+
+/**
+ * Returns the receiver's menu bar if one had previously
+ * been set, otherwise returns null.
+ *
+ * @return the menu bar or null
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Menu getMenuBar () {
+    checkWidget ();
+    return menuBar;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is currently
+ * minimized, and false otherwise.
+ * <p>
+ *
+ * @return the minimized state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setMinimized
+ */
+public bool getMinimized () {
+    checkWidget ();
+    if (OS.IsWinCE) return false;
+    if (OS.IsWindowVisible (handle)) return OS.IsIconic (handle);
+    return swFlags is OS.SW_SHOWMINNOACTIVE;
+}
+
+String getNameText () {
+    return getText ();
+}
+
+public Point getSize () {
+    checkWidget ();
+    if (!OS.IsWinCE) {
+        if (OS.IsIconic (handle)) {
+            WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT ();
+            lpwndpl.length = WINDOWPLACEMENT.sizeof;
+            OS.GetWindowPlacement (handle, lpwndpl);
+            int width = lpwndpl.right - lpwndpl.left;
+            int height = lpwndpl.bottom - lpwndpl.top;
+            return new Point (width, height);
+        }
+    }
+    return super.getSize ();
+}
+
+/**
+ * Returns the receiver's text, which is the string that the
+ * window manager will typically display as the receiver's
+ * <em>title</em>. If the text has not previously been set,
+ * returns an empty string.
+ *
+ * @return the text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText () {
+    checkWidget ();
+    int length = OS.GetWindowTextLength (handle);
+    if (length is 0) return "";
+    /* Use the character encoding for the default locale */
+    TCHAR buffer = new TCHAR (0, length + 1);
+    OS.GetWindowText (handle, buffer, length + 1);
+    return buffer.toString (0, length);
+}
+
+public bool isReparentable () {
+    checkWidget ();
+    /*
+    * Feature in Windows.  Calling SetParent() for a shell causes
+    * a kind of fake MDI to happen.  It doesn't work well on Windows
+    * and is not supported on the other platforms.  The fix is to
+    * disallow the SetParent().
+    */
+    return false;
+}
+
+bool isTabGroup () {
+    /*
+    * Can't test WS_TAB bits because they are the same as WS_MAXIMIZEBOX.
+    */
+    return true;
+}
+
+bool isTabItem () {
+    /*
+    * Can't test WS_TAB bits because they are the same as WS_MAXIMIZEBOX.
+    */
+    return false;
+}
+
+Decorations menuShell () {
+    return this;
+}
+
+void releaseChildren (bool destroy) {
+    if (menuBar !is null) {
+        menuBar.release (false);
+        menuBar = null;
+    }
+    super.releaseChildren (destroy);
+    if (menus !is null) {
+        for (int i=0; i<menus.length; i++) {
+            Menu menu = menus [i];
+            if (menu !is null && !menu.isDisposed ()) {
+                menu.dispose ();
+            }
+        }
+        menus = null;
+    }
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if (smallImage !is null) smallImage.dispose ();
+    if (largeImage !is null) largeImage.dispose ();
+    smallImage = largeImage = image = null;
+    images = null;
+    savedFocus = null;
+    defaultButton = saveDefault = null;
+    if (hAccel !is 0 && hAccel !is -1) OS.DestroyAcceleratorTable (hAccel);
+    hAccel = -1;
+}
+
+void removeMenu (Menu menu) {
+    if (menus is null) return;
+    for (int i=0; i<menus.length; i++) {
+        if (menus [i] is menu) {
+            menus [i] = null;
+            return;
+        }
+    }
+}
+
+bool restoreFocus () {
+    if (display.ignoreRestoreFocus) return true;
+    if (savedFocus !is null && savedFocus.isDisposed ()) savedFocus = null;
+    if (savedFocus !is null && savedFocus.setSavedFocus ()) return true;
+    /*
+    * This code is intentionally commented.  When no widget
+    * has been given focus, some platforms give focus to the
+    * default button.  Windows doesn't do this.
+    */
+//  if (defaultButton !is null && !defaultButton.isDisposed ()) {
+//      if (defaultButton.setFocus ()) return true;
+//  }
+    return false;
+}
+
+void saveFocus () {
+    Control control = display._getFocusControl ();
+    if (control !is null && control !is this && this is control.menuShell ()) {
+        setSavedFocus (control);
+    }
+}
+
+void setBounds (int x, int y, int width, int height, int flags, bool defer) {
+    if (OS.IsWinCE) {
+        swFlags = OS.SW_RESTORE;
+    } else {
+        if (OS.IsIconic (handle)) {
+            setPlacement (x, y, width, height, flags);
+            return;
+        }
+    }
+    forceResize ();
+    RECT rect = new RECT ();
+    OS.GetWindowRect (handle, rect);
+    bool sameOrigin = true;
+    if ((OS.SWP_NOMOVE & flags) is 0) {
+        sameOrigin = rect.left is x && rect.top is y;
+        if (!sameOrigin) moved = true;
+    }
+    bool sameExtent = true;
+    if ((OS.SWP_NOSIZE & flags) is 0) {
+        sameExtent = rect.right - rect.left is width && rect.bottom - rect.top is height;
+        if (!sameExtent) resized = true;
+    }
+    if (!OS.IsWinCE) {
+        if (OS.IsZoomed (handle)) {
+            if (sameOrigin && sameExtent) return;
+            setPlacement (x, y, width, height, flags);
+            _setMaximized (false);
+            return;
+        }
+    }
+    super.setBounds (x, y, width, height, flags, defer);
+}
+
+/**
+ * If the argument is not null, sets the receiver's default
+ * button to the argument, and if the argument is null, sets
+ * the receiver's default button to the first button which
+ * was set as the receiver's default button (called the
+ * <em>saved default button</em>). If no default button had
+ * previously been set, or the saved default button was
+ * disposed, the receiver's default button will be set to
+ * null.
+ * <p>
+ * The default button is the button that is selected when
+ * the receiver is active and the user presses ENTER.
+ * </p>
+ *
+ * @param button the new default button
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the button has been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setDefaultButton (Button button) {
+    checkWidget ();
+    if (button !is null) {
+        if (button.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+        if (button.menuShell () !is this) error(DWT.ERROR_INVALID_PARENT);
+    }
+    setDefaultButton (button, true);
+}
+
+void setDefaultButton (Button button, bool save) {
+    if (button is null) {
+        if (defaultButton is saveDefault) {
+            if (save) saveDefault = null;
+            return;
+        }
+    } else {
+        if ((button.style & DWT.PUSH) is 0) return;
+        if (button is defaultButton) return;
+    }
+    if (defaultButton !is null) {
+        if (!defaultButton.isDisposed ()) defaultButton.setDefault (false);
+    }
+    if ((defaultButton = button) is null) defaultButton = saveDefault;
+    if (defaultButton !is null) {
+        if (!defaultButton.isDisposed ()) defaultButton.setDefault (true);
+    }
+    if (save) saveDefault = defaultButton;
+    if (saveDefault !is null && saveDefault.isDisposed ()) saveDefault = null;
+}
+
+/**
+ * Sets the receiver's image to the argument, which may
+ * be null. The image is typically displayed by the window
+ * manager when the instance is marked as iconified, and
+ * may also be displayed somewhere in the trim when the
+ * instance is in normal or maximized states.
+ *
+ * @param image the new image (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (Image image) {
+    checkWidget ();
+    if (image !is null && image.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+    this.image = image;
+    setImages (image, null);
+}
+
+void setImages (Image image, Image [] images) {
+    /*
+    * Feature in WinCE.  WM_SETICON and WM_GETICON set the icon
+    * for the window class, not the window instance.  This means
+    * that it is possible to set an icon into a window and then
+    * later free the icon, thus freeing the icon for every window.
+    * The fix is to avoid the API.
+    *
+    * On WinCE PPC, icons in windows are not displayed.
+    */
+    if (OS.IsWinCE) return;
+    if (smallImage !is null) smallImage.dispose ();
+    if (largeImage !is null) largeImage.dispose ();
+    smallImage = largeImage = null;
+    int hSmallIcon = 0, hLargeIcon = 0;
+    Image smallIcon = null, largeIcon = null;
+    if (image !is null) {
+        smallIcon = largeIcon = image;
+    } else {
+        if (images !is null && images.length > 0) {
+            int depth = display.getIconDepth ();
+            ImageData [] datas = null;
+            if (images.length > 1) {
+                Image [] bestImages = new Image [images.length];
+                System.arraycopy (images, 0, bestImages, 0, images.length);
+                datas = new ImageData [images.length];
+                for (int i=0; i<datas.length; i++) {
+                    datas [i] = images [i].getImageData ();
+                }
+                images = bestImages;
+                sort (images, datas, OS.GetSystemMetrics (OS.SM_CXSMICON), OS.GetSystemMetrics (OS.SM_CYSMICON), depth);
+            }
+            smallIcon = images [0];
+            if (images.length > 1) {
+                sort (images, datas, OS.GetSystemMetrics (OS.SM_CXICON), OS.GetSystemMetrics (OS.SM_CYICON), depth);
+            }
+            largeIcon = images [0];
+        }
+    }
+    if (smallIcon !is null) {
+        switch (smallIcon.type) {
+            case DWT.BITMAP:
+                smallImage = Display.createIcon (smallIcon);
+                hSmallIcon = smallImage.handle;
+                break;
+            case DWT.ICON:
+                hSmallIcon = smallIcon.handle;
+                break;
+        }
+    } else {
+        /*
+        * Set the default icon for the shell to IDI_APPLICATION.
+        * This is not necessary for native applications but later
+        * versions of Java set the icon in javaw.exe instead of
+        * leaving the default.
+        *
+        * NOTE:  The icon is not leaked.  It is shared within
+        * the process by all threads and is released when the
+        * process exits.
+        */
+        if ((state & FOREIGN_HANDLE) is 0) {
+            hSmallIcon = OS.LoadIcon (0, OS.IDI_APPLICATION);
+        }
+    }
+    OS.SendMessage (handle, OS.WM_SETICON, OS.ICON_SMALL, hSmallIcon);
+    if (largeIcon !is null) {
+        switch (largeIcon.type) {
+            case DWT.BITMAP:
+                largeImage = Display.createIcon (largeIcon);
+                hLargeIcon = largeImage.handle;
+                break;
+            case DWT.ICON:
+                hLargeIcon = largeIcon.handle;
+                break;
+        }
+    }
+    OS.SendMessage (handle, OS.WM_SETICON, OS.ICON_BIG, hLargeIcon);
+
+    /*
+    * Bug in Windows.  When WM_SETICON is used to remove an
+    * icon from the window trimmings for a window with the
+    * extended style bits WS_EX_DLGMODALFRAME, the window
+    * trimmings do not redraw to hide the previous icon.
+    * The fix is to force a redraw.
+    */
+    if (!OS.IsWinCE) {
+        if (hSmallIcon is 0 && hLargeIcon is 0 && (style & DWT.BORDER) !is 0) {
+            int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE;
+            OS.RedrawWindow (handle, null, 0, flags);
+        }
+    }
+}
+
+/**
+ * Sets the receiver's images to the argument, which may
+ * be an empty array. Images are typically displayed by the
+ * window manager when the instance is marked as iconified,
+ * and may also be displayed somewhere in the trim when the
+ * instance is in normal or maximized states. Depending where
+ * the icon is displayed, the platform chooses the icon with
+ * the "best" attributes. It is expected that the array will
+ * contain the same icon rendered at different sizes, with
+ * different depth and transparency attributes.
+ *
+ * @param images the new image array
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of images is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if one of the images is null or has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setImages (Image [] images) {
+    checkWidget ();
+    if (images is null) error (DWT.ERROR_INVALID_ARGUMENT);
+    for (int i = 0; i < images.length; i++) {
+        if (images [i] is null || images [i].isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    this.images = images;
+    setImages (null, images);
+}
+
+/**
+ * Sets the maximized state of the receiver.
+ * If the argument is <code>true</code> causes the receiver
+ * to switch to the maximized state, and if the argument is
+ * <code>false</code> and the receiver was previously maximized,
+ * causes the receiver to switch back to either the minimized
+ * or normal states.
+ * <p>
+ * Note: The result of intermixing calls to <code>setMaximized(true)</code>
+ * and <code>setMinimized(true)</code> will vary by platform. Typically,
+ * the behavior will match the platform user's expectations, but not
+ * always. This should be avoided if possible.
+ * </p>
+ *
+ * @param maximized the new maximized state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setMinimized
+ */
+public void setMaximized (bool maximized) {
+    checkWidget ();
+    Display.lpStartupInfo = null;
+    _setMaximized (maximized);
+}
+
+/**
+ * Sets the receiver's menu bar to the argument, which
+ * may be null.
+ *
+ * @param menu the new menu bar
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMenuBar (Menu menu) {
+    checkWidget ();
+    if (menuBar is menu) return;
+    if (menu !is null) {
+        if (menu.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+        if ((menu.style & DWT.BAR) is 0) error (DWT.ERROR_MENU_NOT_BAR);
+        if (menu.parent !is this) error (DWT.ERROR_INVALID_PARENT);
+    }
+    if (OS.IsWinCE) {
+        if (OS.IsHPC) {
+            bool resize = menuBar !is menu;
+            if (menuBar !is null) OS.CommandBar_Show (menuBar.hwndCB, false);
+            menuBar = menu;
+            if (menuBar !is null) OS.CommandBar_Show (menuBar.hwndCB, true);
+            if (resize) {
+                sendEvent (DWT.Resize);
+                if (isDisposed ()) return;
+                if (layout !is null) {
+                    markLayout (false, false);
+                    updateLayout (true, false);
+                }
+            }
+        } else {
+            if (OS.IsPPC) {
+                /*
+                * Note in WinCE PPC.  The menu bar is a separate popup window.
+                * If the shell is full screen, resize its window to leave
+                * space for the menu bar.
+                */
+                bool resize = getMaximized () && menuBar !is menu;
+                if (menuBar !is null) OS.ShowWindow (menuBar.hwndCB, OS.SW_HIDE);
+                menuBar = menu;
+                if (menuBar !is null) OS.ShowWindow (menuBar.hwndCB, OS.SW_SHOW);
+                if (resize) _setMaximized (true);
+            }
+            if (OS.IsSP) {
+                if (menuBar !is null) OS.ShowWindow (menuBar.hwndCB, OS.SW_HIDE);
+                menuBar = menu;
+                if (menuBar !is null) OS.ShowWindow (menuBar.hwndCB, OS.SW_SHOW);
+            }
+        }
+    } else {
+        if (menu !is null) display.removeBar (menu);
+        menuBar = menu;
+        int hMenu = menuBar !is null ? menuBar.handle: 0;
+        OS.SetMenu (handle, hMenu);
+    }
+    destroyAccelerators ();
+}
+
+/**
+ * Sets the minimized stated of the receiver.
+ * If the argument is <code>true</code> causes the receiver
+ * to switch to the minimized state, and if the argument is
+ * <code>false</code> and the receiver was previously minimized,
+ * causes the receiver to switch back to either the maximized
+ * or normal states.
+ * <p>
+ * Note: The result of intermixing calls to <code>setMaximized(true)</code>
+ * and <code>setMinimized(true)</code> will vary by platform. Typically,
+ * the behavior will match the platform user's expectations, but not
+ * always. This should be avoided if possible.
+ * </p>
+ *
+ * @param minimized the new maximized state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setMaximized
+ */
+public void setMinimized (bool minimized) {
+    checkWidget ();
+    Display.lpStartupInfo = null;
+    _setMinimized (minimized);
+}
+
+void setParent () {
+    /*
+    * In order for an MDI child window to support
+    * a menu bar, setParent () is needed to reset
+    * the parent.  Otherwise, the MDI child window
+    * will appear as a separate shell.  This is an
+    * undocumented and possibly dangerous Windows
+    * feature.
+    */
+    int hwndParent = parent.handle;
+    display.lockActiveWindow = true;
+    OS.SetParent (handle, hwndParent);
+    if (!OS.IsWindowVisible (hwndParent)) {
+        OS.ShowWindow (handle, OS.SW_SHOWNA);
+    }
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    bits &= ~OS.WS_CHILD;
+    OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.WS_POPUP);
+    OS.SetWindowLong (handle, OS.GWL_ID, 0);
+    int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
+    SetWindowPos (handle, OS.HWND_BOTTOM, 0, 0, 0, 0, flags);
+    display.lockActiveWindow = false;
+}
+
+void setPlacement (int x, int y, int width, int height, int flags) {
+    WINDOWPLACEMENT lpwndpl = new WINDOWPLACEMENT ();
+    lpwndpl.length = WINDOWPLACEMENT.sizeof;
+    OS.GetWindowPlacement (handle, lpwndpl);
+    lpwndpl.showCmd = OS.SW_SHOWNA;
+    if (OS.IsIconic (handle)) {
+        lpwndpl.showCmd = OS.SW_SHOWMINNOACTIVE;
+    } else {
+        if (OS.IsZoomed (handle)) {
+            lpwndpl.showCmd = OS.SW_SHOWMAXIMIZED;
+        }
+    }
+    bool sameOrigin = true;
+    if ((flags & OS.SWP_NOMOVE) is 0) {
+        sameOrigin = lpwndpl.left !is x || lpwndpl.top !is y;
+        lpwndpl.right = x + (lpwndpl.right - lpwndpl.left);
+        lpwndpl.bottom = y + (lpwndpl.bottom - lpwndpl.top);
+        lpwndpl.left = x;
+        lpwndpl.top = y;
+    }
+    bool sameExtent = true;
+    if ((flags & OS.SWP_NOSIZE) is 0) {
+        sameExtent = lpwndpl.right - lpwndpl.left !is width || lpwndpl.bottom - lpwndpl.top !is height;
+        lpwndpl.right = lpwndpl.left + width;
+        lpwndpl.bottom = lpwndpl.top + height;
+    }
+    OS.SetWindowPlacement (handle, lpwndpl);
+    if (OS.IsIconic (handle)) {
+        if (sameOrigin) {
+            moved = true;
+            Point location = getLocation ();
+            oldX = location.x;
+            oldY = location.y;
+            sendEvent (DWT.Move);
+            if (isDisposed ()) return;
+        }
+        if (sameExtent) {
+            resized = true;
+            Rectangle rect = getClientArea ();
+            oldWidth = rect.width;
+            oldHeight = rect.height;
+            sendEvent (DWT.Resize);
+            if (isDisposed ()) return;
+            if (layout !is null) {
+                markLayout (false, false);
+                updateLayout (true, false);
+            }
+        }
+    }
+}
+
+void setSavedFocus (Control control) {
+    savedFocus = control;
+}
+
+void setSystemMenu () {
+    if (OS.IsWinCE) return;
+    int hMenu = OS.GetSystemMenu (handle, false);
+    if (hMenu is 0) return;
+    int oldCount = OS.GetMenuItemCount (hMenu);
+    if ((style & DWT.RESIZE) is 0) {
+        OS.DeleteMenu (hMenu, OS.SC_SIZE, OS.MF_BYCOMMAND);
+    }
+    if ((style & DWT.MIN) is 0) {
+        OS.DeleteMenu (hMenu, OS.SC_MINIMIZE, OS.MF_BYCOMMAND);
+    }
+    if ((style & DWT.MAX) is 0) {
+        OS.DeleteMenu (hMenu, OS.SC_MAXIMIZE, OS.MF_BYCOMMAND);
+    }
+    if ((style & (DWT.MIN | DWT.MAX)) is 0) {
+        OS.DeleteMenu (hMenu, OS.SC_RESTORE, OS.MF_BYCOMMAND);
+    }
+    int newCount = OS.GetMenuItemCount (hMenu);
+    if ((style & DWT.CLOSE) is 0 || newCount !is oldCount) {
+        OS.DeleteMenu (hMenu, OS.SC_TASKLIST, OS.MF_BYCOMMAND);
+        MENUITEMINFO info = new MENUITEMINFO ();
+        info.cbSize = MENUITEMINFO.sizeof;
+        info.fMask = OS.MIIM_ID;
+        int index = 0;
+        while (index < newCount) {
+            if (OS.GetMenuItemInfo (hMenu, index, true, info)) {
+                if (info.wID is OS.SC_CLOSE) break;
+            }
+            index++;
+        }
+        if (index !is newCount) {
+            OS.DeleteMenu (hMenu, index - 1, OS.MF_BYPOSITION);
+            if ((style & DWT.CLOSE) is 0) {
+                OS.DeleteMenu (hMenu, OS.SC_CLOSE, OS.MF_BYCOMMAND);
+            }
+        }
+    }
+}
+
+/**
+ * Sets the receiver's text, which is the string that the
+ * window manager will typically display as the receiver's
+ * <em>title</em>, to the argument, which must not be null.
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    /* Use the character encoding for the default locale */
+    TCHAR buffer = new TCHAR (0, string, true);
+    OS.SetWindowText (handle, buffer);
+}
+
+public void setVisible (bool visible) {
+    checkWidget ();
+    if (drawCount !is 0) {
+        if (((state & HIDDEN) is 0) is visible) return;
+    } else {
+        if (visible is OS.IsWindowVisible (handle)) return;
+    }
+    if (visible) {
+        /*
+        * It is possible (but unlikely), that application
+        * code could have disposed the widget in the show
+        * event.  If this happens, just return.
+        */
+        sendEvent (DWT.Show);
+        if (isDisposed ()) return;
+        if (OS.IsHPC) {
+            if (menuBar !is null) {
+                int hwndCB = menuBar.hwndCB;
+                OS.CommandBar_DrawMenuBar (hwndCB, 0);
+            }
+        }
+        if (drawCount !is 0) {
+            state &= ~HIDDEN;
+        } else {
+            if (OS.IsWinCE) {
+                OS.ShowWindow (handle, OS.SW_SHOW);
+            } else {
+                if (menuBar !is null) {
+                    display.removeBar (menuBar);
+                    OS.DrawMenuBar (handle);
+                }
+                STARTUPINFO lpStartUpInfo = Display.lpStartupInfo;
+                if (lpStartUpInfo !is null && (lpStartUpInfo.dwFlags & OS.STARTF_USESHOWWINDOW) !is 0) {
+                    OS.ShowWindow (handle, lpStartUpInfo.wShowWindow);
+                } else {
+                    OS.ShowWindow (handle, swFlags);
+                }
+            }
+            if (isDisposed ()) return;
+            opened = true;
+            if (!moved) {
+                moved = true;
+                Point location = getLocation ();
+                oldX = location.x;
+                oldY = location.y;
+            }
+            if (!resized) {
+                resized = true;
+                Rectangle rect = getClientArea ();
+                oldWidth = rect.width;
+                oldHeight = rect.height;
+            }
+            OS.UpdateWindow (handle);
+        }
+    } else {
+        if (!OS.IsWinCE) {
+            if (OS.IsIconic (handle)) {
+                swFlags = OS.SW_SHOWMINNOACTIVE;
+            } else {
+                if (OS.IsZoomed (handle)) {
+                    swFlags = OS.SW_SHOWMAXIMIZED;
+                } else {
+                    if (handle is OS.GetActiveWindow ()) {
+                        swFlags = OS.SW_RESTORE;
+                    } else {
+                        swFlags = OS.SW_SHOWNOACTIVATE;
+                    }
+                }
+            }
+        }
+        if (drawCount !is 0) {
+            state |= HIDDEN;
+        } else {
+            OS.ShowWindow (handle, OS.SW_HIDE);
+        }
+        if (isDisposed ()) return;
+        sendEvent (DWT.Hide);
+    }
+}
+
+void sort (Image [] images, ImageData [] datas, int width, int height, int depth) {
+    /* Shell Sort from K&R, pg 108 */
+    int length = images.length;
+    if (length <= 1) return;
+    for (int gap=length/2; gap>0; gap/=2) {
+        for (int i=gap; i<length; i++) {
+            for (int j=i-gap; j>=0; j-=gap) {
+                if (compare (datas [j], datas [j + gap], width, height, depth) >= 0) {
+                    Image swap = images [j];
+                    images [j] = images [j + gap];
+                    images [j + gap] = swap;
+                    ImageData swapData = datas [j];
+                    datas [j] = datas [j + gap];
+                    datas [j + gap] = swapData;
+                }
+            }
+        }
+    }
+}
+
+bool translateAccelerator (MSG msg) {
+    if (!isEnabled () || !isActive ()) return false;
+    if (menuBar !is null && !menuBar.isEnabled ()) return false;
+    if (translateMDIAccelerator (msg) || translateMenuAccelerator (msg)) return true;
+    Decorations decorations = parent.menuShell ();
+    return decorations.translateAccelerator (msg);
+}
+
+bool translateMenuAccelerator (MSG msg) {
+    if (hAccel is -1) createAccelerators ();
+    return hAccel !is 0 && OS.TranslateAccelerator (handle, hAccel, msg) !is 0;
+}
+
+bool translateMDIAccelerator (MSG msg) {
+    if (!(this instanceof Shell)) {
+        Shell shell = getShell ();
+        int hwndMDIClient = shell.hwndMDIClient;
+        if (hwndMDIClient !is 0 && OS.TranslateMDISysAccel (hwndMDIClient, msg)) {
+            return true;
+        }
+        if (msg.message is OS.WM_KEYDOWN) {
+            if (OS.GetKeyState (OS.VK_CONTROL) >= 0) return false;
+            switch (msg.wParam) {
+                case OS.VK_F4:
+                    OS.PostMessage (handle, OS.WM_CLOSE, 0, 0);
+                    return true;
+                case OS.VK_F6:
+                    if (traverseDecorations (true)) return true;
+            }
+            return false;
+        }
+        if (msg.message is OS.WM_SYSKEYDOWN) {
+            switch (msg.wParam) {
+                case OS.VK_F4:
+                    OS.PostMessage (shell.handle, OS.WM_CLOSE, 0, 0);
+                    return true;
+            }
+            return false;
+        }
+    }
+    return false;
+}
+
+bool traverseDecorations (bool next) {
+    Control [] children = parent._getChildren ();
+    int length = children.length;
+    int index = 0;
+    while (index < length) {
+        if (children [index] is this) break;
+        index++;
+    }
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in focus in
+    * or out events.  Ensure that a disposed widget is
+    * not accessed.
+    */
+    int start = index, offset = (next) ? 1 : -1;
+    while ((index = (index + offset + length) % length) !is start) {
+        Control child = children [index];
+        if (!child.isDisposed () && child instanceof Decorations) {
+            if (child.setFocus ()) return true;
+        }
+    }
+    return false;
+}
+
+bool traverseItem (bool next) {
+    return false;
+}
+
+bool traverseReturn () {
+    if (defaultButton is null || defaultButton.isDisposed ()) return false;
+    if (!defaultButton.isVisible () || !defaultButton.isEnabled ()) return false;
+    defaultButton.click ();
+    return true;
+}
+
+CREATESTRUCT widgetCreateStruct () {
+    return new CREATESTRUCT ();
+}
+
+int widgetExtStyle () {
+    int bits = super.widgetExtStyle () | OS.WS_EX_MDICHILD;
+    bits &= ~OS.WS_EX_CLIENTEDGE;
+    if ((style & DWT.NO_TRIM) !is 0) return bits;
+    if (OS.IsPPC) {
+        if ((style & DWT.CLOSE) !is 0) bits |= OS.WS_EX_CAPTIONOKBTN;
+    }
+    if ((style & DWT.RESIZE) !is 0) return bits;
+    if ((style & DWT.BORDER) !is 0) bits |= OS.WS_EX_DLGMODALFRAME;
+    return bits;
+}
+
+int widgetParent () {
+    Shell shell = getShell ();
+    return shell.hwndMDIClient ();
+}
+
+int widgetStyle () {
+    /*
+    * Clear WS_VISIBLE and WS_TABSTOP.  NOTE: In Windows, WS_TABSTOP
+    * has the same value as WS_MAXIMIZEBOX so these bits cannot be
+    * used to control tabbing.
+    */
+    int bits = super.widgetStyle () & ~(OS.WS_TABSTOP | OS.WS_VISIBLE);
+
+    /* Set the title bits and no-trim bits */
+    bits &= ~OS.WS_BORDER;
+    if ((style & DWT.NO_TRIM) !is 0) return bits;
+    if ((style & DWT.TITLE) !is 0) bits |= OS.WS_CAPTION;
+
+    /* Set the min and max button bits */
+    if ((style & DWT.MIN) !is 0) bits |= OS.WS_MINIMIZEBOX;
+    if ((style & DWT.MAX) !is 0) bits |= OS.WS_MAXIMIZEBOX;
+
+    /* Set the resize, dialog border or border bits */
+    if ((style & DWT.RESIZE) !is 0) {
+        /*
+        * Note on WinCE PPC.  DWT.RESIZE is used to resize
+        * the Shell according to the state of the IME.
+        * It does not set the WS_THICKFRAME style.
+        */
+        if (!OS.IsPPC) bits |= OS.WS_THICKFRAME;
+    } else {
+        if ((style & DWT.BORDER) is 0) bits |= OS.WS_BORDER;
+    }
+
+    /* Set the system menu and close box bits */
+    if (!OS.IsPPC && !OS.IsSP) {
+        if ((style & DWT.CLOSE) !is 0) bits |= OS.WS_SYSMENU;
+    }
+
+    return bits;
+}
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    switch (msg) {
+        case Display.SWT_GETACCEL:
+        case Display.SWT_GETACCELCOUNT:
+            if (hAccel is -1) createAccelerators ();
+            return msg is Display.SWT_GETACCELCOUNT ? nAccel : hAccel;
+    }
+    return super.windowProc (hwnd, msg, wParam, lParam);
+}
+
+LRESULT WM_ACTIVATE (int wParam, int lParam) {
+    LRESULT result = super.WM_ACTIVATE (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in AWT.  When an AWT Window is activated,
+    * for some reason, it seems to forward the WM_ACTIVATE
+    * message to the parent.  Normally, the parent is an
+    * AWT Frame.  When AWT is embedded in DWT, the DWT
+    * shell gets the WM_ACTIVATE and assumes that it came
+    * from Windows.  When an DWT shell is activated it
+    * restores focus to the last control that had focus.
+    * If this control is an embedded composite, it takes
+    * focus from the AWT Window.  The fix is to ignore
+    * WM_ACTIVATE messages that come from AWT Windows.
+    */
+    if (OS.GetParent (lParam) is handle) {
+        TCHAR buffer = new TCHAR (0, 128);
+        OS.GetClassName (lParam, buffer, buffer.length ());
+        String className = buffer.toString (0, buffer.strlen ());
+        if (className.equals (Display.AWT_WINDOW_CLASS)) {
+            return LRESULT.ZERO;
+        }
+    }
+    if ((wParam & 0xFFFF) !is 0) {
+        /*
+        * When the high word of wParam is non-zero, the activation
+        * state of the window is being changed while the window is
+        * minimized. If this is the case, do not report activation
+        * events or restore the focus.
+        */
+        if ((wParam >> 16) !is 0) return result;
+        Control control = display.findControl (lParam);
+        if (control is null || control instanceof Shell) {
+            if (this instanceof Shell) {
+                sendEvent (DWT.Activate);
+                if (isDisposed ()) return LRESULT.ZERO;
+            }
+        }
+        if (restoreFocus ()) return LRESULT.ZERO;
+    } else {
+        Display display = this.display;
+        bool lockWindow = display.isXMouseActive ();
+        if (lockWindow) display.lockActiveWindow = true;
+        Control control = display.findControl (lParam);
+        if (control is null || control instanceof Shell) {
+            if (this instanceof Shell) {
+                sendEvent (DWT.Deactivate);
+                if (!isDisposed ()) {
+                    Shell shell = getShell ();
+                    shell.setActiveControl (null);
+                    // widget could be disposed at this point
+                }
+            }
+        }
+        if (lockWindow) display.lockActiveWindow = false;
+        if (isDisposed ()) return LRESULT.ZERO;
+        saveFocus ();
+    }
+    return result;
+}
+
+LRESULT WM_CLOSE (int wParam, int lParam) {
+    LRESULT result = super.WM_CLOSE (wParam, lParam);
+    if (result !is null) return result;
+    if (isEnabled () && isActive ()) closeWidget ();
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_HOTKEY (int wParam, int lParam) {
+    LRESULT result = super.WM_HOTKEY (wParam, lParam);
+    if (result !is null) return result;
+    if (OS.IsSP) {
+        /*
+        * Feature on WinCE SP.  The Back key is either used to close
+        * the foreground Dialog or used as a regular Back key in an EDIT
+        * control. The article 'Back Key' in MSDN for Smartphone
+        * describes how an application should handle it.  The
+        * workaround is to override the Back key when creating
+        * the menubar and handle it based on the style of the Shell.
+        * If the Shell has the DWT.CLOSE style, close the Shell.
+        * Otherwise, send the Back key to the window with focus.
+        */
+        if (((lParam >> 16) & 0xFFFF) is OS.VK_ESCAPE) {
+            if ((style & DWT.CLOSE) !is 0) {
+                OS.PostMessage (handle, OS.WM_CLOSE, 0, 0);
+            } else {
+                OS.SHSendBackToFocusWindow (OS.WM_HOTKEY, wParam, lParam);
+            }
+            return LRESULT.ZERO;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
+    saveFocus ();
+    return result;
+}
+
+LRESULT WM_MOVE (int wParam, int lParam) {
+    if (moved) {
+        Point location = getLocation ();
+        if (location.x is oldX && location.y is oldY) {
+            return null;
+        }
+        oldX = location.x;
+        oldY = location.y;
+    }
+    return super.WM_MOVE (wParam, lParam);
+}
+
+LRESULT WM_NCACTIVATE (int wParam, int lParam) {
+    LRESULT result = super.WM_NCACTIVATE (wParam, lParam);
+    if (result !is null) return result;
+    if (wParam is 0) {
+        if (display.lockActiveWindow) return LRESULT.ZERO;
+        Control control = display.findControl (lParam);
+        if (control !is null) {
+            Shell shell = getShell ();
+            Decorations decorations = control.menuShell ();
+            if (decorations.getShell () is shell) {
+                if (this instanceof Shell) return LRESULT.ONE;
+                if (display.ignoreRestoreFocus) {
+                    if (display.lastHittest !is OS.HTCLIENT) {
+                        result = LRESULT.ONE;
+                    }
+                }
+            }
+        }
+    }
+    if (!(this instanceof Shell)) {
+        int hwndShell = getShell().handle;
+        OS.SendMessage (hwndShell, OS.WM_NCACTIVATE, wParam, lParam);
+    }
+    return result;
+}
+
+LRESULT WM_QUERYOPEN (int wParam, int lParam) {
+    LRESULT result = super.WM_QUERYOPEN (wParam, lParam);
+    if (result !is null) return result;
+    sendEvent (DWT.Deiconify);
+    // widget could be disposed at this point
+    return result;
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_SETFOCUS (wParam, lParam);
+    if (savedFocus !is this) restoreFocus ();
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    LRESULT result = null;
+    bool changed = true;
+    if (resized) {
+        int newWidth = 0, newHeight = 0;
+        switch (wParam) {
+            case OS.SIZE_RESTORED:
+            case OS.SIZE_MAXIMIZED:
+                newWidth = lParam & 0xFFFF;
+                newHeight = lParam >> 16;
+                break;
+            case OS.SIZE_MINIMIZED:
+                Rectangle rect = getClientArea ();
+                newWidth = rect.width;
+                newHeight = rect.height;
+                break;
+        }
+        changed = newWidth !is oldWidth || newHeight !is oldHeight;
+        if (changed) {
+            oldWidth = newWidth;
+            oldHeight = newHeight;
+        }
+    }
+    if (changed) {
+        result = super.WM_SIZE (wParam, lParam);
+        if (isDisposed ()) return result;
+    }
+    if (wParam is OS.SIZE_MINIMIZED) {
+        sendEvent (DWT.Iconify);
+        // widget could be disposed at this point
+    }
+    return result;
+}
+
+LRESULT WM_SYSCOMMAND (int wParam, int lParam) {
+    LRESULT result = super.WM_SYSCOMMAND (wParam, lParam);
+    if (result !is null) return result;
+    if (!(this instanceof Shell)) {
+        int cmd = wParam & 0xFFF0;
+        switch (cmd) {
+            case OS.SC_CLOSE: {
+                OS.PostMessage (handle, OS.WM_CLOSE, 0, 0);
+                return LRESULT.ZERO;
+            }
+            case OS.SC_NEXTWINDOW: {
+                traverseDecorations (true);
+                return LRESULT.ZERO;
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_WINDOWPOSCHANGING (int wParam, int lParam) {
+    LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam);
+    if (result !is null) return result;
+    if (display.lockActiveWindow) {
+        WINDOWPOS lpwp = new WINDOWPOS ();
+        OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
+        lpwp.flags |= OS.SWP_NOZORDER;
+        OS.MoveMemory (lParam, lpwp, WINDOWPOS.sizeof);
+    }
+    return result;
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Dialog.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Dialog;
+
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.widgets.Shell;
+import dwt.widgets.Display;
+
+/**
+ * This class is the abstract superclass of the classes
+ * that represent the built in platform dialogs.
+ * A <code>Dialog</code> typically contains other widgets
+ * that are not accessible. A <code>Dialog</code> is not
+ * a <code>Widget</code>.
+ * <p>
+ * This class can also be used as the abstract superclass
+ * for user-designed dialogs. Such dialogs usually consist
+ * of a Shell with child widgets. The basic template for a
+ * user-defined dialog typically looks something like this:
+ * <pre><code>
+ * public class MyDialog extends Dialog {
+ *  Object result;
+ *
+ *  public MyDialog (Shell parent, int style) {
+ *      super (parent, style);
+ *  }
+ *  public MyDialog (Shell parent) {
+ *      this (parent, 0); // your default style bits go here (not the Shell's style bits)
+ *  }
+ *  public Object open () {
+ *      Shell parent = getParent();
+ *      Shell shell = new Shell(parent, DWT.DIALOG_TRIM | DWT.APPLICATION_MODAL);
+ *      shell.setText(getText());
+ *      // Your code goes here (widget creation, set result, etc).
+ *      shell.open();
+ *      Display display = parent.getDisplay();
+ *      while (!shell.isDisposed()) {
+ *          if (!display.readAndDispatch()) display.sleep();
+ *      }
+ *      return result;
+ *  }
+ * }
+ * </pre></code>
+ * <p>
+ * Note: The <em>modality</em> styles supported by this class
+ * are treated as <em>HINT</em>s, because not all are supported
+ * by every subclass on every platform. If a modality style is
+ * not supported, it is "upgraded" to a more restrictive modality
+ * style that is supported.  For example, if <code>PRIMARY_MODAL</code>
+ * is not supported by a particular dialog, it would be upgraded to
+ * <code>APPLICATION_MODAL</code>. In addition, as is the case
+ * for shells, the window manager for the desktop on which the
+ * instance is visible has ultimate control over the appearance
+ * and behavior of the instance, including its modality.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>APPLICATION_MODAL, PRIMARY_MODAL, SYSTEM_MODAL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles APPLICATION_MODAL, PRIMARY_MODAL,
+ * and SYSTEM_MODAL may be specified.
+ * </p>
+ *
+ * @see Shell
+ */
+
+public abstract class Dialog {
+    int style;
+    Shell parent;
+    char[] title;
+
+/**
+ * Constructs a new instance of this class given only its
+ * parent.
+ *
+ * @param parent a shell which will be the parent of the new instance
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ */
+public this (Shell parent) {
+    this (parent, DWT.PRIMARY_MODAL);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ *
+ * @param parent a shell which will be the parent of the new instance
+ * @param style the style of dialog to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see DWT#PRIMARY_MODAL
+ * @see DWT#APPLICATION_MODAL
+ * @see DWT#SYSTEM_MODAL
+ */
+public this (Shell parent, int style) {
+    checkParent (parent);
+    this.parent = parent;
+    this.style = style;
+    title = "";
+}
+
+/**
+ * Checks that this class can be subclassed.
+ * <p>
+ * IMPORTANT: See the comment in <code>Widget.checkSubclass()</code>.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see Widget#checkSubclass
+ */
+protected void checkSubclass () {
+    //PORTING_TODO: implement Display.isValidClass and Class?
+    /+if (!Display.isValidClass (getClass ())) {
+        error (DWT.ERROR_INVALID_SUBCLASS);
+    }+/
+}
+
+/**
+ * Throws an exception if the specified widget can not be
+ * used as a parent for the receiver.
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ */
+void checkParent (Shell parent) {
+    if (parent is null) error (DWT.ERROR_NULL_ARGUMENT);
+    parent.checkWidget ();
+}
+
+/**
+ * Does whatever dialog specific cleanup is required, and then
+ * uses the code in <code>DWTError.error</code> to handle the error.
+ *
+ * @param code the descriptive error code
+ *
+ * @see DWT#error(int)
+ */
+void error (int code) {
+    DWT.error(code);
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Shell</code>
+ * or null.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Shell getParent () {
+    return parent;
+}
+
+/**
+ * Returns the receiver's style information.
+ * <p>
+ * Note that, the value which is returned by this method <em>may
+ * not match</em> the value which was provided to the constructor
+ * when the receiver was created.
+ * </p>
+ *
+ * @return the style bits
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getStyle () {
+    return style;
+}
+
+/**
+ * Returns the receiver's text, which is the string that the
+ * window manager will typically display as the receiver's
+ * <em>title</em>. If the text has not previously been set,
+ * returns an empty string.
+ *
+ * @return the text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public char[] getText () {
+    return title;
+}
+
+/**
+ * Sets the receiver's text, which is the string that the
+ * window manager will typically display as the receiver's
+ * <em>title</em>, to the argument, which must not be null.
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (char[] string) {
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    title = string;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/DirectoryDialog.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.DirectoryDialog;
+
+import dwt.widgets.Dialog;
+import dwt.widgets.Shell;
+
+class DirectoryDialog : Dialog {
+    public this (Shell parent, int style) {
+        super (parent, style);
+    }
+}
+
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.internal.Callback;
+import dwt.internal.win32.BROWSEINFO;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class allow the user to navigate
+ * the file system and select a directory.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+
+public class DirectoryDialog extends Dialog {
+    String message = "", filterPath = "";  //$NON-NLS-1$//$NON-NLS-2$
+    String directoryPath;
+
+/**
+ * Constructs a new instance of this class given only its parent.
+ *
+ * @param parent a shell which will be the parent of the new instance
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public DirectoryDialog (Shell parent) {
+    this (parent, DWT.PRIMARY_MODAL);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a shell which will be the parent of the new instance
+ * @param style the style of dialog to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public DirectoryDialog (Shell parent, int style) {
+    super (parent, style);
+    checkSubclass ();
+}
+
+int BrowseCallbackProc (int hwnd, int uMsg, int lParam, int lpData) {
+    switch (uMsg) {
+        case OS.BFFM_INITIALIZED:
+            if (filterPath !is null && filterPath.length () !is 0) {
+                /* Use the character encoding for the default locale */
+                TCHAR buffer = new TCHAR (0, filterPath.replace ('/', '\\'), true);
+                OS.SendMessage (hwnd, OS.BFFM_SETSELECTION, 1, buffer);
+            }
+            if (title !is null && title.length () !is 0) {
+                /* Use the character encoding for the default locale */
+                TCHAR buffer = new TCHAR (0, title, true);
+                OS.SetWindowText (hwnd, buffer);
+            }
+            break;
+        case OS.BFFM_VALIDATEFAILEDA:
+        case OS.BFFM_VALIDATEFAILEDW:
+            /* Use the character encoding for the default locale */
+            int length = OS.IsUnicode ? OS.wcslen (lParam) : OS.strlen (lParam);
+            TCHAR buffer = new TCHAR (0, length);
+            int byteCount = buffer.length () * TCHAR.sizeof;
+            OS.MoveMemory (buffer, lParam, byteCount);
+            directoryPath = buffer.toString (0, length);
+            break;
+    }
+    return 0;
+}
+
+/**
+ * Returns the path which the dialog will use to filter
+ * the directories it shows.
+ *
+ * @return the filter path
+ *
+ * @see #setFilterPath
+ */
+public String getFilterPath () {
+    return filterPath;
+}
+
+/**
+ * Returns the dialog's message, which is a description of
+ * the purpose for which it was opened. This message will be
+ * visible on the dialog while it is open.
+ *
+ * @return the message
+ */
+public String getMessage () {
+    return message;
+}
+
+/**
+ * Makes the dialog visible and brings it to the front
+ * of the display.
+ *
+ * @return a string describing the absolute path of the selected directory,
+ *         or null if the dialog was cancelled or an error occurred
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li>
+ * </ul>
+ */
+public String open () {
+    if (OS.IsWinCE) DWT.error (DWT.ERROR_NOT_IMPLEMENTED);
+
+    int hHeap = OS.GetProcessHeap ();
+
+    /* Get the owner HWND for the dialog */
+    int hwndOwner = 0;
+    if (parent !is null) hwndOwner = parent.handle;
+
+    /* Copy the message to OS memory */
+    int lpszTitle = 0;
+    if (message.length () !is 0) {
+        String string = message;
+        if (string.indexOf ('&') !is -1) {
+            int length = string.length ();
+            char [] buffer = new char [length * 2];
+            int index = 0;
+            for (int i=0; i<length; i++) {
+                char ch = string.charAt (i);
+                if (ch is '&') buffer [index++] = '&';
+                buffer [index++] = ch;
+            }
+            string = new String (buffer, 0, index);
+        }
+        /* Use the character encoding for the default locale */
+        TCHAR buffer = new TCHAR (0, string, true);
+        int byteCount = buffer.length () * TCHAR.sizeof;
+        lpszTitle = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+        OS.MoveMemory (lpszTitle, buffer, byteCount);
+    }
+
+    /* Create the BrowseCallbackProc */
+    Callback callback = new Callback (this, "BrowseCallbackProc", 4); //$NON-NLS-1$
+    int lpfn = callback.getAddress ();
+    if (lpfn is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS);
+
+    /* Make the parent shell be temporary modal */
+    Shell oldModal = null;
+    Display display = parent.getDisplay ();
+    if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) !is 0) {
+        oldModal = display.getModalDialogShell ();
+        display.setModalDialogShell (parent);
+    }
+
+    directoryPath = null;
+    BROWSEINFO lpbi = new BROWSEINFO ();
+    lpbi.hwndOwner = hwndOwner;
+    lpbi.lpszTitle = lpszTitle;
+    lpbi.ulFlags = OS.BIF_NEWDIALOGSTYLE | OS.BIF_RETURNONLYFSDIRS | OS.BIF_EDITBOX | OS.BIF_VALIDATE;
+    lpbi.lpfn = lpfn;
+    /*
+    * Bug in Windows.  On some hardware configurations, SHBrowseForFolder()
+    * causes warning dialogs with the message "There is no disk in the drive
+    * Please insert a disk into \Device\Harddisk0\DR0".  This is possibly
+    * caused by SHBrowseForFolder() calling internally GetVolumeInformation().
+    * MSDN for GetVolumeInformation() says:
+    *
+    * "If you are attempting to obtain information about a floppy drive
+    * that does not have a floppy disk or a CD-ROM drive that does not
+    * have a compact disc, the system displays a message box asking the
+    * user to insert a floppy disk or a compact disc, respectively.
+    * To prevent the system from displaying this message box, call the
+    * SetErrorMode function with SEM_FAILCRITICALERRORS."
+    *
+    * The fix is to save and restore the error mode using SetErrorMode()
+    * with the SEM_FAILCRITICALERRORS flag around SHBrowseForFolder().
+    */
+    int oldErrorMode = OS.SetErrorMode (OS.SEM_FAILCRITICALERRORS);
+
+    /*
+    * Bug in Windows.  When a WH_MSGFILTER hook is used to run code
+    * during the message loop for SHBrowseForFolder(), running code
+    * in the hook can cause a GP.  Specifically, SetWindowText()
+    * for static controls seemed to make the problem happen.
+    * The fix is to disable async messages while the directory
+    * dialog is open.
+    *
+    * NOTE:  This only happens in versions of the comctl32.dll
+    * earlier than 6.0.
+    */
+    bool oldRunMessages = display.runMessages;
+    if (OS.COMCTL32_MAJOR < 6) display.runMessages = false;
+    int lpItemIdList = OS.SHBrowseForFolder (lpbi);
+    if (OS.COMCTL32_MAJOR < 6) display.runMessages = oldRunMessages;
+    OS.SetErrorMode (oldErrorMode);
+
+    /* Clear the temporary dialog modal parent */
+    if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) !is 0) {
+        display.setModalDialogShell (oldModal);
+    }
+
+    bool success = lpItemIdList !is 0;
+    if (success) {
+        /* Use the character encoding for the default locale */
+        TCHAR buffer = new TCHAR (0, OS.MAX_PATH);
+        if (OS.SHGetPathFromIDList (lpItemIdList, buffer)) {
+            directoryPath = buffer.toString (0, buffer.strlen ());
+            filterPath = directoryPath;
+        }
+    }
+
+    /* Free the BrowseCallbackProc */
+    callback.dispose ();
+
+    /* Free the OS memory */
+    if (lpszTitle !is 0) OS.HeapFree (hHeap, 0, lpszTitle);
+
+    /* Free the pointer to the ITEMIDLIST */
+    int [] ppMalloc = new int [1];
+    if (OS.SHGetMalloc (ppMalloc) is OS.S_OK) {
+        /* void Free (struct IMalloc *this, void *pv); */
+        OS.VtblCall (5, ppMalloc [0], lpItemIdList);
+    }
+
+    /*
+    * This code is intentionally commented.  On some
+    * platforms, the owner window is repainted right
+    * away when a dialog window exits.  This behavior
+    * is currently unspecified.
+    */
+//  if (hwndOwner !is 0) OS.UpdateWindow (hwndOwner);
+
+    /* Return the directory path */
+    if (!success) return null;
+    return directoryPath;
+}
+
+/**
+ * Sets the path that the dialog will use to filter
+ * the directories it shows to the argument, which may
+ * be null. If the string is null, then the operating
+ * system's default filter path will be used.
+ * <p>
+ * Note that the path string is platform dependent.
+ * For convenience, either '/' or '\' can be used
+ * as a path separator.
+ * </p>
+ *
+ * @param string the filter path
+ */
+public void setFilterPath (String string) {
+    filterPath = string;
+}
+
+/**
+ * Sets the dialog's message, which is a description of
+ * the purpose for which it was opened. This message will be
+ * visible on the dialog while it is open.
+ *
+ * @param string the message
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ */
+public void setMessage (String string) {
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    message = string;
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Display.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,4465 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Display;
+
+import dwt.graphics.Device;
+import dwt.widgets.Tray;
+
+class Display : Device {
+    Tray tray;
+    void wakeThread();
+    public void wake () ;
+    public bool isValidThread() ;
+    static Display getDefault();
+    static Display getCurrent();
+}
+/++
+import dwt.DWT;
+import dwt.DWTError;
+import dwt.DWTException;
+import dwt.graphics.Color;
+import dwt.graphics.Cursor;
+import dwt.graphics.Device;
+import dwt.graphics.DeviceData;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Image;
+import dwt.graphics.ImageData;
+import dwt.graphics.PaletteData;
+import dwt.graphics.Point;
+import dwt.graphics.RGB;
+import dwt.graphics.Rectangle;
+import dwt.graphics.Resource;
+import dwt.internal.Callback;
+import dwt.internal.ImageList;
+import dwt.internal.win32.BITMAP;
+import dwt.internal.win32.BITMAPINFOHEADER;
+import dwt.internal.win32.HIGHCONTRAST;
+import dwt.internal.win32.ICONINFO;
+import dwt.internal.win32.INPUT;
+import dwt.internal.win32.KEYBDINPUT;
+import dwt.internal.win32.LOGFONT;
+import dwt.internal.win32.LOGFONTA;
+import dwt.internal.win32.LOGFONTW;
+import dwt.internal.win32.MONITORINFO;
+import dwt.internal.win32.MOUSEINPUT;
+import dwt.internal.win32.MSG;
+import dwt.internal.win32.NONCLIENTMETRICS;
+import dwt.internal.win32.NONCLIENTMETRICSA;
+import dwt.internal.win32.NONCLIENTMETRICSW;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.STARTUPINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class are responsible for managing the
+ * connection between DWT and the underlying operating
+ * system. Their most important function is to implement
+ * the DWT event loop in terms of the platform event model.
+ * They also provide various methods for accessing information
+ * about the operating system, and have overall control over
+ * the operating system resources which DWT allocates.
+ * <p>
+ * Applications which are built with DWT will <em>almost always</em>
+ * require only a single display. In particular, some platforms
+ * which DWT supports will not allow more than one <em>active</em>
+ * display. In other words, some platforms do not support
+ * creating a new display if one already exists that has not been
+ * sent the <code>dispose()</code> message.
+ * <p>
+ * In DWT, the thread which creates a <code>Display</code>
+ * instance is distinguished as the <em>user-interface thread</em>
+ * for that display.
+ * </p>
+ * The user-interface thread for a particular display has the
+ * following special attributes:
+ * <ul>
+ * <li>
+ * The event loop for that display must be run from the thread.
+ * </li>
+ * <li>
+ * Some DWT API methods (notably, most of the public methods in
+ * <code>Widget</code> and its subclasses), may only be called
+ * from the thread. (To support multi-threaded user-interface
+ * applications, class <code>Display</code> provides inter-thread
+ * communication methods which allow threads other than the
+ * user-interface thread to request that it perform operations
+ * on their behalf.)
+ * </li>
+ * <li>
+ * The thread is not allowed to construct other
+ * <code>Display</code>s until that display has been disposed.
+ * (Note that, this is in addition to the restriction mentioned
+ * above concerning platform support for multiple displays. Thus,
+ * the only way to have multiple simultaneously active displays,
+ * even on platforms which support it, is to have multiple threads.)
+ * </li>
+ * </ul>
+ * Enforcing these attributes allows DWT to be implemented directly
+ * on the underlying operating system's event model. This has
+ * numerous benefits including smaller footprint, better use of
+ * resources, safer memory management, clearer program logic,
+ * better performance, and fewer overall operating system threads
+ * required. The down side however, is that care must be taken
+ * (only) when constructing multi-threaded applications to use the
+ * inter-thread communication mechanisms which this class provides
+ * when required.
+ * </p><p>
+ * All DWT API methods which may only be called from the user-interface
+ * thread are distinguished in their documentation by indicating that
+ * they throw the "<code>ERROR_THREAD_INVALID_ACCESS</code>"
+ * DWT exception.
+ * </p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Close, Dispose, Settings</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ * @see #syncExec
+ * @see #asyncExec
+ * @see #wake
+ * @see #readAndDispatch
+ * @see #sleep
+ * @see Device#dispose
+ */
+
+public class Display extends Device {
+
+    /**
+     * the handle to the OS message queue
+     * (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 MSG msg = new MSG ();
+
+    /* Windows and Events */
+    Event [] eventQueue;
+    Callback windowCallback;
+    int windowProc, threadId;
+    TCHAR windowClass, windowShadowClass;
+    static int WindowClassCount;
+    static final String WindowName = "SWT_Window"; //$NON-NLS-1$
+    static final String WindowShadowName = "SWT_WindowShadow"; //$NON-NLS-1$
+    EventTable eventTable, filterTable;
+
+    /* Widget Table */
+    int [] indexTable;
+    Control lastControl, lastGetControl;
+    int freeSlot, lastHwnd, lastGetHwnd;
+    Control [] controlTable;
+    static final int GROW_SIZE = 1024;
+    static final int SWT_OBJECT_INDEX;
+    static final bool USE_PROPERTY = !OS.IsWinCE;
+    static {
+        if (USE_PROPERTY) {
+            SWT_OBJECT_INDEX = OS.GlobalAddAtom (new TCHAR (0, "SWT_OBJECT_INDEX", true)); //$NON-NLS-1$
+        } else {
+            SWT_OBJECT_INDEX = 0;
+        }
+    }
+
+    /* Startup info */
+    static STARTUPINFO lpStartupInfo;
+    static {
+        if (!OS.IsWinCE) {
+            lpStartupInfo = new STARTUPINFO ();
+            lpStartupInfo.cb = STARTUPINFO.sizeof;
+            OS.GetStartupInfo (lpStartupInfo);
+        }
+    }
+
+    /* XP Themes */
+    int hButtonTheme, hEditTheme, hExplorerBarTheme, hScrollBarTheme, hTabTheme;
+    static final char [] BUTTON = new char [] {'B', 'U', 'T', 'T', 'O', 'N', 0};
+    static final char [] EDIT = new char [] {'E', 'D', 'I', 'T', 0};
+    static final char [] EXPLORER = new char [] {'E', 'X', 'P', 'L', 'O', 'R', 'E', 'R', 0};
+    static final char [] EXPLORERBAR = new char [] {'E', 'X', 'P', 'L', 'O', 'R', 'E', 'R', 'B', 'A', 'R', 0};
+    static final char [] SCROLLBAR = new char [] {'S', 'C', 'R', 'O', 'L', 'L', 'B', 'A', 'R', 0};
+    static final char [] LISTVIEW = new char [] {'L', 'I', 'S', 'T', 'V', 'I', 'E', 'W', 0};
+    static final char [] TAB = new char [] {'T', 'A', 'B', 0};
+    static final char [] TREEVIEW = new char [] {'T', 'R', 'E', 'E', 'V', 'I', 'E', 'W', 0};
+
+    /* Focus */
+    int focusEvent;
+    Control focusControl;
+
+    /* Menus */
+    Menu [] bars, popups;
+    MenuItem [] items;
+
+    /*
+    * The start value for WM_COMMAND id's.
+    * Windows reserves the values 0..100.
+    *
+    * The SmartPhone DWT resource file reserves
+    * the values 101..107.
+    */
+    static final int ID_START = 108;
+
+    /* Filter Hook */
+    Callback msgFilterCallback;
+    int msgFilterProc, filterHook;
+    MSG hookMsg = new MSG ();
+    bool runDragDrop = true;
+
+    /* Idle Hook */
+    Callback foregroundIdleCallback;
+    int foregroundIdleProc, idleHook;
+
+    /* Message Hook and Embedding */
+    bool ignoreNextKey;
+    Callback getMsgCallback, embeddedCallback;
+    int getMsgProc, msgHook, embeddedHwnd, embeddedProc;
+    static final String AWT_WINDOW_CLASS = "SunAwtWindow";
+    static final short [] ACCENTS = new short [] {'~', '`', '\'', '^', '"'};
+
+    /* Sync/Async Widget Communication */
+    Synchronizer synchronizer = new Synchronizer (this);
+    bool runMessages = true, runMessagesInIdle = false;
+    static final String RUN_MESSAGES_IN_IDLE_KEY = "org.eclipse.swt.internal.win32.runMessagesInIdle"; //$NON-NLS-1$
+    Thread thread;
+
+    /* Display Shutdown */
+    Runnable [] disposeList;
+
+    /* System Tray */
+    Tray tray;
+    int nextTrayId;
+
+    /* Timers */
+    int [] timerIds;
+    Runnable [] timerList;
+    int nextTimerId = SETTINGS_ID + 1;
+    static final int SETTINGS_ID = 100;
+    static final int SETTINGS_DELAY = 2000;
+
+    /* Keyboard and Mouse */
+    RECT clickRect;
+    int clickCount, lastTime, lastButton, lastClickHwnd;
+    int lastKey, lastAscii, lastMouse;
+    bool lastVirtual, lastNull, lastDead;
+    byte [] keyboard = new byte [256];
+    bool accelKeyHit, mnemonicKeyHit;
+    bool lockActiveWindow, captureChanged, xMouse;
+
+    /* Tool Tips */
+    int nextToolTipId;
+
+    /* MDI */
+    bool ignoreRestoreFocus;
+    Control lastHittestControl;
+    int lastHittest;
+
+    /* Message Only Window */
+    Callback messageCallback;
+    int hwndMessage, messageProc;
+
+    /* System Resources */
+    LOGFONT lfSystemFont;
+    Font systemFont;
+    Image errorImage, infoImage, questionImage, warningIcon;
+    Cursor [] cursors = new Cursor [DWT.CURSOR_HAND + 1];
+    Resource [] resources;
+    static final int RESOURCE_SIZE = 1 + 4 + DWT.CURSOR_HAND + 1;
+
+    /* ImageList Cache */
+    ImageList[] imageList, toolImageList, toolHotImageList, toolDisabledImageList;
+
+    /* Custom Colors for ChooseColor */
+    int lpCustColors;
+
+    /* Sort Indicators */
+    Image upArrow, downArrow;
+
+    /* Table */
+    char [] tableBuffer;
+
+    /* Display Data */
+    Object data;
+    String [] keys;
+    Object [] values;
+
+    /* Key Mappings */
+    static final int [] [] KeyTable = {
+
+        /* Keyboard and Mouse Masks */
+        {OS.VK_MENU,    DWT.ALT},
+        {OS.VK_SHIFT,   DWT.SHIFT},
+        {OS.VK_CONTROL, DWT.CONTROL},
+//      {OS.VK_????,    DWT.COMMAND},
+
+        /* NOT CURRENTLY USED */
+//      {OS.VK_LBUTTON, DWT.BUTTON1},
+//      {OS.VK_MBUTTON, DWT.BUTTON3},
+//      {OS.VK_RBUTTON, DWT.BUTTON2},
+
+        /* Non-Numeric Keypad Keys */
+        {OS.VK_UP,      DWT.ARROW_UP},
+        {OS.VK_DOWN,    DWT.ARROW_DOWN},
+        {OS.VK_LEFT,    DWT.ARROW_LEFT},
+        {OS.VK_RIGHT,   DWT.ARROW_RIGHT},
+        {OS.VK_PRIOR,   DWT.PAGE_UP},
+        {OS.VK_NEXT,    DWT.PAGE_DOWN},
+        {OS.VK_HOME,    DWT.HOME},
+        {OS.VK_END,     DWT.END},
+        {OS.VK_INSERT,  DWT.INSERT},
+
+        /* Virtual and Ascii Keys */
+        {OS.VK_BACK,    DWT.BS},
+        {OS.VK_RETURN,  DWT.CR},
+        {OS.VK_DELETE,  DWT.DEL},
+        {OS.VK_ESCAPE,  DWT.ESC},
+        {OS.VK_RETURN,  DWT.LF},
+        {OS.VK_TAB,     DWT.TAB},
+
+        /* Functions Keys */
+        {OS.VK_F1,  DWT.F1},
+        {OS.VK_F2,  DWT.F2},
+        {OS.VK_F3,  DWT.F3},
+        {OS.VK_F4,  DWT.F4},
+        {OS.VK_F5,  DWT.F5},
+        {OS.VK_F6,  DWT.F6},
+        {OS.VK_F7,  DWT.F7},
+        {OS.VK_F8,  DWT.F8},
+        {OS.VK_F9,  DWT.F9},
+        {OS.VK_F10, DWT.F10},
+        {OS.VK_F11, DWT.F11},
+        {OS.VK_F12, DWT.F12},
+        {OS.VK_F13, DWT.F13},
+        {OS.VK_F14, DWT.F14},
+        {OS.VK_F15, DWT.F15},
+
+        /* Numeric Keypad Keys */
+        {OS.VK_MULTIPLY,    DWT.KEYPAD_MULTIPLY},
+        {OS.VK_ADD,         DWT.KEYPAD_ADD},
+        {OS.VK_RETURN,      DWT.KEYPAD_CR},
+        {OS.VK_SUBTRACT,    DWT.KEYPAD_SUBTRACT},
+        {OS.VK_DECIMAL,     DWT.KEYPAD_DECIMAL},
+        {OS.VK_DIVIDE,      DWT.KEYPAD_DIVIDE},
+        {OS.VK_NUMPAD0,     DWT.KEYPAD_0},
+        {OS.VK_NUMPAD1,     DWT.KEYPAD_1},
+        {OS.VK_NUMPAD2,     DWT.KEYPAD_2},
+        {OS.VK_NUMPAD3,     DWT.KEYPAD_3},
+        {OS.VK_NUMPAD4,     DWT.KEYPAD_4},
+        {OS.VK_NUMPAD5,     DWT.KEYPAD_5},
+        {OS.VK_NUMPAD6,     DWT.KEYPAD_6},
+        {OS.VK_NUMPAD7,     DWT.KEYPAD_7},
+        {OS.VK_NUMPAD8,     DWT.KEYPAD_8},
+        {OS.VK_NUMPAD9,     DWT.KEYPAD_9},
+//      {OS.VK_????,        DWT.KEYPAD_EQUAL},
+
+        /* Other keys */
+        {OS.VK_CAPITAL,     DWT.CAPS_LOCK},
+        {OS.VK_NUMLOCK,     DWT.NUM_LOCK},
+        {OS.VK_SCROLL,      DWT.SCROLL_LOCK},
+        {OS.VK_PAUSE,       DWT.PAUSE},
+        {OS.VK_CANCEL,      DWT.BREAK},
+        {OS.VK_SNAPSHOT,    DWT.PRINT_SCREEN},
+//      {OS.VK_????,        DWT.HELP},
+
+    };
+
+    /* Multiple Displays */
+    static Display Default;
+    static Display [] Displays = new Display [4];
+
+    /* Multiple Monitors */
+    Monitor[] monitors = null;
+    int monitorCount = 0;
+
+    /* Modality */
+    Shell [] modalShells;
+    Shell modalDialogShell;
+    static bool TrimEnabled = false;
+
+    /* Private DWT Window Messages */
+    static final int SWT_GETACCELCOUNT  = OS.WM_APP;
+    static final int SWT_GETACCEL       = OS.WM_APP + 1;
+    static final int SWT_KEYMSG         = OS.WM_APP + 2;
+    static final int SWT_DESTROY        = OS.WM_APP + 3;
+    static final int SWT_TRAYICONMSG    = OS.WM_APP + 4;
+    static final int SWT_NULL           = OS.WM_APP + 5;
+    static final int SWT_RUNASYNC       = OS.WM_APP + 6;
+    static int SWT_TASKBARCREATED;
+    static int SWT_RESTORECARET;
+
+    /* Workaround for Adobe Reader 7.0 */
+    int hitCount;
+
+    /* Package Name */
+    static final String PACKAGE_PREFIX = "org.eclipse.swt.widgets."; //$NON-NLS-1$
+    /*
+    * This code is intentionally commented.  In order
+    * to support CLDC, .class cannot be used because
+    * it does not compile on some Java compilers when
+    * they are targeted for CLDC.
+    */
+//  static {
+//      String name = Display.class.getName ();
+//      int index = name.lastIndexOf ('.');
+//      PACKAGE_PREFIX = name.substring (0, index + 1);
+//  }
+
+    /*
+    * TEMPORARY CODE.  Install the runnable that
+    * gets the current display. This code will
+    * be removed in the future.
+    */
+    static {
+        DeviceFinder = new Runnable () {
+            public void run () {
+                Device device = getCurrent ();
+                if (device is null) {
+                    device = getDefault ();
+                }
+                setDevice (device);
+            }
+        };
+    }
+
+/*
+* TEMPORARY CODE.
+*/
+static void setDevice (Device device) {
+    CurrentDevice = device;
+}
+
+/**
+ * Constructs a new instance of this class.
+ * <p>
+ * Note: The resulting display is marked as the <em>current</em>
+ * display. If this is the first display which has been
+ * constructed since the application started, it is also
+ * marked as the <em>default</em> display.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if called from a thread that already created an existing display</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see #getCurrent
+ * @see #getDefault
+ * @see Widget#checkSubclass
+ * @see Shell
+ */
+public Display () {
+    this (null);
+}
+
+/**
+ * Constructs a new instance of this class using the parameter.
+ *
+ * @param data the device data
+ */
+public Display (DeviceData data) {
+    super (data);
+}
+
+Control _getFocusControl () {
+    return findControl (OS.GetFocus ());
+}
+
+void addBar (Menu menu) {
+    if (bars is null) bars = new Menu [4];
+    int length = bars.length;
+    for (int i=0; i<length; i++) {
+        if (bars [i] is menu) return;
+    }
+    int index = 0;
+    while (index < length) {
+        if (bars [index] is null) break;
+        index++;
+    }
+    if (index is length) {
+        Menu [] newBars = new Menu [length + 4];
+        System.arraycopy (bars, 0, newBars, 0, length);
+        bars = newBars;
+    }
+    bars [index] = menu;
+}
+
+void addControl (int handle, Control control) {
+    if (handle is 0) return;
+    if (freeSlot is -1) {
+        int length = (freeSlot = indexTable.length) + GROW_SIZE;
+        int [] newIndexTable = new int [length];
+        Control [] newControlTable = new Control [length];
+        System.arraycopy (indexTable, 0, newIndexTable, 0, freeSlot);
+        System.arraycopy (controlTable, 0, newControlTable, 0, freeSlot);
+        for (int i=freeSlot; i<length-1; i++) newIndexTable [i] = i + 1;
+        newIndexTable [length - 1] = -1;
+        indexTable = newIndexTable;
+        controlTable = newControlTable;
+    }
+    if (USE_PROPERTY) {
+        OS.SetProp (handle, SWT_OBJECT_INDEX, freeSlot + 1);
+    } else {
+        OS.SetWindowLong (handle, OS.GWL_USERDATA, freeSlot + 1);
+    }
+    int oldSlot = freeSlot;
+    freeSlot = indexTable [oldSlot];
+    indexTable [oldSlot] = -2;
+    controlTable [oldSlot] = control;
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when an event of the given type occurs anywhere
+ * in a widget. The event type is one of the event constants
+ * defined in class <code>DWT</code>. When the event does occur,
+ * the listener is notified by sending it the <code>handleEvent()</code>
+ * message.
+ * <p>
+ * Setting the type of an event to <code>DWT.None</code> from
+ * within the <code>handleEvent()</code> method can be used to
+ * change the event type and stop subsequent Java listeners
+ * from running. Because event filters run before other listeners,
+ * event filters can both block other listeners and set arbitrary
+ * fields within an event. For this reason, event filters are both
+ * powerful and dangerous. They should generally be avoided for
+ * performance, debugging and code maintenance reasons.
+ * </p>
+ *
+ * @param eventType the type of event to listen for
+ * @param listener the listener which should be notified when the event occurs
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Listener
+ * @see DWT
+ * @see #removeFilter
+ * @see #removeListener
+ *
+ * @since 3.0
+ */
+public void addFilter (int eventType, Listener listener) {
+    checkDevice ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (filterTable is null) filterTable = new EventTable ();
+    filterTable.hook (eventType, listener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when an event of the given type occurs. The event
+ * type is one of the event constants defined in class <code>DWT</code>.
+ * When the event does occur in the display, the listener is notified by
+ * sending it the <code>handleEvent()</code> message.
+ *
+ * @param eventType the type of event to listen for
+ * @param listener the listener which should be notified when the event occurs
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Listener
+ * @see DWT
+ * @see #removeListener
+ *
+ * @since 2.0
+ */
+public void addListener (int eventType, Listener listener) {
+    checkDevice ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) eventTable = new EventTable ();
+    eventTable.hook (eventType, listener);
+}
+
+void addMenuItem (MenuItem item) {
+    if (items is null) items = new MenuItem [64];
+    for (int i=0; i<items.length; i++) {
+        if (items [i] is null) {
+            item.id = i + ID_START;
+            items [i] = item;
+            return;
+        }
+    }
+    item.id = items.length + ID_START;
+    MenuItem [] newItems = new MenuItem [items.length + 64];
+    newItems [items.length] = item;
+    System.arraycopy (items, 0, newItems, 0, items.length);
+    items = newItems;
+}
+
+void addPopup (Menu menu) {
+    if (popups is null) popups = new Menu [4];
+    int length = popups.length;
+    for (int i=0; i<length; i++) {
+        if (popups [i] is menu) return;
+    }
+    int index = 0;
+    while (index < length) {
+        if (popups [index] is null) break;
+        index++;
+    }
+    if (index is length) {
+        Menu [] newPopups = new Menu [length + 4];
+        System.arraycopy (popups, 0, newPopups, 0, length);
+        popups = newPopups;
+    }
+    popups [index] = menu;
+}
+
+int asciiKey (int key) {
+    if (OS.IsWinCE) return 0;
+
+    /* Get the current keyboard. */
+    for (int i=0; i<keyboard.length; i++) keyboard [i] = 0;
+    if (!OS.GetKeyboardState (keyboard)) return 0;
+
+    /* Translate the key to ASCII or UNICODE using the virtual keyboard */
+    if (OS.IsUnicode) {
+        char [] result = new char [1];
+        if (OS.ToUnicode (key, key, keyboard, result, 1, 0) is 1) return result [0];
+    } else {
+        short [] result = new short [1];
+        if (OS.ToAscii (key, key, keyboard, result, 0) is 1) return result [0];
+    }
+    return 0;
+}
+
+/**
+ * Causes the <code>run()</code> method of the runnable to
+ * be invoked by the user-interface thread at the next
+ * reasonable opportunity. The caller of this method continues
+ * to run in parallel, and is not notified when the
+ * runnable has completed.  Specifying <code>null</code> as the
+ * runnable simply wakes the user-interface thread when run.
+ * <p>
+ * Note that at the time the runnable is invoked, widgets
+ * that have the receiver as their display may have been
+ * disposed. Therefore, it is necessary to check for this
+ * case inside the runnable before accessing the widget.
+ * </p>
+ *
+ * @param runnable code to run on the user-interface thread or <code>null</code>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #syncExec
+ */
+public void asyncExec (Runnable runnable) {
+    if (isDisposed ()) error (DWT.ERROR_DEVICE_DISPOSED);
+    synchronizer.asyncExec (runnable);
+}
+
+/**
+ * Causes the system hardware to emit a short sound
+ * (if it supports this capability).
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void beep () {
+    checkDevice ();
+    OS.MessageBeep (OS.MB_OK);
+}
+
+/**
+ * Checks that this class can be subclassed.
+ * <p>
+ * IMPORTANT: See the comment in <code>Widget.checkSubclass()</code>.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see Widget#checkSubclass
+ */
+protected void checkSubclass () {
+    if (!isValidClass (getClass ())) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+protected void checkDevice () {
+    if (thread is null) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (thread !is Thread.currentThread ()) error (DWT.ERROR_THREAD_INVALID_ACCESS);
+    if (isDisposed ()) error (DWT.ERROR_DEVICE_DISPOSED);
+}
+
+static synchronized void checkDisplay (Thread thread, bool multiple) {
+    for (int i=0; i<Displays.length; i++) {
+        if (Displays [i] !is null) {
+            if (!multiple) DWT.error (DWT.ERROR_NOT_IMPLEMENTED, null, " [multiple displays]");
+            if (Displays [i].thread is thread) DWT.error (DWT.ERROR_THREAD_INVALID_ACCESS);
+        }
+    }
+}
+
+void clearModal (Shell shell) {
+    if (modalShells is null) return;
+    int index = 0, length = modalShells.length;
+    while (index < length) {
+        if (modalShells [index] is shell) break;
+        if (modalShells [index] is null) return;
+        index++;
+    }
+    if (index is length) return;
+    System.arraycopy (modalShells, index + 1, modalShells, index, --length - index);
+    modalShells [length] = null;
+    if (index is 0 && modalShells [0] is null) modalShells = null;
+    Shell [] shells = getShells ();
+    for (int i=0; i<shells.length; i++) shells [i].updateModal ();
+}
+
+int controlKey (int key) {
+    int upper = OS.CharUpper ((short) key);
+    if (64 <= upper && upper <= 95) return upper & 0xBF;
+    return key;
+}
+
+/**
+ * Requests that the connection between DWT and the underlying
+ * operating system be closed.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Device#dispose
+ *
+ * @since 2.0
+ */
+public void close () {
+    checkDevice ();
+    Event event = new Event ();
+    sendEvent (DWT.Close, event);
+    if (event.doit) dispose ();
+}
+
+/**
+ * Creates the device in the operating system.  If the device
+ * does not have a handle, this method may do nothing depending
+ * on the device.
+ * <p>
+ * This method is called before <code>init</code>.
+ * </p>
+ *
+ * @param data the DeviceData which describes the receiver
+ *
+ * @see #init
+ */
+protected void create (DeviceData data) {
+    checkSubclass ();
+    checkDisplay (thread = Thread.currentThread (), true);
+    createDisplay (data);
+    register (this);
+    if (Default is null) Default = this;
+}
+
+void createDisplay (DeviceData data) {
+}
+
+static int create32bitDIB (Image image) {
+    int transparentPixel = -1, alpha = -1, hMask = 0, hBitmap = 0;
+    byte[] alphaData = null;
+    switch (image.type) {
+        case DWT.ICON:
+            ICONINFO info = new ICONINFO ();
+            OS.GetIconInfo (image.handle, info);
+            hBitmap = info.hbmColor;
+            hMask = info.hbmMask;
+            break;
+        case DWT.BITMAP:
+            ImageData data = image.getImageData ();
+            hBitmap = image.handle;
+            alpha = data.alpha;
+            alphaData = data.alphaData;
+            transparentPixel = data.transparentPixel;
+            break;
+    }
+    BITMAP bm = new BITMAP ();
+    OS.GetObject (hBitmap, BITMAP.sizeof, bm);
+    int imgWidth = bm.bmWidth;
+    int imgHeight = bm.bmHeight;
+    int hDC = OS.GetDC (0);
+    int srcHdc = OS.CreateCompatibleDC (hDC);
+    int oldSrcBitmap = OS.SelectObject (srcHdc, hBitmap);
+    int memHdc = OS.CreateCompatibleDC (hDC);
+    BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER ();
+    bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+    bmiHeader.biWidth = imgWidth;
+    bmiHeader.biHeight = -imgHeight;
+    bmiHeader.biPlanes = 1;
+    bmiHeader.biBitCount = (short)32;
+    bmiHeader.biCompression = OS.BI_RGB;
+    byte [] bmi = new byte [BITMAPINFOHEADER.sizeof];
+    OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+    int [] pBits = new int [1];
+    int memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
+    if (memDib is 0) DWT.error (DWT.ERROR_NO_HANDLES);
+    int oldMemBitmap = OS.SelectObject (memHdc, memDib);
+    BITMAP dibBM = new BITMAP ();
+    OS.GetObject (memDib, BITMAP.sizeof, dibBM);
+    int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight;
+    OS.BitBlt (memHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, 0, OS.SRCCOPY);
+    byte red = 0, green = 0, blue = 0;
+    if (transparentPixel !is -1) {
+        if (bm.bmBitsPixel <= 8) {
+            byte [] color = new byte [4];
+            OS.GetDIBColorTable (srcHdc, transparentPixel, 1, color);
+            blue = color [0];
+            green = color [1];
+            red = color [2];
+        } else {
+            switch (bm.bmBitsPixel) {
+                case 16:
+                    blue = (byte)((transparentPixel & 0x1F) << 3);
+                    green = (byte)((transparentPixel & 0x3E0) >> 2);
+                    red = (byte)((transparentPixel & 0x7C00) >> 7);
+                    break;
+                case 24:
+                    blue = (byte)((transparentPixel & 0xFF0000) >> 16);
+                    green = (byte)((transparentPixel & 0xFF00) >> 8);
+                    red = (byte)(transparentPixel & 0xFF);
+                    break;
+                case 32:
+                    blue = (byte)((transparentPixel & 0xFF000000) >>> 24);
+                    green = (byte)((transparentPixel & 0xFF0000) >> 16);
+                    red = (byte)((transparentPixel & 0xFF00) >> 8);
+                    break;
+            }
+        }
+    }
+    byte [] srcData = new byte [sizeInBytes];
+    OS.MoveMemory (srcData, pBits [0], sizeInBytes);
+    if (hMask !is 0) {
+        OS.SelectObject(srcHdc, hMask);
+        for (int y = 0, dp = 0; y < imgHeight; ++y) {
+            for (int x = 0; x < imgWidth; ++x) {
+                if (OS.GetPixel(srcHdc, x, y) !is 0) {
+                    srcData [dp + 0] = srcData [dp + 1] = srcData [dp + 2] = srcData[dp + 3] = (byte)0;
+                } else {
+                    srcData[dp + 3] = (byte)0xFF;
+                }
+                dp += 4;
+            }
+        }
+    } else if (alpha !is -1) {
+        for (int y = 0, dp = 0; y < imgHeight; ++y) {
+            for (int x = 0; x < imgWidth; ++x) {
+                srcData [dp + 3] = (byte)alpha;
+                if (srcData [dp + 3] is 0) srcData [dp + 0] = srcData [dp + 1] = srcData [dp + 2] = 0;
+                dp += 4;
+            }
+        }
+    } else if (alphaData !is null) {
+        for (int y = 0, dp = 0, ap = 0; y < imgHeight; ++y) {
+            for (int x = 0; x < imgWidth; ++x) {
+                srcData [dp + 3] = alphaData [ap++];
+                if (srcData [dp + 3] is 0) srcData [dp + 0] = srcData [dp + 1] = srcData [dp + 2] = 0;
+                dp += 4;
+            }
+        }
+    } else if (transparentPixel !is -1) {
+        for (int y = 0, dp = 0; y < imgHeight; ++y) {
+            for (int x = 0; x < imgWidth; ++x) {
+                if (srcData [dp] is blue && srcData [dp + 1] is green && srcData [dp + 2] is red) {
+                    srcData [dp + 0] = srcData [dp + 1] = srcData [dp + 2] = srcData [dp + 3] = (byte)0;
+                } else {
+                    srcData [dp + 3] = (byte)0xFF;
+                }
+                dp += 4;
+            }
+        }
+    } else {
+        for (int y = 0, dp = 0; y < imgHeight; ++y) {
+            for (int x = 0; x < imgWidth; ++x) {
+                srcData [dp + 3] = (byte)0xFF;
+                dp += 4;
+            }
+        }
+    }
+    OS.MoveMemory (pBits [0], srcData, sizeInBytes);
+    OS.SelectObject (srcHdc, oldSrcBitmap);
+    OS.SelectObject (memHdc, oldMemBitmap);
+    OS.DeleteObject (srcHdc);
+    OS.DeleteObject (memHdc);
+    OS.ReleaseDC (0, hDC);
+    if (hBitmap !is image.handle && hBitmap !is 0) OS.DeleteObject (hBitmap);
+    if (hMask !is 0) OS.DeleteObject (hMask);
+    return memDib;
+}
+
+static int create32bitDIB (int hBitmap, int alpha, byte [] alphaData, int transparentPixel) {
+    BITMAP bm = new BITMAP ();
+    OS.GetObject (hBitmap, BITMAP.sizeof, bm);
+    int imgWidth = bm.bmWidth;
+    int imgHeight = bm.bmHeight;
+    int hDC = OS.GetDC (0);
+    int srcHdc = OS.CreateCompatibleDC (hDC);
+    int oldSrcBitmap = OS.SelectObject (srcHdc, hBitmap);
+    int memHdc = OS.CreateCompatibleDC (hDC);
+    BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER ();
+    bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
+    bmiHeader.biWidth = imgWidth;
+    bmiHeader.biHeight = -imgHeight;
+    bmiHeader.biPlanes = 1;
+    bmiHeader.biBitCount = (short)32;
+    bmiHeader.biCompression = OS.BI_RGB;
+    byte [] bmi = new byte [BITMAPINFOHEADER.sizeof];
+    OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
+    int [] pBits = new int [1];
+    int memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
+    if (memDib is 0) DWT.error (DWT.ERROR_NO_HANDLES);
+    int oldMemBitmap = OS.SelectObject (memHdc, memDib);
+    BITMAP dibBM = new BITMAP ();
+    OS.GetObject (memDib, BITMAP.sizeof, dibBM);
+    int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight;
+    OS.BitBlt (memHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, 0, OS.SRCCOPY);
+    byte red = 0, green = 0, blue = 0;
+    if (transparentPixel !is -1) {
+        if (bm.bmBitsPixel <= 8) {
+            byte [] color = new byte [4];
+            OS.GetDIBColorTable (srcHdc, transparentPixel, 1, color);
+            blue = color [0];
+            green = color [1];
+            red = color [2];
+        } else {
+            switch (bm.bmBitsPixel) {
+                case 16:
+                    blue = (byte)((transparentPixel & 0x1F) << 3);
+                    green = (byte)((transparentPixel & 0x3E0) >> 2);
+                    red = (byte)((transparentPixel & 0x7C00) >> 7);
+                    break;
+                case 24:
+                    blue = (byte)((transparentPixel & 0xFF0000) >> 16);
+                    green = (byte)((transparentPixel & 0xFF00) >> 8);
+                    red = (byte)(transparentPixel & 0xFF);
+                    break;
+                case 32:
+                    blue = (byte)((transparentPixel & 0xFF000000) >>> 24);
+                    green = (byte)((transparentPixel & 0xFF0000) >> 16);
+                    red = (byte)((transparentPixel & 0xFF00) >> 8);
+                    break;
+            }
+        }
+    }
+    OS.SelectObject (srcHdc, oldSrcBitmap);
+    OS.SelectObject (memHdc, oldMemBitmap);
+    OS.DeleteObject (srcHdc);
+    OS.DeleteObject (memHdc);
+    OS.ReleaseDC (0, hDC);
+    byte [] srcData = new byte [sizeInBytes];
+    OS.MoveMemory (srcData, pBits [0], sizeInBytes);
+    if (alpha !is -1) {
+        for (int y = 0, dp = 0; y < imgHeight; ++y) {
+            for (int x = 0; x < imgWidth; ++x) {
+                srcData [dp + 3] = (byte)alpha;
+                dp += 4;
+            }
+        }
+    } else if (alphaData !is null) {
+        for (int y = 0, dp = 0, ap = 0; y < imgHeight; ++y) {
+            for (int x = 0; x < imgWidth; ++x) {
+                srcData [dp + 3] = alphaData [ap++];
+                dp += 4;
+            }
+        }
+    } else if (transparentPixel !is -1) {
+        for (int y = 0, dp = 0; y < imgHeight; ++y) {
+            for (int x = 0; x < imgWidth; ++x) {
+                if (srcData [dp] is blue && srcData [dp + 1] is green && srcData [dp + 2] is red) {
+                    srcData [dp + 3] = (byte)0;
+                } else {
+                    srcData [dp + 3] = (byte)0xFF;
+                }
+                dp += 4;
+            }
+        }
+    }
+    OS.MoveMemory (pBits [0], srcData, sizeInBytes);
+    return memDib;
+}
+
+static Image createIcon (Image image) {
+    Device device = image.getDevice ();
+    ImageData data = image.getImageData ();
+    if (data.alpha is -1 && data.alphaData is null) {
+        ImageData mask = data.getTransparencyMask ();
+        return new Image (device, data, mask);
+    }
+    int width = data.width, height = data.height;
+    int hMask, hBitmap;
+    int hDC = device.internal_new_GC (null);
+    int dstHdc = OS.CreateCompatibleDC (hDC), oldDstBitmap;
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) {
+        hBitmap = Display.create32bitDIB (image.handle, data.alpha, data.alphaData, data.transparentPixel);
+        hMask = OS.CreateBitmap (width, height, 1, 1, null);
+        oldDstBitmap = OS.SelectObject (dstHdc, hMask);
+        OS.PatBlt (dstHdc, 0, 0, width, height, OS.BLACKNESS);
+    } else {
+        hMask = Display.createMaskFromAlpha (data, width, height);
+        /* Icons need black pixels where the mask is transparent */
+        hBitmap = OS.CreateCompatibleBitmap (hDC, width, height);
+        oldDstBitmap = OS.SelectObject (dstHdc, hBitmap);
+        int srcHdc = OS.CreateCompatibleDC (hDC);
+        int oldSrcBitmap = OS.SelectObject (srcHdc, image.handle);
+        OS.PatBlt (dstHdc, 0, 0, width, height, OS.BLACKNESS);
+        OS.BitBlt (dstHdc, 0, 0, width, height, srcHdc, 0, 0, OS.SRCINVERT);
+        OS.SelectObject (srcHdc, hMask);
+        OS.BitBlt (dstHdc, 0, 0, width, height, srcHdc, 0, 0, OS.SRCAND);
+        OS.SelectObject (srcHdc, image.handle);
+        OS.BitBlt (dstHdc, 0, 0, width, height, srcHdc, 0, 0, OS.SRCINVERT);
+        OS.SelectObject (srcHdc, oldSrcBitmap);
+        OS.DeleteDC (srcHdc);
+    }
+    OS.SelectObject (dstHdc, oldDstBitmap);
+    OS.DeleteDC (dstHdc);
+    device.internal_dispose_GC (hDC, null);
+    ICONINFO info = new ICONINFO ();
+    info.fIcon = true;
+    info.hbmColor = hBitmap;
+    info.hbmMask = hMask;
+    int hIcon = OS.CreateIconIndirect (info);
+    if (hIcon is 0) DWT.error(DWT.ERROR_NO_HANDLES);
+    OS.DeleteObject (hBitmap);
+    OS.DeleteObject (hMask);
+    return Image.win32_new (device, DWT.ICON, hIcon);
+}
+
+static int createMaskFromAlpha (ImageData data, int destWidth, int destHeight) {
+    int srcWidth = data.width;
+    int srcHeight = data.height;
+    ImageData mask = ImageData.internal_new (srcWidth, srcHeight, 1,
+            new PaletteData(new RGB [] {new RGB (0, 0, 0), new RGB (0xff, 0xff, 0xff)}),
+            2, null, 1, null, null, -1, -1, -1, 0, 0, 0, 0);
+    int ap = 0;
+    for (int y = 0; y < mask.height; y++) {
+        for (int x = 0; x < mask.width; x++) {
+            mask.setPixel (x, y, (data.alphaData [ap++] & 0xff) <= 127 ? 1 : 0);
+        }
+    }
+    int hMask = OS.CreateBitmap (srcWidth, srcHeight, 1, 1, mask.data);
+    if (srcWidth !is destWidth || srcHeight !is destHeight) {
+        int hdc = OS.GetDC (0);
+        int hdc1 = OS.CreateCompatibleDC (hdc);
+        OS.SelectObject (hdc1, hMask);
+        int hdc2 = OS.CreateCompatibleDC (hdc);
+        int hMask2 = OS.CreateBitmap (destWidth, destHeight, 1, 1, null);
+        OS.SelectObject (hdc2, hMask2);
+        if (!OS.IsWinCE) OS.SetStretchBltMode(hdc2, OS.COLORONCOLOR);
+        OS.StretchBlt (hdc2, 0, 0, destWidth, destHeight, hdc1, 0, 0, srcWidth, srcHeight, OS.SRCCOPY);
+        OS.DeleteDC (hdc1);
+        OS.DeleteDC (hdc2);
+        OS.ReleaseDC (0, hdc);
+        OS.DeleteObject(hMask);
+        hMask = hMask2;
+    }
+    return hMask;
+}
+
+static synchronized void deregister (Display display) {
+    for (int i=0; i<Displays.length; i++) {
+        if (display is Displays [i]) Displays [i] = null;
+    }
+}
+
+/**
+ * Destroys the device in the operating system and releases
+ * the device's handle.  If the device does not have a handle,
+ * this method may do nothing depending on the device.
+ * <p>
+ * This method is called after <code>release</code>.
+ * </p>
+ * @see Device#dispose
+ * @see #release
+ */
+protected void destroy () {
+    if (this is Default) Default = null;
+    deregister (this);
+    destroyDisplay ();
+}
+
+void destroyDisplay () {
+}
+
+/**
+ * Causes the <code>run()</code> method of the runnable to
+ * be invoked by the user-interface thread just before the
+ * receiver is disposed.  Specifying a <code>null</code> runnable
+ * is ignored.
+ *
+ * @param runnable code to run at dispose time.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public void disposeExec (Runnable runnable) {
+    checkDevice ();
+    if (disposeList is null) disposeList = new Runnable [4];
+    for (int i=0; i<disposeList.length; i++) {
+        if (disposeList [i] is null) {
+            disposeList [i] = runnable;
+            return;
+        }
+    }
+    Runnable [] newDisposeList = new Runnable [disposeList.length + 4];
+    System.arraycopy (disposeList, 0, newDisposeList, 0, disposeList.length);
+    newDisposeList [disposeList.length] = runnable;
+    disposeList = newDisposeList;
+}
+
+void drawMenuBars () {
+    if (bars is null) return;
+    for (int i=0; i<bars.length; i++) {
+        Menu menu = bars [i];
+        if (menu !is null && !menu.isDisposed ()) menu.update ();
+    }
+    bars = null;
+}
+
+int embeddedProc (int hwnd, int msg, int wParam, int lParam) {
+    switch (msg) {
+        case SWT_KEYMSG: {
+            MSG keyMsg = new MSG ();
+            OS.MoveMemory (keyMsg, lParam, MSG.sizeof);
+            OS.TranslateMessage (keyMsg);
+            OS.DispatchMessage (keyMsg);
+            int hHeap = OS.GetProcessHeap ();
+            OS.HeapFree (hHeap, 0, lParam);
+            break;
+        }
+        case SWT_DESTROY: {
+            OS.DestroyWindow (hwnd);
+            if (embeddedCallback !is null) embeddedCallback.dispose ();
+            if (getMsgCallback !is null) getMsgCallback.dispose ();
+            embeddedCallback = getMsgCallback = null;
+            embeddedProc = getMsgProc = 0;
+            break;
+        }
+    }
+    return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+}
+
+/**
+ * Does whatever display specific cleanup is required, and then
+ * uses the code in <code>DWTError.error</code> to handle the error.
+ *
+ * @param code the descriptive error code
+ *
+ * @see DWT#error(int)
+ */
+void error (int code) {
+    DWT.error (code);
+}
+
+bool filterEvent (Event event) {
+    if (filterTable !is null) filterTable.sendEvent (event);
+    return false;
+}
+
+bool filters (int eventType) {
+    if (filterTable is null) return false;
+    return filterTable.hooks (eventType);
+}
+
+bool filterMessage (MSG msg) {
+    int message = msg.message;
+    if (OS.WM_KEYFIRST <= message && message <= OS.WM_KEYLAST) {
+        Control control = findControl (msg.hwnd);
+        if (control !is null) {
+            if (translateAccelerator (msg, control) || translateMnemonic (msg, control) || translateTraversal (msg, control)) {
+                lastAscii = lastKey = 0;
+                lastVirtual = lastNull = lastDead = false;
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+Control findControl (int handle) {
+    if (handle is 0) return null;
+    int hwndOwner = 0;
+    do {
+        Control control = getControl (handle);
+        if (control !is null) return control;
+        hwndOwner = OS.GetWindow (handle, OS.GW_OWNER);
+        handle = OS.GetParent (handle);
+    } while (handle !is 0 && handle !is hwndOwner);
+    return null;
+}
+
+/**
+ * Given the operating system handle for a widget, returns
+ * the instance of the <code>Widget</code> subclass which
+ * represents it in the currently running application, if
+ * such exists, or null if no matching widget can be found.
+ * <p>
+ * <b>IMPORTANT:</b> This method should not be called from
+ * application code. The arguments are platform-specific.
+ * </p>
+ *
+ * @param handle the handle for the widget
+ * @return the DWT widget that the handle represents
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Widget findWidget (int handle) {
+    checkDevice ();
+    return getControl (handle);
+}
+
+/**
+ * Given the operating system handle for a widget,
+ * and widget-specific id, returns the instance of
+ * the <code>Widget</code> subclass which represents
+ * the handle/id pair in the currently running application,
+ * if such exists, or null if no matching widget can be found.
+ * <p>
+ * <b>IMPORTANT:</b> This method should not be called from
+ * application code. The arguments are platform-specific.
+ * </p>
+ *
+ * @param handle the handle for the widget
+ * @param id the id for the subwidget (usually an item)
+ * @return the DWT widget that the handle/id pair represents
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Widget findWidget (int handle, int id) {
+    checkDevice ();
+    Control control = getControl (handle);
+    return control !is null ? control.findItem (id) : null;
+}
+
+/**
+ * Given a widget and a widget-specific id, returns the
+ * instance of the <code>Widget</code> subclass which represents
+ * the widget/id pair in the currently running application,
+ * if such exists, or null if no matching widget can be found.
+ *
+ * @param widget the widget
+ * @param id the id for the subwidget (usually an item)
+ * @return the DWT subwidget (usually an item) that the widget/id pair represents
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public Widget findWidget (Widget widget, int id) {
+    checkDevice ();
+    if (widget instanceof Control) {
+        return findWidget (((Control) widget).handle, id);
+    }
+    return null;
+}
+
+int foregroundIdleProc (int code, int wParam, int lParam) {
+    if (runMessages) {
+        if (code >= 0) {
+            if (getMessageCount () !is 0) {
+                if (runMessagesInIdle) {
+                    OS.PostMessage (hwndMessage, SWT_RUNASYNC, 0, 0);
+                }
+                wakeThread ();
+            }
+        }
+    }
+    return OS.CallNextHookEx (idleHook, code, wParam, lParam);
+}
+
+/**
+ * Returns the display which the given thread is the
+ * user-interface thread for, or null if the given thread
+ * is not a user-interface thread for any display.  Specifying
+ * <code>null</code> as the thread will return <code>null</code>
+ * for the display.
+ *
+ * @param thread the user-interface thread
+ * @return the display for the given thread
+ */
+public static synchronized Display findDisplay (Thread thread) {
+    for (int i=0; i<Displays.length; i++) {
+        Display display = Displays [i];
+        if (display !is null && display.thread is thread) {
+            return display;
+        }
+    }
+    return null;
+}
+
+/**
+ * Returns the currently active <code>Shell</code>, or null
+ * if no shell belonging to the currently running application
+ * is active.
+ *
+ * @return the active shell or null
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Shell getActiveShell () {
+    checkDevice ();
+    Control control = findControl (OS.GetActiveWindow ());
+    return control !is null ? control.getShell () : null;
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location.
+ *
+ * @return the bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Rectangle getBounds () {
+    checkDevice ();
+    if (OS.GetSystemMetrics (OS.SM_CMONITORS) < 2) {
+        int width = OS.GetSystemMetrics (OS.SM_CXSCREEN);
+        int height = OS.GetSystemMetrics (OS.SM_CYSCREEN);
+        return new Rectangle (0, 0, width, height);
+    }
+    int x = OS.GetSystemMetrics (OS.SM_XVIRTUALSCREEN);
+    int y = OS.GetSystemMetrics (OS.SM_YVIRTUALSCREEN);
+    int width = OS.GetSystemMetrics (OS.SM_CXVIRTUALSCREEN);
+    int height = OS.GetSystemMetrics (OS.SM_CYVIRTUALSCREEN);
+    return new Rectangle (x, y, width, height);
+}
+
+/**
+ * Returns the display which the currently running thread is
+ * the user-interface thread for, or null if the currently
+ * running thread is not a user-interface thread for any display.
+ *
+ * @return the current display
+ */
+public static synchronized Display getCurrent () {
+    return findDisplay (Thread.currentThread ());
+}
+
+int getClickCount (int type, int button, int hwnd, int lParam) {
+    switch (type) {
+        case DWT.MouseDown:
+            int doubleClick = OS.GetDoubleClickTime ();
+            if (clickRect is null) clickRect = new RECT ();
+            int deltaTime = Math.abs (lastTime - getLastEventTime ());
+            POINT pt = new POINT ();
+            pt.x = (short) (lParam & 0xFFFF);
+            pt.y = (short) (lParam >> 16);
+            if (lastClickHwnd is hwnd && lastButton is button && (deltaTime <= doubleClick) && OS.PtInRect (clickRect, pt)) {
+                clickCount++;
+            } else {
+                clickCount = 1;
+            }
+            //FALL THROUGH
+        case DWT.MouseDoubleClick:
+            lastButton = button;
+            lastClickHwnd = hwnd;
+            lastTime = getLastEventTime ();
+            int xInset = OS.GetSystemMetrics (OS.SM_CXDOUBLECLK) / 2;
+            int yInset = OS.GetSystemMetrics (OS.SM_CYDOUBLECLK) / 2;
+            int x = (short) (lParam & 0xFFFF), y = (short) (lParam >> 16);
+            OS.SetRect (clickRect, x - xInset, y - yInset, x + xInset, y + yInset);
+            //FALL THROUGH
+        case DWT.MouseUp:
+            return clickCount;
+    }
+    return 0;
+}
+
+/**
+ * Returns a rectangle which describes the area of the
+ * receiver which is capable of displaying data.
+ *
+ * @return the client area
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #getBounds
+ */
+public Rectangle getClientArea () {
+    checkDevice ();
+    if (OS.GetSystemMetrics (OS.SM_CMONITORS) < 2) {
+        RECT rect = new RECT ();
+        OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0);
+        int width = rect.right - rect.left;
+        int height = rect.bottom - rect.top;
+        return new Rectangle (rect.left, rect.top, width, height);
+    }
+    int x = OS.GetSystemMetrics (OS.SM_XVIRTUALSCREEN);
+    int y = OS.GetSystemMetrics (OS.SM_YVIRTUALSCREEN);
+    int width = OS.GetSystemMetrics (OS.SM_CXVIRTUALSCREEN);
+    int height = OS.GetSystemMetrics (OS.SM_CYVIRTUALSCREEN);
+    return new Rectangle (x, y, width, height);
+}
+
+Control getControl (int handle) {
+    if (handle is 0) return null;
+    if (lastControl !is null && lastHwnd is handle) {
+        return lastControl;
+    }
+    if (lastGetControl !is null && lastGetHwnd is handle) {
+        return lastGetControl;
+    }
+    int index;
+    if (USE_PROPERTY) {
+        index = OS.GetProp (handle, SWT_OBJECT_INDEX) - 1;
+    } else {
+        index = OS.GetWindowLong (handle, OS.GWL_USERDATA) - 1;
+    }
+    if (0 <= index && index < controlTable.length) {
+        Control control = controlTable [index];
+        /*
+        * Because GWL_USERDATA can be used by native widgets that
+        * do not belong to DWT, it is possible that GWL_USERDATA
+        * could return an index that is in the range of the table,
+        * but was not put there by DWT.  Therefore, it is necessary
+        * to check the handle of the control that is in the table
+        * against the handle that provided the GWL_USERDATA.
+        */
+        if (control !is null && control.checkHandle (handle)) {
+            lastGetHwnd = handle;
+            lastGetControl = control;
+            return control;
+        }
+    }
+    return null;
+}
+
+/**
+ * Returns the control which the on-screen pointer is currently
+ * over top of, or null if it is not currently over one of the
+ * controls built by the currently running application.
+ *
+ * @return the control under the cursor
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Control getCursorControl () {
+    checkDevice ();
+    POINT pt = new POINT ();
+    if (!OS.GetCursorPos (pt)) return null;
+    return findControl (OS.WindowFromPoint (pt));
+}
+
+/**
+ * Returns the location of the on-screen pointer relative
+ * to the top left corner of the screen.
+ *
+ * @return the cursor location
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Point getCursorLocation () {
+    checkDevice ();
+    POINT pt = new POINT ();
+    OS.GetCursorPos (pt);
+    return new Point (pt.x, pt.y);
+}
+
+/**
+ * Returns an array containing the recommended cursor sizes.
+ *
+ * @return the array of cursor sizes
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Point [] getCursorSizes () {
+    checkDevice ();
+    return new Point [] {
+        new Point (OS.GetSystemMetrics (OS.SM_CXCURSOR), OS.GetSystemMetrics (OS.SM_CYCURSOR))};
+}
+
+/**
+ * Returns the default display. One is created (making the
+ * thread that invokes this method its user-interface thread)
+ * if it did not already exist.
+ *
+ * @return the default display
+ */
+public static synchronized Display getDefault () {
+    if (Default is null) Default = new Display ();
+    return Default;
+}
+
+static bool isValidClass (Class clazz) {
+    String name = clazz.getName ();
+    int index = name.lastIndexOf ('.');
+    return name.substring (0, index + 1).equals (PACKAGE_PREFIX);
+}
+
+/**
+ * Returns the application defined property of the receiver
+ * with the specified name, or null if it has not been set.
+ * <p>
+ * Applications may have associated arbitrary objects with the
+ * receiver in this fashion. If the objects stored in the
+ * properties need to be notified when the display is disposed
+ * of, it is the application's responsibility to provide a
+ * <code>disposeExec()</code> handler which does so.
+ * </p>
+ *
+ * @param key the name of the property
+ * @return the value of the property or null if it has not been set
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the key is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #setData(String, Object)
+ * @see #disposeExec(Runnable)
+ */
+public Object getData (String key) {
+    checkDevice ();
+    if (key is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (key.equals (RUN_MESSAGES_IN_IDLE_KEY)) {
+        return new bool (runMessagesInIdle);
+    }
+    if (keys is null) return null;
+    for (int i=0; i<keys.length; i++) {
+        if (keys [i].equals (key)) return values [i];
+    }
+    return null;
+}
+
+/**
+ * Returns the application defined, display specific data
+ * associated with the receiver, or null if it has not been
+ * set. The <em>display specific data</em> is a single,
+ * unnamed field that is stored with every display.
+ * <p>
+ * Applications may put arbitrary objects in this field. If
+ * the object stored in the display specific data needs to
+ * be notified when the display is disposed of, it is the
+ * application's responsibility to provide a
+ * <code>disposeExec()</code> handler which does so.
+ * </p>
+ *
+ * @return the display specific data
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #setData(Object)
+ * @see #disposeExec(Runnable)
+ */
+public Object getData () {
+    checkDevice ();
+    return data;
+}
+
+/**
+ * Returns the button dismissal alignment, one of <code>LEFT</code> or <code>RIGHT</code>.
+ * The button dismissal alignment is the ordering that should be used when positioning the
+ * default dismissal button for a dialog.  For example, in a dialog that contains an OK and
+ * CANCEL button, on platforms where the button dismissal alignment is <code>LEFT</code>, the
+ * button ordering should be OK/CANCEL.  When button dismissal alignment is <code>RIGHT</code>,
+ * the button ordering should be CANCEL/OK.
+ *
+ * @return the button dismissal order
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public int getDismissalAlignment () {
+    checkDevice ();
+    return DWT.LEFT;
+}
+
+
+/**
+ * Returns the longest duration, in milliseconds, between
+ * two mouse button clicks that will be considered a
+ * <em>double click</em> by the underlying operating system.
+ *
+ * @return the double click time
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public int getDoubleClickTime () {
+    checkDevice ();
+    return OS.GetDoubleClickTime ();
+}
+
+/**
+ * Returns the control which currently has keyboard focus,
+ * or null if keyboard events are not currently going to
+ * any of the controls built by the currently running
+ * application.
+ *
+ * @return the control under the cursor
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Control getFocusControl () {
+    checkDevice ();
+    if (focusControl !is null && !focusControl.isDisposed ()) {
+        return focusControl;
+    }
+    return _getFocusControl ();
+}
+
+String getFontName (LOGFONT logFont) {
+    char[] chars;
+    if (OS.IsUnicode) {
+        chars = ((LOGFONTW)logFont).lfFaceName;
+    } else {
+        chars = new char[OS.LF_FACESIZE];
+        byte[] bytes = ((LOGFONTA)logFont).lfFaceName;
+        OS.MultiByteToWideChar (OS.CP_ACP, OS.MB_PRECOMPOSED, bytes, bytes.length, chars, chars.length);
+    }
+    int index = 0;
+    while (index < chars.length) {
+        if (chars [index] is 0) break;
+        index++;
+    }
+    return new String (chars, 0, index);
+}
+
+/**
+ * Returns true when the high contrast mode is enabled.
+ * Otherwise, false is returned.
+ * <p>
+ * Note: This operation is a hint and is not supported on
+ * platforms that do not have this concept.
+ * </p>
+ *
+ * @return the high contrast mode
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public bool getHighContrast () {
+    checkDevice ();
+    if (OS.IsWinCE) return false;
+    HIGHCONTRAST pvParam = new HIGHCONTRAST ();
+    pvParam.cbSize = HIGHCONTRAST.sizeof;
+    OS.SystemParametersInfo (OS.SPI_GETHIGHCONTRAST, 0, pvParam, 0);
+    return (pvParam.dwFlags & OS.HCF_HIGHCONTRASTON) !is 0;
+}
+
+/**
+ * Returns the maximum allowed depth of icons on this display, in bits per pixel.
+ * On some platforms, this may be different than the actual depth of the display.
+ *
+ * @return the maximum icon depth
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Device#getDepth
+ */
+public int getIconDepth () {
+    checkDevice ();
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) {
+        if (getDepth () >= 24) return 32;
+    }
+
+    /* Use the character encoding for the default locale */
+    TCHAR buffer1 = new TCHAR (0, "Control Panel\\Desktop\\WindowMetrics", true); //$NON-NLS-1$
+
+    int [] phkResult = new int [1];
+    int result = OS.RegOpenKeyEx (OS.HKEY_CURRENT_USER, buffer1, 0, OS.KEY_READ, phkResult);
+    if (result !is 0) return 4;
+    int depth = 4;
+    int [] lpcbData = new int [1];
+
+    /* Use the character encoding for the default locale */
+    TCHAR buffer2 = new TCHAR (0, "Shell Icon BPP", true); //$NON-NLS-1$
+    result = OS.RegQueryValueEx (phkResult [0], buffer2, 0, null, (TCHAR) null, lpcbData);
+    if (result is 0) {
+        TCHAR lpData = new TCHAR (0, lpcbData [0] / TCHAR.sizeof);
+        result = OS.RegQueryValueEx (phkResult [0], buffer2, 0, null, lpData, lpcbData);
+        if (result is 0) {
+            try {
+                depth = Integer.parseInt (lpData.toString (0, lpData.strlen ()));
+            } catch (NumberFormatException e) {}
+        }
+    }
+    OS.RegCloseKey (phkResult [0]);
+    return depth;
+}
+
+/**
+ * Returns an array containing the recommended icon sizes.
+ *
+ * @return the array of icon sizes
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Decorations#setImages(Image[])
+ *
+ * @since 3.0
+ */
+public Point [] getIconSizes () {
+    checkDevice ();
+    return new Point [] {
+        new Point (OS.GetSystemMetrics (OS.SM_CXSMICON), OS.GetSystemMetrics (OS.SM_CYSMICON)),
+        new Point (OS.GetSystemMetrics (OS.SM_CXICON), OS.GetSystemMetrics (OS.SM_CYICON)),
+    };
+}
+
+ImageList getImageList (int style, int width, int height) {
+    if (imageList is null) imageList = new ImageList [4];
+
+    int i = 0;
+    int length = imageList.length;
+    while (i < length) {
+        ImageList list = imageList [i];
+        if (list is null) break;
+        Point size = list.getImageSize();
+        if (size.x is width && size.y is height) {
+            if (list.getStyle () is style) {
+                list.addRef();
+                return list;
+            }
+        }
+        i++;
+    }
+
+    if (i is length) {
+        ImageList [] newList = new ImageList [length + 4];
+        System.arraycopy (imageList, 0, newList, 0, length);
+        imageList = newList;
+    }
+
+    ImageList list = new ImageList (style);
+    imageList [i] = list;
+    list.addRef();
+    return list;
+}
+
+ImageList getImageListToolBar (int style, int width, int height) {
+    if (toolImageList is null) toolImageList = new ImageList [4];
+
+    int i = 0;
+    int length = toolImageList.length;
+    while (i < length) {
+        ImageList list = toolImageList [i];
+        if (list is null) break;
+        Point size = list.getImageSize();
+        if (size.x is width && size.y is height) {
+            if (list.getStyle () is style) {
+                list.addRef();
+                return list;
+            }
+        }
+        i++;
+    }
+
+    if (i is length) {
+        ImageList [] newList = new ImageList [length + 4];
+        System.arraycopy (toolImageList, 0, newList, 0, length);
+        toolImageList = newList;
+    }
+
+    ImageList list = new ImageList (style);
+    toolImageList [i] = list;
+    list.addRef();
+    return list;
+}
+
+ImageList getImageListToolBarDisabled (int style, int width, int height) {
+    if (toolDisabledImageList is null) toolDisabledImageList = new ImageList [4];
+
+    int i = 0;
+    int length = toolDisabledImageList.length;
+    while (i < length) {
+        ImageList list = toolDisabledImageList [i];
+        if (list is null) break;
+        Point size = list.getImageSize();
+        if (size.x is width && size.y is height) {
+            if (list.getStyle () is style) {
+                list.addRef();
+                return list;
+            }
+        }
+        i++;
+    }
+
+    if (i is length) {
+        ImageList [] newList = new ImageList [length + 4];
+        System.arraycopy (toolDisabledImageList, 0, newList, 0, length);
+        toolDisabledImageList = newList;
+    }
+
+    ImageList list = new ImageList (style);
+    toolDisabledImageList [i] = list;
+    list.addRef();
+    return list;
+}
+
+ImageList getImageListToolBarHot (int style, int width, int height) {
+    if (toolHotImageList is null) toolHotImageList = new ImageList [4];
+
+    int i = 0;
+    int length = toolHotImageList.length;
+    while (i < length) {
+        ImageList list = toolHotImageList [i];
+        if (list is null) break;
+        Point size = list.getImageSize();
+        if (size.x is width && size.y is height) {
+            if (list.getStyle () is style) {
+                list.addRef();
+                return list;
+            }
+        }
+        i++;
+    }
+
+    if (i is length) {
+        ImageList [] newList = new ImageList [length + 4];
+        System.arraycopy (toolHotImageList, 0, newList, 0, length);
+        toolHotImageList = newList;
+    }
+
+    ImageList list = new ImageList (style);
+    toolHotImageList [i] = list;
+    list.addRef();
+    return list;
+}
+
+int getLastEventTime () {
+    return OS.IsWinCE ? OS.GetTickCount () : OS.GetMessageTime ();
+}
+
+MenuItem getMenuItem (int id) {
+    if (items is null) return null;
+    id = id - ID_START;
+    if (0 <= id && id < items.length) return items [id];
+    return null;
+}
+
+int getMessageCount () {
+    return synchronizer.getMessageCount ();
+}
+
+
+Shell getModalShell () {
+    if (modalShells is null) return null;
+    int index = modalShells.length;
+    while (--index >= 0) {
+        Shell shell = modalShells [index];
+        if (shell !is null) return shell;
+    }
+    return null;
+}
+
+Shell getModalDialogShell () {
+    if (modalDialogShell !is null && modalDialogShell.isDisposed ()) modalDialogShell = null;
+    return modalDialogShell;
+}
+
+/**
+ * Returns an array of monitors attached to the device.
+ *
+ * @return the array of monitors
+ *
+ * @since 3.0
+ */
+public Monitor [] getMonitors () {
+    checkDevice ();
+    if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) {
+        return new Monitor [] {getPrimaryMonitor ()};
+    }
+    monitors = new Monitor [4];
+    Callback callback = new Callback (this, "monitorEnumProc", 4); //$NON-NLS-1$
+    int lpfnEnum = callback.getAddress ();
+    if (lpfnEnum is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS);
+    OS.EnumDisplayMonitors (0, null, lpfnEnum, 0);
+    callback.dispose ();
+    Monitor [] result = new Monitor [monitorCount];
+    System.arraycopy (monitors, 0, result, 0, monitorCount);
+    monitors = null;
+    monitorCount = 0;
+    return result;
+}
+
+int getMsgProc (int code, int wParam, int lParam) {
+    if (embeddedHwnd is 0) {
+        int hInstance = OS.GetModuleHandle (null);
+        embeddedHwnd = OS.CreateWindowEx (0,
+            windowClass,
+            null,
+            OS.WS_OVERLAPPED,
+            0, 0, 0, 0,
+            0,
+            0,
+            hInstance,
+            null);
+        embeddedCallback = new Callback (this, "embeddedProc", 4); //$NON-NLS-1$
+        embeddedProc = embeddedCallback.getAddress ();
+        if (embeddedProc is 0) error (DWT.ERROR_NO_MORE_CALLBACKS);
+        OS.SetWindowLong (embeddedHwnd, OS.GWL_WNDPROC, embeddedProc);
+    }
+    if (code >= 0 && wParam !is OS.PM_NOREMOVE) {
+        MSG msg = new MSG ();
+        OS.MoveMemory (msg, lParam, MSG.sizeof);
+        switch (msg.message) {
+            case OS.WM_KEYDOWN:
+            case OS.WM_KEYUP:
+            case OS.WM_SYSKEYDOWN:
+            case OS.WM_SYSKEYUP: {
+                Control control = findControl (msg.hwnd);
+                if (control !is null) {
+                    int hHeap = OS.GetProcessHeap ();
+                    int keyMsg = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, MSG.sizeof);
+                    OS.MoveMemory (keyMsg, msg, MSG.sizeof);
+                    OS.PostMessage (hwndMessage, SWT_KEYMSG, wParam, keyMsg);
+                    msg.message = OS.WM_NULL;
+                    OS.MoveMemory (lParam, msg, MSG.sizeof);
+                }
+            }
+        }
+    }
+    return OS.CallNextHookEx (msgHook, code, wParam, lParam);
+}
+
+/**
+ * Returns the primary monitor for that device.
+ *
+ * @return the primary monitor
+ *
+ * @since 3.0
+ */
+public Monitor getPrimaryMonitor () {
+    checkDevice ();
+    if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) {
+        Monitor monitor = new Monitor();
+        int width = OS.GetSystemMetrics (OS.SM_CXSCREEN);
+        int height = OS.GetSystemMetrics (OS.SM_CYSCREEN);
+        monitor.width = width;
+        monitor.height = height;
+        RECT rect = new RECT ();
+        OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0);
+        monitor.clientX = rect.left;
+        monitor.clientY = rect.top;
+        monitor.clientWidth = rect.right - rect.left;
+        monitor.clientHeight = rect.bottom - rect.top;
+        return monitor;
+    }
+    monitors = new Monitor [4];
+    Callback callback = new Callback (this, "monitorEnumProc", 4); //$NON-NLS-1$
+    int lpfnEnum = callback.getAddress ();
+    if (lpfnEnum is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS);
+    OS.EnumDisplayMonitors (0, null, lpfnEnum, 0);
+    callback.dispose ();
+    Monitor result = null;
+    MONITORINFO lpmi = new MONITORINFO ();
+    lpmi.cbSize = MONITORINFO.sizeof;
+    for (int i = 0; i < monitorCount; i++) {
+        Monitor monitor = monitors [i];
+        OS.GetMonitorInfo (monitors [i].handle, lpmi);
+        if ((lpmi.dwFlags & OS.MONITORINFOF_PRIMARY) !is 0) {
+            result = monitor;
+            break;
+        }
+    }
+    monitors = null;
+    monitorCount = 0;
+    return result;
+}
+
+/**
+ * Returns a (possibly empty) array containing all shells which have
+ * not been disposed and have the receiver as their display.
+ *
+ * @return the receiver's shells
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Shell [] getShells () {
+    checkDevice ();
+    int index = 0;
+    Shell [] result = new Shell [16];
+    for (int i = 0; i < controlTable.length; i++) {
+        Control control = controlTable [i];
+        if (control !is null && control instanceof Shell) {
+            int j = 0;
+            while (j < index) {
+                if (result [j] is control) break;
+                j++;
+            }
+            if (j is index) {
+                if (index is result.length) {
+                    Shell [] newResult = new Shell [index + 16];
+                    System.arraycopy (result, 0, newResult, 0, index);
+                    result = newResult;
+                }
+                result [index++] = (Shell) control;
+            }
+        }
+    }
+    if (index is result.length) return result;
+    Shell [] newResult = new Shell [index];
+    System.arraycopy (result, 0, newResult, 0, index);
+    return newResult;
+}
+
+Image getSortImage (int direction) {
+    switch (direction) {
+        case DWT.UP: {
+            if (upArrow !is null) return upArrow;
+            Color c1 = getSystemColor (DWT.COLOR_WIDGET_NORMAL_SHADOW);
+            Color c2 = getSystemColor (DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+            Color c3 = getSystemColor (DWT.COLOR_WIDGET_BACKGROUND);
+            PaletteData palette = new PaletteData(new RGB [] {c1.getRGB (), c2.getRGB (), c3.getRGB ()});
+            ImageData imageData = new ImageData (8, 8, 4, palette);
+            imageData.transparentPixel = 2;
+            upArrow = new Image (this, imageData);
+            GC gc = new GC (upArrow);
+            gc.setBackground (c3);
+            gc.fillRectangle (0, 0, 8, 8);
+            gc.setForeground (c1);
+            int [] line1 = new int [] {0,6, 1,6, 1,4, 2,4, 2,2, 3,2, 3,1};
+            gc.drawPolyline (line1);
+            gc.setForeground (c2);
+            int [] line2 = new int [] {0,7, 7,7, 7,6, 6,6, 6,4, 5,4, 5,2, 4,2, 4,1};
+            gc.drawPolyline (line2);
+            gc.dispose ();
+            return upArrow;
+        }
+        case DWT.DOWN: {
+            if (downArrow !is null) return downArrow;
+            Color c1 = getSystemColor (DWT.COLOR_WIDGET_NORMAL_SHADOW);
+            Color c2 = getSystemColor (DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
+            Color c3 = getSystemColor (DWT.COLOR_WIDGET_BACKGROUND);
+            PaletteData palette = new PaletteData (new RGB [] {c1.getRGB (), c2.getRGB (), c3.getRGB ()});
+            ImageData imageData = new ImageData (8, 8, 4, palette);
+            imageData.transparentPixel = 2;
+            downArrow = new Image (this, imageData);
+            GC gc = new GC (downArrow);
+            gc.setBackground (c3);
+            gc.fillRectangle (0, 0, 8, 8);
+            gc.setForeground (c1);
+            int [] line1 = new int [] {7,0, 0,0, 0,1, 1,1, 1,3, 2,3, 2,5, 3,5, 3,6};
+            gc.drawPolyline (line1);
+            gc.setForeground (c2);
+            int [] line2 = new int [] {4,6, 4,5, 5,5, 5,3, 6,3, 6,1, 7,1};
+            gc.drawPolyline (line2);
+            gc.dispose ();
+            return downArrow;
+        }
+    }
+    return null;
+}
+
+/**
+ * Returns the thread that has invoked <code>syncExec</code>
+ * or null if no such runnable is currently being invoked by
+ * the user-interface thread.
+ * <p>
+ * Note: If a runnable invoked by asyncExec is currently
+ * running, this method will return null.
+ * </p>
+ *
+ * @return the receiver's sync-interface thread
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Thread getSyncThread () {
+    if (isDisposed ()) error (DWT.ERROR_DEVICE_DISPOSED);
+    return synchronizer.syncThread;
+}
+
+/**
+ * Returns the matching standard color for the given
+ * constant, which should be one of the color constants
+ * specified in class <code>DWT</code>. Any value other
+ * than one of the DWT color constants which is passed
+ * in will result in the color black. This color should
+ * not be free'd because it was allocated by the system,
+ * not the application.
+ *
+ * @param id the color constant
+ * @return the matching color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see DWT
+ */
+public Color getSystemColor (int id) {
+    checkDevice ();
+    int pixel = 0x00000000;
+    switch (id) {
+        case DWT.COLOR_WIDGET_DARK_SHADOW:      pixel = OS.GetSysColor (OS.COLOR_3DDKSHADOW);   break;
+        case DWT.COLOR_WIDGET_NORMAL_SHADOW:    pixel = OS.GetSysColor (OS.COLOR_3DSHADOW);     break;
+        case DWT.COLOR_WIDGET_LIGHT_SHADOW:     pixel = OS.GetSysColor (OS.COLOR_3DLIGHT);      break;
+        case DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW: pixel = OS.GetSysColor (OS.COLOR_3DHIGHLIGHT);  break;
+        case DWT.COLOR_WIDGET_BACKGROUND:       pixel = OS.GetSysColor (OS.COLOR_3DFACE);   break;
+        case DWT.COLOR_WIDGET_BORDER:       pixel = OS.GetSysColor (OS.COLOR_WINDOWFRAME);  break;
+        case DWT.COLOR_WIDGET_FOREGROUND:
+        case DWT.COLOR_LIST_FOREGROUND:         pixel = OS.GetSysColor (OS.COLOR_WINDOWTEXT);   break;
+        case DWT.COLOR_LIST_BACKGROUND:         pixel = OS.GetSysColor (OS.COLOR_WINDOW);   break;
+        case DWT.COLOR_LIST_SELECTION:      pixel = OS.GetSysColor (OS.COLOR_HIGHLIGHT);    break;
+        case DWT.COLOR_LIST_SELECTION_TEXT:     pixel = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);break;
+        case DWT.COLOR_INFO_FOREGROUND:     pixel = OS.GetSysColor (OS.COLOR_INFOTEXT); break;
+        case DWT.COLOR_INFO_BACKGROUND:     pixel = OS.GetSysColor (OS.COLOR_INFOBK);       break;
+        case DWT.COLOR_TITLE_FOREGROUND:        pixel = OS.GetSysColor (OS.COLOR_CAPTIONTEXT);  break;
+        case DWT.COLOR_TITLE_BACKGROUND:        pixel = OS.GetSysColor (OS.COLOR_ACTIVECAPTION);        break;
+        case DWT.COLOR_TITLE_BACKGROUND_GRADIENT:
+            pixel = OS.GetSysColor (OS.COLOR_GRADIENTACTIVECAPTION);
+            if (pixel is 0) pixel = OS.GetSysColor (OS.COLOR_ACTIVECAPTION);
+            break;
+        case DWT.COLOR_TITLE_INACTIVE_FOREGROUND:       pixel = OS.GetSysColor (OS.COLOR_INACTIVECAPTIONTEXT);  break;
+        case DWT.COLOR_TITLE_INACTIVE_BACKGROUND:           pixel = OS.GetSysColor (OS.COLOR_INACTIVECAPTION);      break;
+        case DWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT:
+            pixel = OS.GetSysColor (OS.COLOR_GRADIENTINACTIVECAPTION);
+            if (pixel is 0) pixel = OS.GetSysColor (OS.COLOR_INACTIVECAPTION);
+            break;
+        default:
+            return super.getSystemColor (id);
+    }
+    return Color.win32_new (this, pixel);
+}
+
+/**
+ * Returns the matching standard platform cursor for the given
+ * constant, which should be one of the cursor constants
+ * specified in class <code>DWT</code>. This cursor should
+ * not be free'd because it was allocated by the system,
+ * not the application.  A value of <code>null</code> will
+ * be returned if the supplied constant is not an DWT cursor
+ * constant.
+ *
+ * @param id the DWT cursor constant
+ * @return the corresponding cursor or <code>null</code>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</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
+ *
+ * @since 3.0
+ */
+public Cursor getSystemCursor (int id) {
+    checkDevice ();
+    if (!(0 <= id && id < cursors.length)) return null;
+    if (cursors [id] is null) {
+        cursors [id] = new Cursor (this, id);
+    }
+    return cursors [id];
+}
+
+/**
+ * Returns a reasonable font for applications to use.
+ * On some platforms, this will match the "default font"
+ * or "system font" if such can be found.  This font
+ * should not be free'd because it was allocated by the
+ * system, not the application.
+ * <p>
+ * Typically, applications which want the default look
+ * should simply not set the font on the widgets they
+ * create. Widgets are always created with the correct
+ * default font for the class of user-interface component
+ * they represent.
+ * </p>
+ *
+ * @return a font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Font getSystemFont () {
+    checkDevice ();
+    if (systemFont !is null) return systemFont;
+    int hFont = 0;
+    if (!OS.IsWinCE) {
+        NONCLIENTMETRICS info = OS.IsUnicode ? (NONCLIENTMETRICS) new NONCLIENTMETRICSW () : new NONCLIENTMETRICSA ();
+        info.cbSize = NONCLIENTMETRICS.sizeof;
+        if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, info, 0)) {
+            LOGFONT logFont = OS.IsUnicode ? (LOGFONT) ((NONCLIENTMETRICSW)info).lfMessageFont : ((NONCLIENTMETRICSA)info).lfMessageFont;
+            hFont = OS.CreateFontIndirect (logFont);
+            lfSystemFont = hFont !is 0 ? logFont : null;
+        }
+    }
+    if (hFont is 0) hFont = OS.GetStockObject (OS.DEFAULT_GUI_FONT);
+    if (hFont is 0) hFont = OS.GetStockObject (OS.SYSTEM_FONT);
+    return systemFont = Font.win32_new (this, hFont);
+}
+
+/**
+ * Returns the matching standard platform image for the given
+ * constant, which should be one of the icon constants
+ * specified in class <code>DWT</code>. This image should
+ * not be free'd because it was allocated by the system,
+ * not the application.  A value of <code>null</code> will
+ * be returned either if the supplied constant is not an
+ * DWT icon constant or if the platform does not define an
+ * image that corresponds to the constant.
+ *
+ * @param id the DWT icon constant
+ * @return the corresponding image or <code>null</code>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see DWT#ICON_ERROR
+ * @see DWT#ICON_INFORMATION
+ * @see DWT#ICON_QUESTION
+ * @see DWT#ICON_WARNING
+ * @see DWT#ICON_WORKING
+ *
+ * @since 3.0
+ */
+public Image getSystemImage (int id) {
+    checkDevice ();
+    switch (id) {
+        case DWT.ICON_ERROR: {
+            if (errorImage !is null) return errorImage;
+            int hIcon = OS.LoadImage (0, OS.OIC_HAND, OS.IMAGE_ICON, 0, 0, OS.LR_SHARED);
+            return errorImage = Image.win32_new (this, DWT.ICON, hIcon);
+        }
+        case DWT.ICON_WORKING:
+        case DWT.ICON_INFORMATION: {
+            if (infoImage !is null) return infoImage;
+            int hIcon = OS.LoadImage (0, OS.OIC_INFORMATION, OS.IMAGE_ICON, 0, 0, OS.LR_SHARED);
+            return infoImage = Image.win32_new (this, DWT.ICON, hIcon);
+        }
+        case DWT.ICON_QUESTION: {
+            if (questionImage !is null) return questionImage;
+            int hIcon = OS.LoadImage (0, OS.OIC_QUES, OS.IMAGE_ICON, 0, 0, OS.LR_SHARED);
+            return questionImage = Image.win32_new (this, DWT.ICON, hIcon);
+        }
+        case DWT.ICON_WARNING: {
+            if (warningIcon !is null) return warningIcon;
+            int hIcon = OS.LoadImage (0, OS.OIC_BANG, OS.IMAGE_ICON, 0, 0, OS.LR_SHARED);
+            return warningIcon = Image.win32_new (this, DWT.ICON, hIcon);
+        }
+    }
+    return null;
+}
+
+/**
+ * Returns the single instance of the system tray or null
+ * when there is no system tray available for the platform.
+ *
+ * @return the system tray or <code>null</code>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Tray getSystemTray () {
+    checkDevice ();
+    if (tray !is null) return tray;
+    if (!OS.IsWinCE) tray = new Tray (this, DWT.NONE);
+    return tray;
+}
+
+/**
+ * Returns the user-interface thread for the receiver.
+ *
+ * @return the receiver's user-interface thread
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Thread getThread () {
+    if (isDisposed ()) error (DWT.ERROR_DEVICE_DISPOSED);
+    return thread;
+}
+
+int hButtonTheme () {
+    if (hButtonTheme !is 0) return hButtonTheme;
+    return hButtonTheme = OS.OpenThemeData (hwndMessage, BUTTON);
+}
+
+int hEditTheme () {
+    if (hEditTheme !is 0) return hEditTheme;
+    return hEditTheme = OS.OpenThemeData (hwndMessage, EDIT);
+}
+
+int hExplorerBarTheme () {
+    if (hExplorerBarTheme !is 0) return hExplorerBarTheme;
+    return hExplorerBarTheme = OS.OpenThemeData (hwndMessage, EXPLORERBAR);
+}
+
+int hScrollBarTheme () {
+    if (hScrollBarTheme !is 0) return hScrollBarTheme;
+    return hScrollBarTheme = OS.OpenThemeData (hwndMessage, SCROLLBAR);
+}
+
+int hTabTheme () {
+    if (hTabTheme !is 0) return hTabTheme;
+    return hTabTheme = OS.OpenThemeData (hwndMessage, TAB);
+}
+
+/**
+ * Invokes platform specific functionality to allocate a new GC handle.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Display</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 data the platform specific GC data
+ * @return the platform specific GC handle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ * @exception DWTError <ul>
+ *    <li>ERROR_NO_HANDLES if a handle could not be obtained for gc creation</li>
+ * </ul>
+ */
+public int internal_new_GC (GCData data) {
+    if (isDisposed()) DWT.error(DWT.ERROR_DEVICE_DISPOSED);
+    int hDC = OS.GetDC (0);
+    if (hDC is 0) DWT.error (DWT.ERROR_NO_HANDLES);
+    if (data !is null) {
+        int mask = DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT;
+        if ((data.style & mask) !is 0) {
+            data.layout = (data.style & DWT.RIGHT_TO_LEFT) !is 0 ? OS.LAYOUT_RTL : 0;
+        } else {
+            data.style |= DWT.LEFT_TO_RIGHT;
+        }
+        data.device = this;
+        data.hFont = getSystemFont ().handle;
+    }
+    return hDC;
+}
+
+/**
+ * Initializes any internal resources needed by the
+ * device.
+ * <p>
+ * This method is called after <code>create</code>.
+ * </p>
+ *
+ * @see #create
+ */
+protected void init () {
+    super.init ();
+
+    /* Create the callbacks */
+    windowCallback = new Callback (this, "windowProc", 4); //$NON-NLS-1$
+    windowProc = windowCallback.getAddress ();
+    if (windowProc is 0) error (DWT.ERROR_NO_MORE_CALLBACKS);
+
+    /* Remember the current thread id */
+    threadId = OS.GetCurrentThreadId ();
+
+    /* Use the character encoding for the default locale */
+    windowClass = new TCHAR (0, WindowName + WindowClassCount, true);
+    windowShadowClass = new TCHAR (0, WindowShadowName + WindowClassCount, true);
+    WindowClassCount++;
+
+    /* Register the DWT window class */
+    int hHeap = OS.GetProcessHeap ();
+    int hInstance = OS.GetModuleHandle (null);
+    WNDCLASS lpWndClass = new WNDCLASS ();
+    lpWndClass.hInstance = hInstance;
+    lpWndClass.lpfnWndProc = windowProc;
+    lpWndClass.style = OS.CS_BYTEALIGNWINDOW | OS.CS_DBLCLKS;
+    lpWndClass.hCursor = OS.LoadCursor (0, OS.IDC_ARROW);
+    int byteCount = windowClass.length () * TCHAR.sizeof;
+    lpWndClass.lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+    OS.MoveMemory (lpWndClass.lpszClassName, windowClass, byteCount);
+    OS.RegisterClass (lpWndClass);
+    OS.HeapFree (hHeap, 0, lpWndClass.lpszClassName);
+
+    /* Register the DWT drop shadow window class */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) lpWndClass.style |= OS.CS_DROPSHADOW;
+    byteCount = windowShadowClass.length () * TCHAR.sizeof;
+    lpWndClass.lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+    OS.MoveMemory (lpWndClass.lpszClassName, windowShadowClass, byteCount);
+    OS.RegisterClass (lpWndClass);
+    OS.HeapFree (hHeap, 0, lpWndClass.lpszClassName);
+
+    /* Create the message only HWND */
+    hwndMessage = OS.CreateWindowEx (0,
+        windowClass,
+        null,
+        OS.WS_OVERLAPPED,
+        0, 0, 0, 0,
+        0,
+        0,
+        hInstance,
+        null);
+    messageCallback = new Callback (this, "messageProc", 4); //$NON-NLS-1$
+    messageProc = messageCallback.getAddress ();
+    if (messageProc is 0) error (DWT.ERROR_NO_MORE_CALLBACKS);
+    OS.SetWindowLong (hwndMessage, OS.GWL_WNDPROC, messageProc);
+
+    /* Create the filter hook */
+    if (!OS.IsWinCE) {
+        msgFilterCallback = new Callback (this, "msgFilterProc", 3); //$NON-NLS-1$
+        msgFilterProc = msgFilterCallback.getAddress ();
+        if (msgFilterProc is 0) error (DWT.ERROR_NO_MORE_CALLBACKS);
+        filterHook = OS.SetWindowsHookEx (OS.WH_MSGFILTER, msgFilterProc, 0, threadId);
+    }
+
+    /* Create the idle hook */
+    if (!OS.IsWinCE) {
+        foregroundIdleCallback = new Callback (this, "foregroundIdleProc", 3); //$NON-NLS-1$
+        foregroundIdleProc = foregroundIdleCallback.getAddress ();
+        if (foregroundIdleProc is 0) error (DWT.ERROR_NO_MORE_CALLBACKS);
+        idleHook = OS.SetWindowsHookEx (OS.WH_FOREGROUNDIDLE, foregroundIdleProc, 0, threadId);
+    }
+
+    /* Register custom messages message */
+    SWT_TASKBARCREATED = OS.RegisterWindowMessage (new TCHAR (0, "TaskbarCreated", true));
+    SWT_RESTORECARET = OS.RegisterWindowMessage (new TCHAR (0, "SWT_RESTORECARET", true));
+
+    /* Initialize OLE */
+    if (!OS.IsWinCE) OS.OleInitialize (0);
+
+    /* Initialize buffered painting */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)){
+        OS.BufferedPaintInit ();
+    }
+
+    /* Initialize the Widget Table */
+    indexTable = new int [GROW_SIZE];
+    controlTable = new Control [GROW_SIZE];
+    for (int i=0; i<GROW_SIZE-1; i++) indexTable [i] = i + 1;
+    indexTable [GROW_SIZE - 1] = -1;
+}
+
+/**
+ * Invokes platform specific functionality to dispose a GC handle.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Display</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 hDC the platform specific GC handle
+ * @param data the platform specific GC data
+ */
+public void internal_dispose_GC (int hDC, GCData data) {
+    OS.ReleaseDC (0, hDC);
+}
+
+bool isXMouseActive () {
+    /*
+    * NOTE: X-Mouse is active when bit 1 of the UserPreferencesMask is set.
+    */
+    bool xMouseActive = false;
+    TCHAR key = new TCHAR (0, "Control Panel\\Desktop", true); //$NON-NLS-1$
+    int [] phKey = new int [1];
+    int result = OS.RegOpenKeyEx (OS.HKEY_CURRENT_USER, key, 0, OS.KEY_READ, phKey);
+    if (result is 0) {
+        TCHAR lpValueName = new TCHAR (0, "UserPreferencesMask", true); //$NON-NLS-1$
+        int [] lpcbData = new int [] {4}, lpData = new int [1];
+        result = OS.RegQueryValueEx (phKey [0], lpValueName, 0, null, lpData, lpcbData);
+        if (result is 0) xMouseActive = (lpData [0] & 0x01) !is 0;
+        OS.RegCloseKey (phKey [0]);
+    }
+    return xMouseActive;
+}
+
+bool isValidThread () {
+    return thread is Thread.currentThread ();
+}
+
+/**
+ * Maps a point from one coordinate system to another.
+ * When the control is null, coordinates are mapped to
+ * the display.
+ * <p>
+ * NOTE: On right-to-left platforms where the coordinate
+ * systems are mirrored, special care needs to be taken
+ * when mapping coordinates from one control to another
+ * to ensure the result is correctly mirrored.
+ *
+ * Mapping a point that is the origin of a rectangle and
+ * then adding the width and height is not equivalent to
+ * mapping the rectangle.  When one control is mirrored
+ * and the other is not, adding the width and height to a
+ * point that was mapped causes the rectangle to extend
+ * in the wrong direction.  Mapping the entire rectangle
+ * instead of just one point causes both the origin and
+ * the corner of the rectangle to be mapped.
+ * </p>
+ *
+ * @param from the source <code>Control</code> or <code>null</code>
+ * @param to the destination <code>Control</code> or <code>null</code>
+ * @param point to be mapped
+ * @return point with mapped coordinates
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public Point map (Control from, Control to, Point point) {
+    checkDevice ();
+    if (point is null) error (DWT.ERROR_NULL_ARGUMENT);
+    return map (from, to, point.x, point.y);
+}
+
+/**
+ * Maps a point from one coordinate system to another.
+ * When the control is null, coordinates are mapped to
+ * the display.
+ * <p>
+ * NOTE: On right-to-left platforms where the coordinate
+ * systems are mirrored, special care needs to be taken
+ * when mapping coordinates from one control to another
+ * to ensure the result is correctly mirrored.
+ *
+ * Mapping a point that is the origin of a rectangle and
+ * then adding the width and height is not equivalent to
+ * mapping the rectangle.  When one control is mirrored
+ * and the other is not, adding the width and height to a
+ * point that was mapped causes the rectangle to extend
+ * in the wrong direction.  Mapping the entire rectangle
+ * instead of just one point causes both the origin and
+ * the corner of the rectangle to be mapped.
+ * </p>
+ *
+ * @param from the source <code>Control</code> or <code>null</code>
+ * @param to the destination <code>Control</code> or <code>null</code>
+ * @param x coordinates to be mapped
+ * @param y coordinates to be mapped
+ * @return point with mapped coordinates
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public Point map (Control from, Control to, int x, int y) {
+    checkDevice ();
+    if (from !is null && from.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT);
+    if (to !is null && to.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT);
+    if (from is to) return new Point (x, y);
+    int hwndFrom = from !is null ? from.handle : 0;
+    int hwndTo = to !is null ? to.handle : 0;
+    POINT point = new POINT ();
+    point.x = x;
+    point.y = y;
+    OS.MapWindowPoints (hwndFrom, hwndTo, point, 1);
+    return new Point (point.x, point.y);
+}
+
+/**
+ * Maps a point from one coordinate system to another.
+ * When the control is null, coordinates are mapped to
+ * the display.
+ * <p>
+ * NOTE: On right-to-left platforms where the coordinate
+ * systems are mirrored, special care needs to be taken
+ * when mapping coordinates from one control to another
+ * to ensure the result is correctly mirrored.
+ *
+ * Mapping a point that is the origin of a rectangle and
+ * then adding the width and height is not equivalent to
+ * mapping the rectangle.  When one control is mirrored
+ * and the other is not, adding the width and height to a
+ * point that was mapped causes the rectangle to extend
+ * in the wrong direction.  Mapping the entire rectangle
+ * instead of just one point causes both the origin and
+ * the corner of the rectangle to be mapped.
+ * </p>
+ *
+ * @param from the source <code>Control</code> or <code>null</code>
+ * @param to the destination <code>Control</code> or <code>null</code>
+ * @param rectangle to be mapped
+ * @return rectangle with mapped coordinates
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the rectangle is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public Rectangle map (Control from, Control to, Rectangle rectangle) {
+    checkDevice ();
+    if (rectangle is null) error (DWT.ERROR_NULL_ARGUMENT);
+    return map (from, to, rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+}
+
+/**
+ * Maps a point from one coordinate system to another.
+ * When the control is null, coordinates are mapped to
+ * the display.
+ * <p>
+ * NOTE: On right-to-left platforms where the coordinate
+ * systems are mirrored, special care needs to be taken
+ * when mapping coordinates from one control to another
+ * to ensure the result is correctly mirrored.
+ *
+ * Mapping a point that is the origin of a rectangle and
+ * then adding the width and height is not equivalent to
+ * mapping the rectangle.  When one control is mirrored
+ * and the other is not, adding the width and height to a
+ * point that was mapped causes the rectangle to extend
+ * in the wrong direction.  Mapping the entire rectangle
+ * instead of just one point causes both the origin and
+ * the corner of the rectangle to be mapped.
+ * </p>
+ *
+ * @param from the source <code>Control</code> or <code>null</code>
+ * @param to the destination <code>Control</code> or <code>null</code>
+ * @param x coordinates to be mapped
+ * @param y coordinates to be mapped
+ * @param width coordinates to be mapped
+ * @param height coordinates to be mapped
+ * @return rectangle with mapped coordinates
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the Control from or the Control to have been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public Rectangle map (Control from, Control to, int x, int y, int width, int height) {
+    checkDevice ();
+    if (from !is null && from.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT);
+    if (to !is null && to.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT);
+    if (from is to) return new Rectangle (x, y, width, height);
+    int hwndFrom = from !is null ? from.handle : 0;
+    int hwndTo = to !is null ? to.handle : 0;
+    RECT rect = new RECT ();
+    rect.left = x;
+    rect.top  = y;
+    rect.right = x + width;
+    rect.bottom = y + height;
+    OS.MapWindowPoints (hwndFrom, hwndTo, rect, 2);
+    return new Rectangle (rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+}
+
+/*
+ * Returns a single character, converted from the default
+ * multi-byte character set (MBCS) used by the operating
+ * system widgets to a wide character set (WCS) used by Java.
+ *
+ * @param ch the MBCS character
+ * @return the WCS character
+ */
+static char mbcsToWcs (int ch) {
+    return mbcsToWcs (ch, 0);
+}
+
+/*
+ * Returns a single character, converted from the specified
+ * multi-byte character set (MBCS) used by the operating
+ * system widgets to a wide character set (WCS) used by Java.
+ *
+ * @param ch the MBCS character
+ * @param codePage the code page used to convert the character
+ * @return the WCS character
+ */
+static char mbcsToWcs (int ch, int codePage) {
+    if (OS.IsUnicode) return (char) ch;
+    int key = ch & 0xFFFF;
+    if (key <= 0x7F) return (char) ch;
+    byte [] buffer;
+    if (key <= 0xFF) {
+        buffer = new byte [1];
+        buffer [0] = (byte) key;
+    } else {
+        buffer = new byte [2];
+        buffer [0] = (byte) ((key >> 8) & 0xFF);
+        buffer [1] = (byte) (key & 0xFF);
+    }
+    char [] unicode = new char [1];
+    int cp = codePage !is 0 ? codePage : OS.CP_ACP;
+    int count = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, buffer.length, unicode, 1);
+    if (count is 0) return 0;
+    return unicode [0];
+}
+
+int messageProc (int hwnd, int msg, int wParam, int lParam) {
+    switch (msg) {
+        case SWT_RUNASYNC: {
+            if (runMessagesInIdle) runAsyncMessages (false);
+            break;
+        }
+        case SWT_KEYMSG: {
+            bool consumed = false;
+            MSG keyMsg = new MSG ();
+            OS.MoveMemory (keyMsg, lParam, MSG.sizeof);
+            Control control = findControl (keyMsg.hwnd);
+            if (control !is null) {
+                /*
+                * Feature in Windows.  When the user types an accent key such
+                * as ^ in order to get an accented character on a German keyboard,
+                * calling TranslateMessage(), ToUnicode() or ToAscii() consumes
+                * the key.  This means that a subsequent call to TranslateMessage()
+                * will see a regular key rather than the accented key.  The fix
+                * is to use MapVirtualKey() and VkKeyScan () to detect an accent
+                * and avoid calls to TranslateMessage().
+                */
+                bool accentKey = false;
+                switch (keyMsg.message) {
+                    case OS.WM_KEYDOWN:
+                    case OS.WM_SYSKEYDOWN: {
+                        if (!OS.IsWinCE) {
+                            switch (keyMsg.wParam) {
+                                case OS.VK_SHIFT:
+                                case OS.VK_MENU:
+                                case OS.VK_CONTROL:
+                                case OS.VK_CAPITAL:
+                                case OS.VK_NUMLOCK:
+                                case OS.VK_SCROLL:
+                                    break;
+                                default: {
+                                    /*
+                                    * Bug in Windows. The high bit in the result of MapVirtualKey() on
+                                    * Windows NT is bit 32 while the high bit on Windows 95 is bit 16.
+                                    * They should both be bit 32.  The fix is to test the right bit.
+                                    */
+                                    int mapKey = OS.MapVirtualKey (keyMsg.wParam, 2);
+                                    if (mapKey !is 0) {
+                                        accentKey = (mapKey & (OS.IsWinNT ? 0x80000000 : 0x8000)) !is 0;
+                                        if (!accentKey) {
+                                            for (int i=0; i<ACCENTS.length; i++) {
+                                                int value = OS.VkKeyScan (ACCENTS [i]);
+                                                if (value !is -1 && (value & 0xFF) is keyMsg.wParam) {
+                                                    int state = value >> 8;
+                                                    if ((OS.GetKeyState (OS.VK_SHIFT) < 0) is ((state & 0x1) !is 0) &&
+                                                        (OS.GetKeyState (OS.VK_CONTROL) < 0) is ((state & 0x2) !is 0) &&
+                                                        (OS.GetKeyState (OS.VK_MENU) < 0) is ((state & 0x4) !is 0)) {
+                                                            if ((state & 0x7) !is 0) accentKey = true;
+                                                            break;
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                        break;
+                    }
+                }
+                if (!accentKey && !ignoreNextKey) {
+                    keyMsg.hwnd = control.handle;
+                    int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE;
+                    do {
+                        if (!(consumed |= filterMessage (keyMsg))) {
+                            OS.TranslateMessage (keyMsg);
+                            consumed |= OS.DispatchMessage (keyMsg) is 1;
+                        }
+                    } while (OS.PeekMessage (keyMsg, keyMsg.hwnd, OS.WM_KEYFIRST, OS.WM_KEYLAST, flags));
+                }
+                switch (keyMsg.message) {
+                    case OS.WM_KEYDOWN:
+                    case OS.WM_SYSKEYDOWN: {
+                        switch (keyMsg.wParam) {
+                            case OS.VK_SHIFT:
+                            case OS.VK_MENU:
+                            case OS.VK_CONTROL:
+                            case OS.VK_CAPITAL:
+                            case OS.VK_NUMLOCK:
+                            case OS.VK_SCROLL:
+                                break;
+                            default: {
+                                ignoreNextKey = accentKey;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            if (consumed) {
+                int hHeap = OS.GetProcessHeap ();
+                OS.HeapFree (hHeap, 0, lParam);
+            } else {
+                OS.PostMessage (embeddedHwnd, SWT_KEYMSG, wParam, lParam);
+            }
+            return 0;
+        }
+        case SWT_TRAYICONMSG: {
+            if (tray !is null) {
+                TrayItem [] items = tray.items;
+                for (int i=0; i<items.length; i++) {
+                    TrayItem item = items [i];
+                    if (item !is null && item.id is wParam) {
+                        return item.messageProc (hwnd, msg, wParam, lParam);
+                    }
+                }
+            }
+            return 0;
+        }
+        case OS.WM_ACTIVATEAPP: {
+            /*
+            * Feature in Windows.  When multiple shells are
+            * disabled and one of the shells has an enabled
+            * dialog child and the user selects a disabled
+            * shell that does not have the enabled dialog
+            * child using the Task bar, Windows brings the
+            * disabled shell to the front.  As soon as the
+            * user clicks on the disabled shell, the enabled
+            * dialog child comes to the front.  This behavior
+            * is unspecified and seems strange.  Normally, a
+            * disabled shell is frozen on the screen and the
+            * user cannot change the z-order by clicking with
+            * the mouse.  The fix is to look for WM_ACTIVATEAPP
+            * and force the enabled dialog child to the front.
+            * This is typically what the user is expecting.
+            *
+            * NOTE: If the modal shell is disabled for any
+            * reason, it should not be brought to the front.
+            */
+            if (wParam !is 0) {
+                if (!isXMouseActive ()) {
+                    if (modalDialogShell !is null && modalDialogShell.isDisposed ()) modalDialogShell = null;
+                    Shell modal = modalDialogShell !is null ? modalDialogShell : getModalShell ();
+                    if (modal !is null) {
+                        int hwndModal = modal.handle;
+                        if (OS.IsWindowEnabled (hwndModal)) {
+                            modal.bringToTop ();
+                            if (modal.isDisposed ()) break;
+                        }
+                        int hwndPopup = OS.GetLastActivePopup (hwndModal);
+                        if (hwndPopup !is 0 && hwndPopup !is modal.handle) {
+                            if (getControl (hwndPopup) is null) {
+                                if (OS.IsWindowEnabled (hwndPopup)) {
+                                    OS.SetActiveWindow (hwndPopup);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            break;
+        }
+        case OS.WM_ENDSESSION: {
+            if (wParam !is 0) {
+                dispose ();
+                /*
+                * When the session is ending, no DWT program can continue
+                * to run.  In order to avoid running code after the display
+                * has been disposed, exit from Java.
+                */
+                System.exit (0);
+            }
+            break;
+        }
+        case OS.WM_QUERYENDSESSION: {
+            Event event = new Event ();
+            sendEvent (DWT.Close, event);
+            if (!event.doit) return 0;
+            break;
+        }
+        case OS.WM_SETTINGCHANGE: {
+            switch (wParam) {
+                case 0:
+                case 1:
+                case OS.SPI_SETHIGHCONTRAST:
+                    OS.SetTimer (hwndMessage, SETTINGS_ID, SETTINGS_DELAY, 0);
+            }
+            break;
+        }
+        case OS.WM_THEMECHANGED: {
+            if (OS.COMCTL32_MAJOR >= 6) {
+                if (hButtonTheme !is 0) OS.CloseThemeData (hButtonTheme);
+                if (hEditTheme !is 0) OS.CloseThemeData (hEditTheme);
+                if (hExplorerBarTheme !is 0) OS.CloseThemeData (hExplorerBarTheme);
+                if (hScrollBarTheme !is 0) OS.CloseThemeData (hScrollBarTheme);
+                if (hTabTheme !is 0) OS.CloseThemeData (hTabTheme);
+                hButtonTheme = hEditTheme = hExplorerBarTheme = hScrollBarTheme = hTabTheme = 0;
+            }
+            break;
+        }
+        case OS.WM_TIMER: {
+            if (wParam is SETTINGS_ID) {
+                OS.KillTimer (hwndMessage, SETTINGS_ID);
+                runSettings ();
+            } else {
+                runTimer (wParam);
+            }
+            break;
+        }
+        default: {
+            if (msg is SWT_TASKBARCREATED) {
+                if (tray !is null) {
+                    TrayItem [] items = tray.items;
+                    for (int i=0; i<items.length; i++) {
+                        TrayItem item = items [i];
+                        if (item !is null) item.recreate ();
+                    }
+                }
+            }
+        }
+    }
+    return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+}
+
+int monitorEnumProc (int hmonitor, int hdc, int lprcMonitor, int dwData) {
+    if (monitorCount >= monitors.length) {
+        Monitor[] newMonitors = new Monitor [monitors.length + 4];
+        System.arraycopy (monitors, 0, newMonitors, 0, monitors.length);
+        monitors = newMonitors;
+    }
+    MONITORINFO lpmi = new MONITORINFO ();
+    lpmi.cbSize = MONITORINFO.sizeof;
+    OS.GetMonitorInfo (hmonitor, lpmi);
+    Monitor monitor = new Monitor ();
+    monitor.handle = hmonitor;
+    monitor.x = lpmi.rcMonitor_left;
+    monitor.y = lpmi.rcMonitor_top;
+    monitor.width = lpmi.rcMonitor_right - lpmi.rcMonitor_left;
+    monitor.height = lpmi.rcMonitor_bottom - lpmi.rcMonitor_top;
+    monitor.clientX = lpmi.rcWork_left;
+    monitor.clientY = lpmi.rcWork_top;
+    monitor.clientWidth = lpmi.rcWork_right - lpmi.rcWork_left;
+    monitor.clientHeight = lpmi.rcWork_bottom - lpmi.rcWork_top;
+    monitors [monitorCount++] = monitor;
+    return 1;
+}
+
+int msgFilterProc (int code, int wParam, int lParam) {
+    switch (code) {
+        case OS.MSGF_COMMCTRL_BEGINDRAG: {
+            if (!runDragDrop) {
+                OS.MoveMemory (hookMsg, lParam, MSG.sizeof);
+                if (hookMsg.message is OS.WM_MOUSEMOVE) {
+                    OS.SendMessage (hookMsg.hwnd, OS.WM_CANCELMODE, 0, 0);
+                }
+            }
+            break;
+        }
+        /*
+        * Feature in Windows.  For some reason, when the user clicks
+        * a table or tree, the Windows hook WH_MSGFILTER is sent when
+        * an input event from a dialog box, message box, menu, or scroll
+        * bar did not occur, causing async messages to run at the wrong
+        * time.  The fix is to check the message filter code.
+        */
+        case OS.MSGF_DIALOGBOX:
+        case OS.MSGF_MAINLOOP:
+        case OS.MSGF_MENU:
+        case OS.MSGF_MOVE:
+        case OS.MSGF_MESSAGEBOX:
+        case OS.MSGF_NEXTWINDOW:
+        case OS.MSGF_SCROLLBAR:
+        case OS.MSGF_SIZE: {
+            if (runMessages) {
+                OS.MoveMemory (hookMsg, lParam, MSG.sizeof);
+                if (hookMsg.message is OS.WM_NULL) {
+                    MSG msg = new MSG ();
+                    int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE;
+                    if (!OS.PeekMessage (msg, 0, 0, 0, flags)) {
+                        if (runAsyncMessages (false)) wakeThread ();
+                    }
+                }
+            }
+            break;
+        }
+    }
+    return OS.CallNextHookEx (filterHook, code, wParam, lParam);
+}
+
+int numpadKey (int key) {
+    switch (key) {
+        case OS.VK_NUMPAD0: return '0';
+        case OS.VK_NUMPAD1: return '1';
+        case OS.VK_NUMPAD2: return '2';
+        case OS.VK_NUMPAD3: return '3';
+        case OS.VK_NUMPAD4: return '4';
+        case OS.VK_NUMPAD5: return '5';
+        case OS.VK_NUMPAD6: return '6';
+        case OS.VK_NUMPAD7: return '7';
+        case OS.VK_NUMPAD8: return '8';
+        case OS.VK_NUMPAD9: return '9';
+        case OS.VK_MULTIPLY:    return '*';
+        case OS.VK_ADD:         return '+';
+        case OS.VK_SEPARATOR:   return '\0';
+        case OS.VK_SUBTRACT:    return '-';
+        case OS.VK_DECIMAL: return '.';
+        case OS.VK_DIVIDE:      return '/';
+    }
+    return 0;
+}
+
+/**
+ * Generate a low level system event.
+ *
+ * <code>post</code> is used to generate low level keyboard
+ * and mouse events. The intent is to enable automated UI
+ * testing by simulating the input from the user.  Most
+ * DWT applications should never need to call this method.
+ * <p>
+ * Note that this operation can fail when the operating system
+ * fails to generate the event for any reason.  For example,
+ * this can happen when there is no such key or mouse button
+ * or when the system event queue is full.
+ * </p>
+ * <p>
+ * <b>Event Types:</b>
+ * <p>KeyDown, KeyUp
+ * <p>The following fields in the <code>Event</code> apply:
+ * <ul>
+ * <li>(in) type KeyDown or KeyUp</li>
+ * <p> Either one of:
+ * <li>(in) character a character that corresponds to a keyboard key</li>
+ * <li>(in) keyCode the key code of the key that was typed,
+ *          as defined by the key code constants in class <code>DWT</code></li>
+ * </ul>
+ * <p>MouseDown, MouseUp</p>
+ * <p>The following fields in the <code>Event</code> apply:
+ * <ul>
+ * <li>(in) type MouseDown or MouseUp
+ * <li>(in) button the button that is pressed or released
+ * </ul>
+ * <p>MouseMove</p>
+ * <p>The following fields in the <code>Event</code> apply:
+ * <ul>
+ * <li>(in) type MouseMove
+ * <li>(in) x the x coordinate to move the mouse pointer to in screen coordinates
+ * <li>(in) y the y coordinate to move the mouse pointer to in screen coordinates
+ * </ul>
+ * </dl>
+ *
+ * @param event the event to be generated
+ *
+ * @return true if the event was generated or false otherwise
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the event is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 3.0
+ *
+ */
+public bool post (Event event) {
+    if (isDisposed ()) error (DWT.ERROR_DEVICE_DISPOSED);
+    if (event is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int type = event.type;
+    switch (type){
+        case DWT.KeyDown:
+        case DWT.KeyUp: {
+            KEYBDINPUT inputs = new KEYBDINPUT ();
+            inputs.wVk = (short) untranslateKey (event.keyCode);
+            if (inputs.wVk is 0) {
+                char key = event.character;
+                switch (key) {
+                    case DWT.BS: inputs.wVk = (short) OS.VK_BACK; break;
+                    case DWT.CR: inputs.wVk = (short) OS.VK_RETURN; break;
+                    case DWT.DEL: inputs.wVk = (short) OS.VK_DELETE; break;
+                    case DWT.ESC: inputs.wVk = (short) OS.VK_ESCAPE; break;
+                    case DWT.TAB: inputs.wVk = (short) OS.VK_TAB; break;
+                    /*
+                    * Since there is no LF key on the keyboard, do not attempt
+                    * to map LF to CR or attempt to post an LF key.
+                    */
+//                  case DWT.LF: inputs.wVk = (short) OS.VK_RETURN; break;
+                    case DWT.LF: return false;
+                    default: {
+                        if (OS.IsWinCE) {
+                            inputs.wVk = OS.CharUpper ((short) key);
+                        } else {
+                            inputs.wVk = OS.VkKeyScan ((short) wcsToMbcs (key, 0));
+                            if (inputs.wVk is -1) return false;
+                            inputs.wVk &= 0xFF;
+                        }
+                    }
+                }
+            }
+            inputs.dwFlags = type is DWT.KeyUp ? OS.KEYEVENTF_KEYUP : 0;
+            int hHeap = OS.GetProcessHeap ();
+            int pInputs = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, INPUT.sizeof);
+            OS.MoveMemory(pInputs, new int[] {OS.INPUT_KEYBOARD}, 4);
+            OS.MoveMemory (pInputs + 4, inputs, KEYBDINPUT.sizeof);
+            bool result = OS.SendInput (1, pInputs, INPUT.sizeof) !is 0;
+            OS.HeapFree (hHeap, 0, pInputs);
+            return result;
+        }
+        case DWT.MouseDown:
+        case DWT.MouseMove:
+        case DWT.MouseUp:
+        case DWT.MouseWheel: {
+            MOUSEINPUT inputs = new MOUSEINPUT ();
+            if (type is DWT.MouseMove){
+                inputs.dwFlags = OS.MOUSEEVENTF_MOVE | OS.MOUSEEVENTF_ABSOLUTE;
+                int x= 0, y = 0, width = 0, height = 0;
+                if (OS.WIN32_VERSION >= OS.VERSION (5, 0)) {
+                    inputs.dwFlags |= OS.MOUSEEVENTF_VIRTUALDESK;
+                    x = OS.GetSystemMetrics (OS.SM_XVIRTUALSCREEN);
+                    y = OS.GetSystemMetrics (OS.SM_YVIRTUALSCREEN);
+                    width = OS.GetSystemMetrics (OS.SM_CXVIRTUALSCREEN);
+                    height = OS.GetSystemMetrics (OS.SM_CYVIRTUALSCREEN);
+                } else {
+                    width = OS.GetSystemMetrics (OS.SM_CXSCREEN);
+                    height = OS.GetSystemMetrics (OS.SM_CYSCREEN);
+                }
+                inputs.dx = ((event.x - x) * 65535 + width - 2) / (width - 1);
+                inputs.dy = ((event.y - y) * 65535 + height - 2) / (height - 1);
+            } else {
+                if (type is DWT.MouseWheel) {
+                    if (OS.WIN32_VERSION < OS.VERSION (5, 0)) return false;
+                    inputs.dwFlags = OS.MOUSEEVENTF_WHEEL;
+                    switch (event.detail) {
+                        case DWT.SCROLL_PAGE:
+                            inputs.mouseData = event.count * OS.WHEEL_DELTA;
+                            break;
+                        case DWT.SCROLL_LINE:
+                            int [] value = new int [1];
+                            OS.SystemParametersInfo (OS.SPI_GETWHEELSCROLLLINES, 0, value, 0);
+                            inputs.mouseData = event.count * OS.WHEEL_DELTA / value [0];
+                            break;
+                        default: return false;
+                    }
+                } else {
+                    switch (event.button) {
+                        case 1: inputs.dwFlags = type is DWT.MouseDown ? OS.MOUSEEVENTF_LEFTDOWN : OS.MOUSEEVENTF_LEFTUP; break;
+                        case 2: inputs.dwFlags = type is DWT.MouseDown ? OS.MOUSEEVENTF_MIDDLEDOWN : OS.MOUSEEVENTF_MIDDLEUP; break;
+                        case 3: inputs.dwFlags = type is DWT.MouseDown ? OS.MOUSEEVENTF_RIGHTDOWN : OS.MOUSEEVENTF_RIGHTUP; break;
+                        case 4: {
+                            if (OS.WIN32_VERSION < OS.VERSION (5, 0)) return false;
+                            inputs.dwFlags = type is DWT.MouseDown ? OS.MOUSEEVENTF_XDOWN : OS.MOUSEEVENTF_XUP;
+                            inputs.mouseData = OS.XBUTTON1;
+                            break;
+                        }
+                        case 5: {
+                            if (OS.WIN32_VERSION < OS.VERSION (5, 0)) return false;
+                            inputs.dwFlags = type is DWT.MouseDown ? OS.MOUSEEVENTF_XDOWN : OS.MOUSEEVENTF_XUP;
+                            inputs.mouseData = OS.XBUTTON2;
+                            break;
+                        }
+                        default: return false;
+                    }
+                }
+            }
+            int hHeap = OS.GetProcessHeap ();
+            int pInputs = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, INPUT.sizeof);
+            OS.MoveMemory(pInputs, new int[] {OS.INPUT_MOUSE}, 4);
+            OS.MoveMemory (pInputs + 4, inputs, MOUSEINPUT.sizeof);
+            bool result = OS.SendInput (1, pInputs, INPUT.sizeof) !is 0;
+            OS.HeapFree (hHeap, 0, pInputs);
+            return result;
+        }
+    }
+    return false;
+}
+
+void postEvent (Event event) {
+    /*
+    * Place the event at the end of the event queue.
+    * This code is always called in the Display's
+    * thread so it must be re-enterant but does not
+    * need to be synchronized.
+    */
+    if (eventQueue is null) eventQueue = new Event [4];
+    int index = 0;
+    int length = eventQueue.length;
+    while (index < length) {
+        if (eventQueue [index] is null) break;
+        index++;
+    }
+    if (index is length) {
+        Event [] newQueue = new Event [length + 4];
+        System.arraycopy (eventQueue, 0, newQueue, 0, length);
+        eventQueue = newQueue;
+    }
+    eventQueue [index] = event;
+}
+
+/**
+ * Reads an event from the operating system's event queue,
+ * dispatches it appropriately, and returns <code>true</code>
+ * if there is potentially more work to do, or <code>false</code>
+ * if the caller can sleep until another event is placed on
+ * the event queue.
+ * <p>
+ * In addition to checking the system event queue, this method also
+ * checks if any inter-thread messages (created by <code>syncExec()</code>
+ * or <code>asyncExec()</code>) are waiting to be processed, and if
+ * so handles them before returning.
+ * </p>
+ *
+ * @return <code>false</code> if the caller can sleep upon return from this method
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_FAILED_EXEC - if an exception occurred while running an inter-thread message</li>
+ * </ul>
+ *
+ * @see #sleep
+ * @see #wake
+ */
+public bool readAndDispatch () {
+    checkDevice ();
+    lpStartupInfo = null;
+    drawMenuBars ();
+    runPopups ();
+    if (OS.PeekMessage (msg, 0, 0, 0, OS.PM_REMOVE)) {
+        if (!filterMessage (msg)) {
+            OS.TranslateMessage (msg);
+            OS.DispatchMessage (msg);
+        }
+        runDeferredEvents ();
+        return true;
+    }
+    return runMessages && runAsyncMessages (false);
+}
+
+static synchronized void register (Display display) {
+    for (int i=0; i<Displays.length; i++) {
+        if (Displays [i] is null) {
+            Displays [i] = display;
+            return;
+        }
+    }
+    Display [] newDisplays = new Display [Displays.length + 4];
+    System.arraycopy (Displays, 0, newDisplays, 0, Displays.length);
+    newDisplays [Displays.length] = display;
+    Displays = newDisplays;
+}
+
+/**
+ * Releases any internal resources back to the operating
+ * system and clears all fields except the device handle.
+ * <p>
+ * Disposes all shells which are currently open on the display.
+ * After this method has been invoked, all related related shells
+ * will answer <code>true</code> when sent the message
+ * <code>isDisposed()</code>.
+ * </p><p>
+ * When a device is destroyed, resources that were acquired
+ * on behalf of the programmer need to be returned to the
+ * operating system.  For example, if the device allocated a
+ * font to be used as the system font, this font would be
+ * freed in <code>release</code>.  Also,to assist the garbage
+ * collector and minimize the amount of memory that is not
+ * reclaimed when the programmer keeps a reference to a
+ * disposed device, all fields except the handle are zero'd.
+ * The handle is needed by <code>destroy</code>.
+ * </p>
+ * This method is called before <code>destroy</code>.
+ *
+ * @see Device#dispose
+ * @see #destroy
+ */
+protected void release () {
+    sendEvent (DWT.Dispose, new Event ());
+    Shell [] shells = getShells ();
+    for (int i=0; i<shells.length; i++) {
+        Shell shell = shells [i];
+        if (!shell.isDisposed ()) shell.dispose ();
+    }
+    if (tray !is null) tray.dispose ();
+    tray = null;
+    while (readAndDispatch ()) {}
+    if (disposeList !is null) {
+        for (int i=0; i<disposeList.length; i++) {
+            if (disposeList [i] !is null) disposeList [i].run ();
+        }
+    }
+    disposeList = null;
+    synchronizer.releaseSynchronizer ();
+    synchronizer = null;
+    releaseDisplay ();
+    super.release ();
+}
+
+void releaseDisplay () {
+    if (embeddedHwnd !is 0) {
+        OS.PostMessage (embeddedHwnd, SWT_DESTROY, 0, 0);
+    }
+
+    /* Release XP Themes */
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if (hButtonTheme !is 0) OS.CloseThemeData (hButtonTheme);
+        if (hEditTheme !is 0) OS.CloseThemeData (hEditTheme);
+        if (hExplorerBarTheme !is 0) OS.CloseThemeData (hExplorerBarTheme);
+        if (hScrollBarTheme !is 0) OS.CloseThemeData (hScrollBarTheme);
+        if (hTabTheme !is 0) OS.CloseThemeData (hTabTheme);
+        hButtonTheme = hEditTheme = hExplorerBarTheme = hScrollBarTheme = hTabTheme = 0;
+    }
+
+    /* Unhook the message hook */
+    if (!OS.IsWinCE) {
+        if (msgHook !is 0) OS.UnhookWindowsHookEx (msgHook);
+        msgHook = 0;
+    }
+
+    /* Unhook the filter hook */
+    if (!OS.IsWinCE) {
+        if (filterHook !is 0) OS.UnhookWindowsHookEx (filterHook);
+        filterHook = 0;
+        msgFilterCallback.dispose ();
+        msgFilterCallback = null;
+        msgFilterProc = 0;
+    }
+
+    /* Unhook the idle hook */
+    if (!OS.IsWinCE) {
+        if (idleHook !is 0) OS.UnhookWindowsHookEx (idleHook);
+        idleHook = 0;
+        foregroundIdleCallback.dispose ();
+        foregroundIdleCallback = null;
+        foregroundIdleProc = 0;
+    }
+
+    /* Destroy the message only HWND */
+    if (hwndMessage !is 0) OS.DestroyWindow (hwndMessage);
+    hwndMessage = 0;
+    messageCallback.dispose ();
+    messageCallback = null;
+    messageProc = 0;
+
+    /* Unregister the DWT window class */
+    int hHeap = OS.GetProcessHeap ();
+    int hInstance = OS.GetModuleHandle (null);
+    OS.UnregisterClass (windowClass, hInstance);
+
+    /* Unregister the DWT drop shadow window class */
+    OS.UnregisterClass (windowShadowClass, hInstance);
+    windowClass = windowShadowClass = null;
+    windowCallback.dispose ();
+    windowCallback = null;
+    windowProc = 0;
+
+    /* Release the System fonts */
+    if (systemFont !is null) systemFont.dispose ();
+    systemFont = null;
+    lfSystemFont = null;
+
+    /* Release the System Images */
+    if (errorImage !is null) errorImage.dispose ();
+    if (infoImage !is null) infoImage.dispose ();
+    if (questionImage !is null) questionImage.dispose ();
+    if (warningIcon !is null) warningIcon.dispose ();
+    errorImage = infoImage = questionImage = warningIcon = null;
+
+    /* Release Sort Indicators */
+    if (upArrow !is null) upArrow.dispose ();
+    if (downArrow !is null) downArrow.dispose ();
+    upArrow = downArrow = null;
+
+    /* Release the System Cursors */
+    for (int i = 0; i < cursors.length; i++) {
+        if (cursors [i] !is null) cursors [i].dispose ();
+    }
+    cursors = null;
+
+    /* Release Acquired Resources */
+    if (resources !is null) {
+        for (int i=0; i<resources.length; i++) {
+            if (resources [i] !is null) resources [i].dispose ();
+        }
+        resources = null;
+    }
+
+    /* Release Custom Colors for ChooseColor */
+    if (lpCustColors !is 0) OS.HeapFree (hHeap, 0, lpCustColors);
+    lpCustColors = 0;
+
+    /* Uninitialize OLE */
+    if (!OS.IsWinCE) OS.OleUninitialize ();
+
+    /* Uninitialize buffered painting */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        OS.BufferedPaintUnInit ();
+    }
+
+    /* Release references */
+    thread = null;
+    msg = null;
+    keyboard = null;
+    modalDialogShell = null;
+    modalShells = null;
+    data = null;
+    keys = null;
+    values = null;
+    bars = popups = null;
+    indexTable = null;
+    controlTable = null;
+    lastControl = lastGetControl = lastHittestControl = null;
+    imageList = toolImageList = toolHotImageList = toolDisabledImageList = null;
+}
+
+void releaseImageList (ImageList list) {
+    int i = 0;
+    int length = imageList.length;
+    while (i < length) {
+        if (imageList [i] is list) {
+            if (list.removeRef () > 0) return;
+            list.dispose ();
+            System.arraycopy (imageList, i + 1, imageList, i, --length - i);
+            imageList [length] = null;
+            for (int j=0; j<length; j++) {
+                if (imageList [j] !is null) return;
+            }
+            imageList = null;
+            return;
+        }
+        i++;
+    }
+}
+
+void releaseToolImageList (ImageList list) {
+    int i = 0;
+    int length = toolImageList.length;
+    while (i < length) {
+        if (toolImageList [i] is list) {
+            if (list.removeRef () > 0) return;
+            list.dispose ();
+            System.arraycopy (toolImageList, i + 1, toolImageList, i, --length - i);
+            toolImageList [length] = null;
+            for (int j=0; j<length; j++) {
+                if (toolImageList [j] !is null) return;
+            }
+            toolImageList = null;
+            return;
+        }
+        i++;
+    }
+}
+
+void releaseToolHotImageList (ImageList list) {
+    int i = 0;
+    int length = toolHotImageList.length;
+    while (i < length) {
+        if (toolHotImageList [i] is list) {
+            if (list.removeRef () > 0) return;
+            list.dispose ();
+            System.arraycopy (toolHotImageList, i + 1, toolHotImageList, i, --length - i);
+            toolHotImageList [length] = null;
+            for (int j=0; j<length; j++) {
+                if (toolHotImageList [j] !is null) return;
+            }
+            toolHotImageList = null;
+            return;
+        }
+        i++;
+    }
+}
+
+void releaseToolDisabledImageList (ImageList list) {
+    int i = 0;
+    int length = toolDisabledImageList.length;
+    while (i < length) {
+        if (toolDisabledImageList [i] is list) {
+            if (list.removeRef () > 0) return;
+            list.dispose ();
+            System.arraycopy (toolDisabledImageList, i + 1, toolDisabledImageList, i, --length - i);
+            toolDisabledImageList [length] = null;
+            for (int j=0; j<length; j++) {
+                if (toolDisabledImageList [j] !is null) return;
+            }
+            toolDisabledImageList = null;
+            return;
+        }
+        i++;
+    }
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when an event of the given type occurs anywhere in
+ * a widget. The event type is one of the event constants defined
+ * in class <code>DWT</code>.
+ *
+ * @param eventType the type of event to listen for
+ * @param listener the listener which should no longer be notified when the event occurs
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Listener
+ * @see DWT
+ * @see #addFilter
+ * @see #addListener
+ *
+ * @since 3.0
+ */
+public void removeFilter (int eventType, Listener listener) {
+    checkDevice ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (filterTable is null) return;
+    filterTable.unhook (eventType, listener);
+    if (filterTable.size () is 0) filterTable = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when an event of the given type occurs. The event type
+ * is one of the event constants defined in class <code>DWT</code>.
+ *
+ * @param eventType the type of event to listen for
+ * @param listener the listener which should no longer be notified when the event occurs
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Listener
+ * @see DWT
+ * @see #addListener
+ *
+ * @since 2.0
+ */
+public void removeListener (int eventType, Listener listener) {
+    checkDevice ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (eventType, listener);
+}
+
+void removeBar (Menu menu) {
+    if (bars is null) return;
+    for (int i=0; i<bars.length; i++) {
+        if (bars [i] is menu) {
+            bars [i] = null;
+            return;
+        }
+    }
+}
+
+Control removeControl (int handle) {
+    if (handle is 0) return null;
+    lastControl = lastGetControl = null;
+    Control control = null;
+    int index;
+    if (USE_PROPERTY) {
+        index = OS.RemoveProp (handle, SWT_OBJECT_INDEX) - 1;
+    } else {
+        index = OS.GetWindowLong (handle, OS.GWL_USERDATA) - 1;
+    }
+    if (0 <= index && index < controlTable.length) {
+        control = controlTable [index];
+        controlTable [index] = null;
+        indexTable [index] = freeSlot;
+        freeSlot = index;
+        if (!USE_PROPERTY) {
+            OS.SetWindowLong (handle, OS.GWL_USERDATA, 0);
+        }
+    }
+    return control;
+}
+
+void removeMenuItem (MenuItem item) {
+    if (items is null) return;
+    items [item.id - ID_START] = null;
+}
+
+void removePopup (Menu menu) {
+    if (popups is null) return;
+    for (int i=0; i<popups.length; i++) {
+        if (popups [i] is menu) {
+            popups [i] = null;
+            return;
+        }
+    }
+}
+
+bool runAsyncMessages (bool all) {
+    return synchronizer.runAsyncMessages (all);
+}
+
+bool runDeferredEvents () {
+    /*
+    * Run deferred events.  This code is always
+    * called in the Display's thread so it must
+    * be re-enterant but need not be synchronized.
+    */
+    while (eventQueue !is null) {
+
+        /* Take an event off the queue */
+        Event event = eventQueue [0];
+        if (event is null) break;
+        int length = eventQueue.length;
+        System.arraycopy (eventQueue, 1, eventQueue, 0, --length);
+        eventQueue [length] = null;
+
+        /* Run the event */
+        Widget widget = event.widget;
+        if (widget !is null && !widget.isDisposed ()) {
+            Widget item = event.item;
+            if (item is null || !item.isDisposed ()) {
+                widget.sendEvent (event);
+            }
+        }
+
+        /*
+        * At this point, the event queue could
+        * be null due to a recursive invocation
+        * when running the event.
+        */
+    }
+
+    /* Clear the queue */
+    eventQueue = null;
+    return true;
+}
+
+bool runPopups () {
+    if (popups is null) return false;
+    bool result = false;
+    while (popups !is null) {
+        Menu menu = popups [0];
+        if (menu is null) break;
+        int length = popups.length;
+        System.arraycopy (popups, 1, popups, 0, --length);
+        popups [length] = null;
+        runDeferredEvents ();
+        if (!menu.isDisposed ()) menu._setVisible (true);
+        result = true;
+    }
+    popups = null;
+    return result;
+}
+
+void runSettings () {
+    Font oldFont = getSystemFont ();
+    saveResources ();
+    updateImages ();
+    sendEvent (DWT.Settings, null);
+    Font newFont = getSystemFont ();
+    bool sameFont = oldFont.equals (newFont);
+    Shell [] shells = getShells ();
+    for (int i=0; i<shells.length; i++) {
+        Shell shell = shells [i];
+        if (!shell.isDisposed ()) {
+            if (!sameFont) {
+                shell.updateFont (oldFont, newFont);
+            }
+            /* This code is intentionally commented */
+            //shell.redraw (true);
+            shell.layout (true, true);
+        }
+    }
+}
+
+bool runTimer (int id) {
+    if (timerList !is null && timerIds !is null) {
+        int index = 0;
+        while (index <timerIds.length) {
+            if (timerIds [index] is id) {
+                OS.KillTimer (hwndMessage, timerIds [index]);
+                timerIds [index] = 0;
+                Runnable runnable = timerList [index];
+                timerList [index] = null;
+                if (runnable !is null) runnable.run ();
+                return true;
+            }
+            index++;
+        }
+    }
+    return false;
+}
+
+void saveResources () {
+    int resourceCount = 0;
+    if (resources is null) {
+        resources = new Resource [RESOURCE_SIZE];
+    } else {
+        resourceCount = resources.length;
+        Resource [] newResources = new Resource [resourceCount + RESOURCE_SIZE];
+        System.arraycopy (resources, 0, newResources, 0, resourceCount);
+        resources = newResources;
+    }
+    if (systemFont !is null) {
+        if (!OS.IsWinCE) {
+            NONCLIENTMETRICS info = OS.IsUnicode ? (NONCLIENTMETRICS) new NONCLIENTMETRICSW () : new NONCLIENTMETRICSA ();
+            info.cbSize = NONCLIENTMETRICS.sizeof;
+            if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, info, 0)) {
+                LOGFONT logFont = OS.IsUnicode ? (LOGFONT) ((NONCLIENTMETRICSW)info).lfMessageFont : ((NONCLIENTMETRICSA)info).lfMessageFont;
+                if (lfSystemFont is null ||
+                    logFont.lfCharSet !is lfSystemFont.lfCharSet ||
+                    logFont.lfHeight !is lfSystemFont.lfHeight ||
+                    logFont.lfWidth !is lfSystemFont.lfWidth ||
+                    logFont.lfEscapement !is lfSystemFont.lfEscapement ||
+                    logFont.lfOrientation !is lfSystemFont.lfOrientation ||
+                    logFont.lfWeight !is lfSystemFont.lfWeight ||
+                    logFont.lfItalic !is lfSystemFont.lfItalic ||
+                    logFont.lfUnderline !is lfSystemFont.lfUnderline ||
+                    logFont.lfStrikeOut !is lfSystemFont.lfStrikeOut ||
+                    logFont.lfCharSet !is lfSystemFont.lfCharSet ||
+                    logFont.lfOutPrecision !is lfSystemFont.lfOutPrecision ||
+                    logFont.lfClipPrecision !is lfSystemFont.lfClipPrecision ||
+                    logFont.lfQuality !is lfSystemFont.lfQuality ||
+                    logFont.lfPitchAndFamily !is lfSystemFont.lfPitchAndFamily ||
+                    !getFontName (logFont).equals (getFontName (lfSystemFont))) {
+                        resources [resourceCount++] = systemFont;
+                        lfSystemFont = logFont;
+                        systemFont = null;
+                }
+            }
+        }
+    }
+    if (errorImage !is null) resources [resourceCount++] = errorImage;
+    if (infoImage !is null) resources [resourceCount++] = infoImage;
+    if (questionImage !is null) resources [resourceCount++] = questionImage;
+    if (warningIcon !is null) resources [resourceCount++] = warningIcon;
+    errorImage = infoImage = questionImage = warningIcon = null;
+    for (int i=0; i<cursors.length; i++) {
+        if (cursors [i] !is null) resources [resourceCount++] = cursors [i];
+        cursors [i] = null;
+    }
+    if (resourceCount < RESOURCE_SIZE) {
+        Resource [] newResources = new Resource [resourceCount];
+        System.arraycopy (resources, 0, newResources, 0, resourceCount);
+        resources = newResources;
+    }
+}
+
+void sendEvent (int eventType, Event event) {
+    if (eventTable is null && filterTable is null) {
+        return;
+    }
+    if (event is null) event = new Event ();
+    event.display = this;
+    event.type = eventType;
+    if (event.time is 0) event.time = getLastEventTime ();
+    if (!filterEvent (event)) {
+        if (eventTable !is null) eventTable.sendEvent (event);
+    }
+}
+
+/**
+ * Sets the location of the on-screen pointer relative to the top left corner
+ * of the screen.  <b>Note: It is typically considered bad practice for a
+ * program to move the on-screen pointer location.</b>
+ *
+ * @param x the new x coordinate for the cursor
+ * @param y the new y coordinate for the cursor
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public void setCursorLocation (int x, int y) {
+    checkDevice ();
+    OS.SetCursorPos (x, y);
+}
+
+/**
+ * Sets the location of the on-screen pointer relative to the top left corner
+ * of the screen.  <b>Note: It is typically considered bad practice for a
+ * program to move the on-screen pointer location.</b>
+ *
+ * @param point new position
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public void setCursorLocation (Point point) {
+    checkDevice ();
+    if (point is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setCursorLocation (point.x, point.y);
+}
+
+/**
+ * Sets the application defined property of the receiver
+ * with the specified name to the given argument.
+ * <p>
+ * Applications may have associated arbitrary objects with the
+ * receiver in this fashion. If the objects stored in the
+ * properties need to be notified when the display is disposed
+ * of, it is the application's responsibility provide a
+ * <code>disposeExec()</code> handler which does so.
+ * </p>
+ *
+ * @param key the name of the property
+ * @param value the new value for the property
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the key is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #getData(String)
+ * @see #disposeExec(Runnable)
+ */
+public void setData (String key, Object value) {
+    checkDevice ();
+    if (key is null) error (DWT.ERROR_NULL_ARGUMENT);
+
+    if (key.equals (RUN_MESSAGES_IN_IDLE_KEY)) {
+        bool data = (bool) value;
+        runMessagesInIdle = data !is null && data.booleanValue ();
+        return;
+    }
+
+    /* Remove the key/value pair */
+    if (value is null) {
+        if (keys is null) return;
+        int index = 0;
+        while (index < keys.length && !keys [index].equals (key)) index++;
+        if (index is keys.length) return;
+        if (keys.length is 1) {
+            keys = null;
+            values = null;
+        } else {
+            String [] newKeys = new String [keys.length - 1];
+            Object [] newValues = new Object [values.length - 1];
+            System.arraycopy (keys, 0, newKeys, 0, index);
+            System.arraycopy (keys, index + 1, newKeys, index, newKeys.length - index);
+            System.arraycopy (values, 0, newValues, 0, index);
+            System.arraycopy (values, index + 1, newValues, index, newValues.length - index);
+            keys = newKeys;
+            values = newValues;
+        }
+        return;
+    }
+
+    /* Add the key/value pair */
+    if (keys is null) {
+        keys = new String [] {key};
+        values = new Object [] {value};
+        return;
+    }
+    for (int i=0; i<keys.length; i++) {
+        if (keys [i].equals (key)) {
+            values [i] = value;
+            return;
+        }
+    }
+    String [] newKeys = new String [keys.length + 1];
+    Object [] newValues = new Object [values.length + 1];
+    System.arraycopy (keys, 0, newKeys, 0, keys.length);
+    System.arraycopy (values, 0, newValues, 0, values.length);
+    newKeys [keys.length] = key;
+    newValues [values.length] = value;
+    keys = newKeys;
+    values = newValues;
+}
+
+/**
+ * Sets the application defined, display specific data
+ * associated with the receiver, to the argument.
+ * The <em>display specific data</em> is a single,
+ * unnamed field that is stored with every display.
+ * <p>
+ * Applications may put arbitrary objects in this field. If
+ * the object stored in the display specific data needs to
+ * be notified when the display is disposed of, it is the
+ * application's responsibility provide a
+ * <code>disposeExec()</code> handler which does so.
+ * </p>
+ *
+ * @param data the new display specific data
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #getData()
+ * @see #disposeExec(Runnable)
+ */
+public void setData (Object data) {
+    checkDevice ();
+    this.data = data;
+}
+
+/**
+ * On platforms which support it, sets the application name
+ * to be the argument. On Motif, for example, this can be used
+ * to set the name used for resource lookup.  Specifying
+ * <code>null</code> for the name clears it.
+ *
+ * @param name the new app name or <code>null</code>
+ */
+public static void setAppName (String name) {
+    /* Do nothing */
+}
+
+void setModalDialogShell (Shell modalDailog) {
+    if (modalDialogShell !is null && modalDialogShell.isDisposed ()) modalDialogShell = null;
+    this.modalDialogShell = modalDailog;
+    Shell [] shells = getShells ();
+    for (int i=0; i<shells.length; i++) shells [i].updateModal ();
+}
+
+void setModalShell (Shell shell) {
+    if (modalShells is null) modalShells = new Shell [4];
+    int index = 0, length = modalShells.length;
+    while (index < length) {
+        if (modalShells [index] is shell) return;
+        if (modalShells [index] is null) break;
+        index++;
+    }
+    if (index is length) {
+        Shell [] newModalShells = new Shell [length + 4];
+        System.arraycopy (modalShells, 0, newModalShells, 0, length);
+        modalShells = newModalShells;
+    }
+    modalShells [index] = shell;
+    Shell [] shells = getShells ();
+    for (int i=0; i<shells.length; i++) shells [i].updateModal ();
+}
+
+/**
+ * Sets the synchronizer used by the display to be
+ * the argument, which can not be null.
+ *
+ * @param synchronizer the new synchronizer for the display (must not be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the synchronizer is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_FAILED_EXEC - if an exception occurred while running an inter-thread message</li>
+ * </ul>
+ */
+public void setSynchronizer (Synchronizer synchronizer) {
+    checkDevice ();
+    if (synchronizer is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (this.synchronizer !is null) {
+        this.synchronizer.runAsyncMessages(true);
+    }
+    this.synchronizer = synchronizer;
+}
+
+int shiftedKey (int key) {
+    if (OS.IsWinCE) return 0;
+
+    /* Clear the virtual keyboard and press the shift key */
+    for (int i=0; i<keyboard.length; i++) keyboard [i] = 0;
+    keyboard [OS.VK_SHIFT] |= 0x80;
+
+    /* Translate the key to ASCII or UNICODE using the virtual keyboard */
+    if (OS.IsUnicode) {
+        char [] result = new char [1];
+        if (OS.ToUnicode (key, key, keyboard, result, 1, 0) is 1) return result [0];
+    } else {
+        short [] result = new short [1];
+        if (OS.ToAscii (key, key, keyboard, result, 0) is 1) return result [0];
+    }
+    return 0;
+}
+
+/**
+ * Causes the user-interface thread to <em>sleep</em> (that is,
+ * to be put in a state where it does not consume CPU cycles)
+ * until an event is received or it is otherwise awakened.
+ *
+ * @return <code>true</code> if an event requiring dispatching was placed on the queue.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #wake
+ */
+public bool sleep () {
+    checkDevice ();
+    if (runMessages && getMessageCount () !is 0) return true;
+    if (OS.IsWinCE) {
+        OS.MsgWaitForMultipleObjectsEx (0, 0, OS.INFINITE, OS.QS_ALLINPUT, OS.MWMO_INPUTAVAILABLE);
+        return true;
+    }
+    return OS.WaitMessage ();
+}
+
+/**
+ * Causes the <code>run()</code> method of the runnable to
+ * be invoked by the user-interface thread at the next
+ * reasonable opportunity. The thread which calls this method
+ * is suspended until the runnable completes.  Specifying <code>null</code>
+ * as the runnable simply wakes the user-interface thread.
+ * <p>
+ * Note that at the time the runnable is invoked, widgets
+ * that have the receiver as their display may have been
+ * disposed. Therefore, it is necessary to check for this
+ * case inside the runnable before accessing the widget.
+ * </p>
+ *
+ * @param runnable code to run on the user-interface thread or <code>null</code>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_FAILED_EXEC - if an exception occurred when executing the runnable</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #asyncExec
+ */
+public void syncExec (Runnable runnable) {
+    if (isDisposed ()) error (DWT.ERROR_DEVICE_DISPOSED);
+    synchronizer.syncExec (runnable);
+}
+
+/**
+ * Causes the <code>run()</code> method of the runnable to
+ * be invoked by the user-interface thread after the specified
+ * number of milliseconds have elapsed. If milliseconds is less
+ * than zero, the runnable is not executed.
+ * <p>
+ * Note that at the time the runnable is invoked, widgets
+ * that have the receiver as their display may have been
+ * disposed. Therefore, it is necessary to check for this
+ * case inside the runnable before accessing the widget.
+ * </p>
+ *
+ * @param milliseconds the delay before running the runnable
+ * @param runnable code to run on the user-interface thread
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the runnable is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #asyncExec
+ */
+public void timerExec (int milliseconds, Runnable runnable) {
+    checkDevice ();
+    if (runnable is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (timerList is null) timerList = new Runnable [4];
+    if (timerIds is null) timerIds = new int [4];
+    int index = 0;
+    while (index < timerList.length) {
+        if (timerList [index] is runnable) break;
+        index++;
+    }
+    int timerId = 0;
+    if (index !is timerList.length) {
+        timerId = timerIds [index];
+        if (milliseconds < 0) {
+            OS.KillTimer (hwndMessage, timerId);
+            timerList [index] = null;
+            timerIds [index] = 0;
+            return;
+        }
+    } else {
+        if (milliseconds < 0) return;
+        index = 0;
+        while (index < timerList.length) {
+            if (timerList [index] is null) break;
+            index++;
+        }
+        timerId = nextTimerId++;
+        if (index is timerList.length) {
+            Runnable [] newTimerList = new Runnable [timerList.length + 4];
+            System.arraycopy (timerList, 0, newTimerList, 0, timerList.length);
+            timerList = newTimerList;
+            int [] newTimerIds = new int [timerIds.length + 4];
+            System.arraycopy (timerIds, 0, newTimerIds, 0, timerIds.length);
+            timerIds = newTimerIds;
+        }
+    }
+    int newTimerID = OS.SetTimer (hwndMessage, timerId, milliseconds, 0);
+    if (newTimerID !is 0) {
+        timerList [index] = runnable;
+        timerIds [index] = newTimerID;
+    }
+}
+
+bool translateAccelerator (MSG msg, Control control) {
+    accelKeyHit = true;
+    bool result = control.translateAccelerator (msg);
+    accelKeyHit = false;
+    return result;
+}
+
+static int translateKey (int key) {
+    for (int i=0; i<KeyTable.length; i++) {
+        if (KeyTable [i] [0] is key) return KeyTable [i] [1];
+    }
+    return 0;
+}
+
+bool translateMnemonic (MSG msg, Control control) {
+    switch (msg.message) {
+        case OS.WM_CHAR:
+        case OS.WM_SYSCHAR:
+            return control.translateMnemonic (msg);
+    }
+    return false;
+}
+
+bool translateTraversal (MSG msg, Control control) {
+    switch (msg.message) {
+        case OS.WM_KEYDOWN:
+            switch (msg.wParam) {
+                case OS.VK_RETURN:
+                case OS.VK_ESCAPE:
+                case OS.VK_TAB:
+                case OS.VK_UP:
+                case OS.VK_DOWN:
+                case OS.VK_LEFT:
+                case OS.VK_RIGHT:
+                case OS.VK_PRIOR:
+                case OS.VK_NEXT:
+                    return control.translateTraversal (msg);
+            }
+            break;
+        case OS.WM_SYSKEYDOWN:
+            switch (msg.wParam) {
+                case OS.VK_MENU:
+                    return control.translateTraversal (msg);
+            }
+            break;
+    }
+    return false;
+}
+
+static int untranslateKey (int key) {
+    for (int i=0; i<KeyTable.length; i++) {
+        if (KeyTable [i] [1] is key) return KeyTable [i] [0];
+    }
+    return 0;
+}
+
+/**
+ * Forces all outstanding paint requests for the display
+ * to be processed before this method returns.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see Control#update()
+ */
+public void update() {
+    checkDevice ();
+    /*
+    * Feature in Windows.  When an application does not remove
+    * events from the event queue for some time, Windows assumes
+    * the application is not responding and no longer sends paint
+    * events to the application.  The fix is to detect that the
+    * application is not responding and call PeekMessage() with
+    * PM_REMOVE to tell Windows that the application is ready
+    * to dispatch events.  Note that the message does not have
+    * to be found or dispatched in order to wake Windows up.
+    *
+    * NOTE: This allows other cross thread messages to be delivered,
+    * most notably WM_ACTIVATE.
+    */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+        if (OS.IsHungAppWindow (hwndMessage)) {
+            MSG msg = new MSG ();
+            int flags = OS.PM_REMOVE | OS.PM_NOYIELD;
+            OS.PeekMessage (msg, hwndMessage, SWT_NULL, SWT_NULL, flags);
+        }
+    }
+    Shell[] shells = getShells ();
+    for (int i=0; i<shells.length; i++) {
+        Shell shell = shells [i];
+        if (!shell.isDisposed ()) shell.update (true);
+    }
+}
+
+void updateImages () {
+    if (upArrow !is null) upArrow.dispose ();
+    if (downArrow !is null) downArrow.dispose ();
+    upArrow = downArrow = null;
+    for (int i=0; i<controlTable.length; i++) {
+        Control control = controlTable [i];
+        if (control !is null) control.updateImages ();
+    }
+}
+
+/**
+ * If the receiver's user-interface thread was <code>sleep</code>ing,
+ * causes it to be awakened and start running again. Note that this
+ * method may be called from any thread.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ *
+ * @see #sleep
+ */
+public void wake () {
+    if (isDisposed ()) error (DWT.ERROR_DEVICE_DISPOSED);
+    if (thread is Thread.currentThread ()) return;
+    wakeThread ();
+}
+
+void wakeThread () {
+    if (OS.IsWinCE) {
+        OS.PostMessage (hwndMessage, OS.WM_NULL, 0, 0);
+    } else {
+        OS.PostThreadMessage (threadId, OS.WM_NULL, 0, 0);
+    }
+}
+
+/*
+ * Returns a single character, converted from the wide
+ * character set (WCS) used by Java to the specified
+ * multi-byte character set used by the operating system
+ * widgets.
+ *
+ * @param ch the WCS character
+ * @param codePage the code page used to convert the character
+ * @return the MBCS character
+ */
+static int wcsToMbcs (char ch, int codePage) {
+    if (OS.IsUnicode) return ch;
+    if (ch <= 0x7F) return ch;
+    TCHAR buffer = new TCHAR (codePage, ch, false);
+    return buffer.tcharAt (0);
+}
+
+/*
+ * Returns a single character, converted from the wide
+ * character set (WCS) used by Java to the default
+ * multi-byte character set used by the operating system
+ * widgets.
+ *
+ * @param ch the WCS character
+ * @return the MBCS character
+ */
+static int wcsToMbcs (char ch) {
+    return wcsToMbcs (ch, 0);
+}
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    /*
+    * Bug in Adobe Reader 7.0.  For some reason, when Adobe
+    * Reader 7.0 is deactivated from within Internet Explorer,
+    * it sends thousands of consecutive WM_NCHITTEST messages
+    * to the control that is under the cursor.  It seems that
+    * if the control takes some time to respond to the message,
+    * Adobe stops sending them.  The fix is to detect this case
+    * and sleep.
+    *
+    * NOTE: Under normal circumstances, Windows will never send
+    * consecutive WM_NCHITTEST messages to the same control without
+    * another message (normally WM_SETCURSOR) in between.
+    */
+    if (msg is OS.WM_NCHITTEST) {
+        if (hitCount++ >= 1024) {
+            try {Thread.sleep (1);} catch (Throwable t) {}
+        }
+    } else {
+        hitCount = 0;
+    }
+    if (lastControl !is null && lastHwnd is hwnd) {
+        return lastControl.windowProc (hwnd, msg, wParam, lParam);
+    }
+    int index;
+    if (USE_PROPERTY) {
+        index = OS.GetProp (hwnd, SWT_OBJECT_INDEX) - 1;
+    } else {
+        index = OS.GetWindowLong (hwnd, OS.GWL_USERDATA) - 1;
+    }
+    if (0 <= index && index < controlTable.length) {
+        Control control = controlTable [index];
+        if (control !is null) {
+            lastHwnd = hwnd;
+            lastControl = control;
+            return control.windowProc (hwnd, msg, wParam, lParam);
+        }
+    }
+    return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+}
+
+static String withCrLf (String string) {
+
+    /* If the string is empty, return the string. */
+    int length = string.length ();
+    if (length is 0) return string;
+
+    /*
+    * Check for an LF or CR/LF and assume the rest of
+    * the string is formated that way.  This will not
+    * work if the string contains mixed delimiters.
+    */
+    int i = string.indexOf ('\n', 0);
+    if (i is -1) return string;
+    if (i > 0 && string.charAt (i - 1) is '\r') {
+        return string;
+    }
+
+    /*
+    * The string is formatted with LF.  Compute the
+    * number of lines and the size of the buffer
+    * needed to hold the result
+    */
+    i++;
+    int count = 1;
+    while (i < length) {
+        if ((i = string.indexOf ('\n', i)) is -1) break;
+        count++; i++;
+    }
+    count += length;
+
+    /* Create a new string with the CR/LF line terminator. */
+    i = 0;
+    StringBuffer result = new StringBuffer (count);
+    while (i < length) {
+        int j = string.indexOf ('\n', i);
+        if (j is -1) j = length;
+        result.append (string.substring (i, j));
+        if ((i = j) < length) {
+            result.append ("\r\n"); //$NON-NLS-1$
+            i++;
+        }
+    }
+    return result.toString ();
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Event.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Event;
+
+import dwt.graphics.GC;
+import dwt.graphics.Rectangle;
+
+import dwt.widgets.Widget;
+import dwt.widgets.Display;
+
+import tango.text.convert.Format;
+
+
+/**
+ * Instances of this class provide a description of a particular
+ * event which occurred within DWT. The DWT <em>untyped listener</em>
+ * API uses these instances for all event dispatching.
+ * <p>
+ * Note: For a given event, only the fields which are appropriate
+ * will be filled in. The contents of the fields which are not used
+ * by the event are unspecified.
+ * </p>
+ *
+ * @see Listener
+ * @see dwt.events.TypedEvent
+ */
+
+public class Event {
+
+    /**
+     * the display where the event occurred
+     *
+     * @since 2.0
+     */
+    public Display display;
+
+    /**
+     * the widget that issued the event
+     */
+    public Widget widget;
+
+    /**
+     * the type of event, as defined by the event type constants
+     * in class <code>DWT</code>
+     *
+     * @see dwt.DWT
+     */
+    public int type;
+
+    /**
+     * the event specific detail field, as defined by the detail constants
+     * in class <code>DWT</code>
+     *
+     * @see dwt.DWT
+     */
+    public int detail;
+
+    /**
+     * the item that the event occurred in (can be null)
+     */
+    public Widget item;
+
+    /**
+     * the index of the item where the event occurred
+     *
+     * @since 3.2
+     */
+    public int index;
+
+    /**
+     * the graphics context to use when painting
+     * that is configured to use the colors, font and
+     * damaged region of the control.  It is valid
+     * only during the paint and must not be disposed
+     */
+    public GC gc;
+
+    /**
+     * depending on the event type, the x offset of the bounding
+     * rectangle of the region that requires painting or the
+     * widget-relative, x coordinate of the pointer at the
+     * time the mouse button was pressed or released
+     */
+    public int x;
+
+    /**
+     * depending on the event type, the y offset of the bounding
+     * rectangle of the  region that requires painting or the
+     * widget-relative, y coordinate of the pointer at the
+     * time the mouse button was pressed or released
+     */
+    public int y;
+
+    /**
+     * the width of the bounding rectangle of the
+     * region that requires painting
+     */
+    public int width;
+
+    /**
+     * the height of the bounding rectangle of the
+     * region that requires painting
+     */
+    public int height;
+
+    /**
+     * depending on the event type, the number of following
+     * paint events which are pending which may always be zero
+     * on some platforms or the number of lines or pages to
+     * scroll using the mouse wheel
+     */
+    public int count;
+
+    /**
+     * the time that the event occurred.
+     *
+     * NOTE: This field is an unsigned integer and should
+     * be AND'ed with 0xFFFFFFFFL so that it can be treated
+     * as a signed long.
+     */
+    public int time;
+
+    /**
+     * the button that was pressed or released; 1 for the
+     * first button, 2 for the second button, and 3 for the
+     * third button, etc.
+     */
+    public int button;
+
+    /**
+     * depending on the event, the character represented by the key
+     * that was typed.  This is the final character that results
+     * after all modifiers have been applied.  For example, when the
+     * user types Ctrl+A, the character value is 0x01 (ASCII SOH).
+     * It is important that applications do not attempt to modify the
+     * character value based on a stateMask (such as DWT.CTRL) or the
+     * resulting character will not be correct.
+     */
+    public wchar character = '\0';
+
+    /**
+     * depending on the event, the key code of the key that was typed,
+     * as defined by the key code constants in class <code>DWT</code>.
+     * When the character field of the event is ambiguous, this field
+     * contains the unaffected value of the original character.  For
+     * example, typing Ctrl+M or Enter both result in the character '\r'
+     * but the keyCode field will also contain '\r' when Enter was typed
+     * and 'm' when Ctrl+M was typed.
+     *
+     * @see dwt.DWT
+     */
+    public int keyCode;
+
+    /**
+     * depending on the event, the state of the keyboard modifier
+     * keys and mouse masks at the time the event was generated.
+     *
+     * @see dwt.DWT
+     */
+    public int stateMask;
+
+    /**
+     * depending on the event, the range of text being modified.
+     * Setting these fields has no effect.
+     */
+    public int start, end;
+
+    /**
+     * depending on the event, the new text that will be inserted.
+     * Setting this field will change the text that is about to
+     * be inserted or deleted.
+     */
+    public char[] text;
+
+    /**
+     * depending on the event, a flag indicating whether the operation
+     * should be allowed.  Setting this field to false will cancel the
+     * operation.
+     */
+    public bool doit = true;
+
+    /**
+     * a field for application use
+     */
+    public Object data;
+
+/**
+ * Gets the bounds.
+ *
+ * @return a rectangle that is the bounds.
+ */
+public Rectangle getBounds () {
+    return new Rectangle (x, y, width, height);
+}
+
+/**
+ * Sets the bounds.
+ *
+ * @param rect the new rectangle
+ */
+public void setBounds (Rectangle rect) {
+    this.x = rect.x;
+    this.y = rect.y;
+    this.width = rect.width;
+    this.height = rect.height;
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the event
+ */
+public char[] toString () {
+    return Format( "Event {type={} {} time={} data={} x={} y={} width={} height={} detail={}}",
+        type, widget, time, data, x, y, width, height, detail );  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/EventTable.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.EventTable;
+
+import dwt.widgets.Listener;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.TypedListener;
+import dwt.internal.DWTEventListener;
+import dwt.dwthelper.System;
+import dwt.DWT;
+import dwt.internal.DWTEventListener;
+
+/**
+ * Instances of this class implement a simple
+ * look up mechanism that maps an event type
+ * to a listener.  Multiple listeners for the
+ * same event type are supported.
+ */
+
+class EventTable {
+    int [] types;
+    Listener [] listeners;
+    int level;
+
+public void hook (int eventType, Listener listener) {
+    if (types is null) types = new int [4];
+    if (listeners is null) listeners = new Listener [4];
+    int length = types.length, index = length - 1;
+    while (index >= 0) {
+        if (types [index] !is 0) break;
+        --index;
+    }
+    index++;
+    if (index is length) {
+        int [] newTypes = new int [length + 4];
+        System.arraycopy (types, 0, newTypes, 0, length);
+        types = newTypes;
+        Listener [] newListeners = new Listener [length + 4];
+        SimpleType!(Listener).arraycopy (listeners, 0, newListeners, 0, length);
+        listeners = newListeners;
+    }
+    types [index] = eventType;
+    listeners [index] = listener;
+}
+
+public bool hooks (int eventType) {
+    if (types is null) return false;
+    for (int i=0; i<types.length; i++) {
+        if (types [i] is eventType) return true;
+    }
+    return false;
+}
+
+public void sendEvent (Event event) {
+    if (types is null) return;
+    level += level >= 0 ? 1 : -1;
+    try {
+        for (int i=0; i<types.length; i++) {
+            if (event.type is DWT.None) return;
+            if (types [i] is event.type) {
+                Listener listener = listeners [i];
+                if (listener !is null) listener.handleEvent (event);
+            }
+        }
+    } finally {
+        bool compact = level < 0;
+        level -= level >= 0 ? 1 : -1;
+        if (compact && level is 0) {
+            int index = 0;
+            for (int i=0; i<types.length; i++) {
+                if (types [i] !is 0) {
+                    types [index] = types [i];
+                    listeners [index] = listeners [i];
+                    index++;
+                }
+            }
+            for (int i=index; i<types.length; i++) {
+                types [i] = 0;
+                listeners [i] = null;
+            }
+        }
+    }
+}
+
+public int size () {
+    if (types is null) return 0;
+    int count = 0;
+    for (int i=0; i<types.length; i++) {
+        if (types [i] !is 0) count++;
+    }
+    return count;
+}
+
+void remove (int index) {
+    if (level is 0) {
+        int end = types.length - 1;
+        System.arraycopy (types, index + 1, types, index, end - index);
+        SimpleType!(Listener).arraycopy (listeners, index + 1, listeners, index, end - index);
+        index = end;
+    } else {
+        if (level > 0) level = -level;
+    }
+    types [index] = 0;
+    listeners [index] = null;
+}
+
+public void unhook (int eventType, Listener listener) {
+    if (types is null) return;
+    for (int i=0; i<types.length; i++) {
+        if (types [i] is eventType && listeners [i] is listener) {
+            remove (i);
+            return;
+        }
+    }
+}
+
+public void unhook (int eventType, DWTEventListener listener) {
+    if (types is null) return;
+    for (int i=0; i<types.length; i++) {
+        if (types [i] is eventType) {
+            if ( auto typedListener = cast(TypedListener) listeners [i] ) {
+                if (typedListener.getEventListener () is listener) {
+                    remove (i);
+                    return;
+                }
+            }
+        }
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/ExpandBar.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,792 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.ExpandBar;
+
+import dwt.widgets.Composite;
+class ExpandBar : Composite {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.ExpandAdapter;
+import dwt.events.ExpandEvent;
+import dwt.events.ExpandListener;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.LOGFONT;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.NONCLIENTMETRICS;
+import dwt.internal.win32.NONCLIENTMETRICSA;
+import dwt.internal.win32.NONCLIENTMETRICSW;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.PAINTSTRUCT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SCROLLINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+
+/**
+ * Instances of this class support the layout of selectable
+ * expand bar items.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>ExpandItem</code>.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>V_SCROLL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Expand, Collapse</dd>
+ * </dl>
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see ExpandItem
+ * @see ExpandEvent
+ * @see ExpandListener
+ * @see ExpandAdapter
+ *
+ * @since 3.2
+ */
+public class ExpandBar extends Composite {
+    ExpandItem[] items;
+    int itemCount;
+    ExpandItem focusItem;
+    int spacing = 4;
+    int yCurrentScroll;
+    int hFont;
+
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public ExpandBar (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when an item in the receiver is expanded or collapsed
+ * by sending it one of the messages defined in the <code>ExpandListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ExpandListener
+ * @see #removeExpandListener
+ */
+public void addExpandListener (ExpandListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Expand, typedListener);
+    addListener (DWT.Collapse, typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+static int checkStyle (int style) {
+    style &= ~DWT.H_SCROLL;
+    return style | DWT.NO_BACKGROUND;
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int height = 0, width = 0;
+    if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) {
+        if (itemCount > 0) {
+            int hDC = OS.GetDC (handle);
+            int hTheme = 0;
+            if (isAppThemed ()) {
+                hTheme = display.hExplorerBarTheme ();
+            }
+            int hCurrentFont = 0, oldFont = 0;
+            if (hTheme is 0) {
+                if (hFont !is 0) {
+                    hCurrentFont = hFont;
+                } else {
+                    if (!OS.IsWinCE) {
+                        NONCLIENTMETRICS info = OS.IsUnicode ? (NONCLIENTMETRICS) new NONCLIENTMETRICSW () : new NONCLIENTMETRICSA ();
+                        info.cbSize = NONCLIENTMETRICS.sizeof;
+                        if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, info, 0)) {
+                            LOGFONT logFont = OS.IsUnicode ? (LOGFONT) ((NONCLIENTMETRICSW)info).lfCaptionFont : ((NONCLIENTMETRICSA)info).lfCaptionFont;
+                            hCurrentFont = OS.CreateFontIndirect (logFont);
+                        }
+                    }
+                }
+                if (hCurrentFont !is 0) {
+                    oldFont = OS.SelectObject (hDC, hCurrentFont);
+                }
+            }
+            height += spacing;
+            for (int i = 0; i < itemCount; i++) {
+                ExpandItem item = items [i];
+                height += item.getHeaderHeight ();
+                if (item.expanded) height += item.height;
+                height += spacing;
+                width = Math.max (width, item.getPreferredWidth (hTheme, hDC));
+            }
+            if (hCurrentFont !is 0) {
+                OS.SelectObject (hDC, oldFont);
+                if (hCurrentFont !is hFont) OS.DeleteObject (hCurrentFont);
+            }
+            OS.ReleaseDC (handle, hDC);
+        }
+    }
+    if (width is 0) width = DEFAULT_WIDTH;
+    if (height is 0) height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    Rectangle trim = computeTrim (0, 0, width, height);
+    return new Point (trim.width, trim.height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state &= ~CANVAS;
+    state |= TRACK_MOUSE;
+}
+
+void createItem (ExpandItem item, int style, int index) {
+    if (!(0 <= index && index <= itemCount)) error (DWT.ERROR_INVALID_RANGE);
+    if (itemCount is items.length) {
+        ExpandItem [] newItems = new ExpandItem [itemCount + 4];
+        System.arraycopy (items, 0, newItems, 0, items.length);
+        items = newItems;
+    }
+    System.arraycopy (items, index, items, index + 1, itemCount - index);
+    items [index] = item;
+    itemCount++;
+    if (focusItem is null) focusItem = item;
+
+    RECT rect = new RECT ();
+    OS.GetWindowRect (handle, rect);
+    item.width = Math.max (0, rect.right - rect.left - spacing * 2);
+    layoutItems (index, true);
+}
+
+void createWidget () {
+    super.createWidget ();
+    items = new ExpandItem [4];
+    if (!isAppThemed ()) {
+        backgroundMode = DWT.INHERIT_DEFAULT;
+    }
+}
+
+int defaultBackground() {
+    if (!isAppThemed ()) {
+        return OS.GetSysColor (OS.COLOR_WINDOW);
+    }
+    return super.defaultBackground();
+}
+
+void destroyItem (ExpandItem item) {
+    int index = 0;
+    while (index < itemCount) {
+        if (items [index] is item) break;
+        index++;
+    }
+    if (index is itemCount) return;
+    if (item is focusItem) {
+        int focusIndex = index > 0 ? index - 1 : 1;
+        if (focusIndex < itemCount) {
+            focusItem = items [focusIndex];
+            focusItem.redraw (true);
+        } else {
+            focusItem = null;
+        }
+    }
+    System.arraycopy (items, index + 1, items, index, --itemCount - index);
+    items [itemCount] = null;
+    item.redraw (true);
+    layoutItems (index, true);
+}
+
+void drawThemeBackground (int hDC, int hwnd, RECT rect) {
+    RECT rect2 = new RECT ();
+    OS.GetClientRect (handle, rect2);
+    OS.MapWindowPoints (handle, hwnd, rect2, 2);
+    OS.DrawThemeBackground (display.hExplorerBarTheme (), hDC, OS.EBP_NORMALGROUPBACKGROUND, 0, rect2, null);
+}
+
+void drawWidget (GC gc, RECT clipRect) {
+    int hTheme = 0;
+    if (isAppThemed ()) {
+        hTheme = display.hExplorerBarTheme ();
+    }
+    if (hTheme !is 0) {
+        RECT rect = new RECT ();
+        OS.GetClientRect (handle, rect);
+        OS.DrawThemeBackground (hTheme, gc.handle, OS.EBP_HEADERBACKGROUND, 0, rect, clipRect);
+    } else {
+        drawBackground (gc.handle);
+    }
+    bool drawFocus = false;
+    if (handle is OS.GetFocus ()) {
+        int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+        drawFocus = (uiState & OS.UISF_HIDEFOCUS) is 0;
+    }
+    int hCaptionFont = 0, oldFont = 0;
+    if (hTheme is 0) {
+        if (!OS.IsWinCE && hFont is 0) {
+            NONCLIENTMETRICS info = OS.IsUnicode ? (NONCLIENTMETRICS) new NONCLIENTMETRICSW () : new NONCLIENTMETRICSA ();
+            info.cbSize = NONCLIENTMETRICS.sizeof;
+            if (OS.SystemParametersInfo (OS.SPI_GETNONCLIENTMETRICS, 0, info, 0)) {
+                LOGFONT logFont = OS.IsUnicode ? (LOGFONT) ((NONCLIENTMETRICSW)info).lfCaptionFont : ((NONCLIENTMETRICSA)info).lfCaptionFont;
+                hCaptionFont = OS.CreateFontIndirect (logFont);
+                oldFont = OS.SelectObject (gc.handle, hCaptionFont);
+            }
+        }
+    }
+    for (int i = 0; i < itemCount; i++) {
+        ExpandItem item = items[i];
+        item.drawItem (gc, hTheme, clipRect, item is focusItem && drawFocus);
+    }
+    if (hCaptionFont !is 0) {
+        OS.SelectObject (gc.handle, oldFont);
+        OS.DeleteObject (hCaptionFont);
+    }
+}
+
+Control findBackgroundControl () {
+    Control control = super.findBackgroundControl ();
+    if (!isAppThemed ()) {
+        if (control is null) control = this;
+    }
+    return control;
+}
+
+Control findThemeControl () {
+    return isAppThemed () ? this : super.findThemeControl ();
+}
+
+int getBandHeight () {
+    if (hFont is 0) return ExpandItem.CHEVRON_SIZE;
+    int hDC = OS.GetDC (handle);
+    int oldHFont = OS.SelectObject (hDC, hFont);
+    TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA();
+    OS.GetTextMetrics (hDC, lptm);
+    OS.SelectObject (hDC, oldHFont);
+    OS.ReleaseDC (handle, hDC);
+    return Math.max (ExpandItem.CHEVRON_SIZE, lptm.tmHeight + 4);
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public ExpandItem getItem (int index) {
+    checkWidget ();
+    if (!(0 <= index && index < itemCount)) error (DWT.ERROR_INVALID_RANGE);
+    return items [index];
+}
+
+/**
+ * Returns the number of items contained in the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    return itemCount;
+}
+
+/**
+ * Returns an array of <code>ExpandItem</code>s which are the items
+ * in the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public ExpandItem [] getItems () {
+    checkWidget ();
+    ExpandItem [] result = new ExpandItem [itemCount];
+    System.arraycopy (items, 0, result, 0, itemCount);
+    return result;
+}
+
+/**
+ * Returns the receiver's spacing.
+ *
+ * @return the spacing
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSpacing () {
+    checkWidget ();
+    return spacing;
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (ExpandItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    for (int i = 0; i < itemCount; i++) {
+        if (items [i] is item) return i;
+    }
+    return -1;
+}
+
+bool isAppThemed () {
+    if (background !is -1) return false;
+    if (foreground !is -1) return false;
+    if (hFont !is 0) return false;
+    return OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ();
+}
+
+void layoutItems (int index, bool setScrollbar) {
+    if (index < itemCount) {
+        int y = spacing - yCurrentScroll;
+        for (int i = 0; i < index; i++) {
+            ExpandItem item = items [i];
+            if (item.expanded) y += item.height;
+            y += item.getHeaderHeight () + spacing;
+        }
+        for (int i = index; i < itemCount; i++) {
+            ExpandItem item = items [i];
+            item.setBounds (spacing, y, 0, 0, true, false);
+            if (item.expanded) y += item.height;
+            y += item.getHeaderHeight () + spacing;
+        }
+    }
+    if (setScrollbar) setScrollbar ();
+}
+
+void releaseChildren (bool destroy) {
+    if (items !is null) {
+        for (int i=0; i<items.length; i++) {
+            ExpandItem item = items [i];
+            if (item !is null && !item.isDisposed ()) {
+                item.release (false);
+            }
+        }
+        items = null;
+    }
+    focusItem = null;
+    super.releaseChildren (destroy);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when items in the receiver are expanded or collapsed.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ExpandListener
+ * @see #addExpandListener
+ */
+public void removeExpandListener (ExpandListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Expand, listener);
+    eventTable.unhook (DWT.Collapse, listener);
+}
+
+void setBackgroundPixel (int pixel) {
+    super.setBackgroundPixel (pixel);
+    if (!OS.IsWinCE) {
+        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+        OS.RedrawWindow (handle, null, 0, flags);
+    }
+}
+
+public void setFont (Font font) {
+    super.setFont (font);
+    hFont = font !is null ? font.handle : 0;
+    layoutItems (0, true);
+}
+
+void setForegroundPixel (int pixel) {
+    super.setForegroundPixel (pixel);
+    if (!OS.IsWinCE) {
+        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+        OS.RedrawWindow (handle, null, 0, flags);
+    }
+}
+
+void setScrollbar () {
+    if (itemCount is 0) return;
+    if ((style & DWT.V_SCROLL) is 0) return;
+    RECT rect = new RECT();
+    OS.GetClientRect (handle, rect);
+    int height = rect.bottom - rect.top;
+    ExpandItem item = items [itemCount - 1];
+    int maxHeight = item.y + getBandHeight () + spacing;
+    if (item.expanded) maxHeight += item.height;
+
+    //claim bottom free space
+    if (yCurrentScroll > 0 && height > maxHeight) {
+        yCurrentScroll = Math.max (0, yCurrentScroll + maxHeight - height);
+        layoutItems (0, false);
+    }
+    maxHeight += yCurrentScroll;
+
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_RANGE | OS.SIF_PAGE | OS.SIF_POS;
+    info.nMin = 0;
+    info.nMax = maxHeight;
+    info.nPage = height;
+    info.nPos = Math.min (yCurrentScroll, info.nMax);
+    if (info.nPage !is 0) info.nPage++;
+    OS.SetScrollInfo (handle, OS.SB_VERT, info, true);
+}
+
+/**
+ * Sets the receiver's spacing. Spacing specifies the number of pixels allocated around
+ * each item.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSpacing (int spacing) {
+    checkWidget ();
+    if (spacing < 0) return;
+    if (spacing is this.spacing) return;
+    this.spacing = spacing;
+    RECT rect = new RECT ();
+    OS.GetClientRect (handle, rect);
+    int width = Math.max (0, (rect.right - rect.left) - spacing * 2);
+    for (int i = 0; i < itemCount; i++) {
+        ExpandItem item = items[i];
+        if (item.width !is width) item.setBounds (0, 0, width, item.height, false, true);
+    }
+    layoutItems (0, true);
+    OS.InvalidateRect (handle, null, true);
+}
+
+void showItem (ExpandItem item) {
+    Control control = item.control;
+    if (control !is null && !control.isDisposed ()) {
+        control.setVisible (item.expanded);
+    }
+    item.redraw (true);
+    int index = indexOf (item);
+    layoutItems (index + 1, true);
+}
+
+TCHAR windowClass () {
+    return display.windowClass;
+}
+
+int windowProc () {
+    return display.windowProc;
+}
+
+LRESULT WM_KEYDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_KEYDOWN (wParam, lParam);
+    if (result !is null) return result;
+    if (focusItem is null) return result;
+    switch (wParam) {
+        case OS.VK_SPACE:
+        case OS.VK_RETURN:
+            Event event = new Event ();
+            event.item = focusItem;
+            sendEvent (focusItem.expanded ? DWT.Collapse : DWT.Expand, event);
+            focusItem.expanded = !focusItem.expanded;
+            showItem (focusItem);
+            return LRESULT.ZERO;
+        case OS.VK_UP: {
+            int focusIndex = indexOf (focusItem);
+            if (focusIndex > 0) {
+                focusItem.redraw (true);
+                focusItem = items [focusIndex - 1];
+                focusItem.redraw (true);
+                return LRESULT.ZERO;
+            }
+            break;
+        }
+        case OS.VK_DOWN: {
+            int focusIndex = indexOf (focusItem);
+            if (focusIndex < itemCount - 1) {
+                focusItem.redraw (true);
+                focusItem = items [focusIndex + 1];
+                focusItem.redraw (true);
+                return LRESULT.ZERO;
+            }
+            break;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
+    if (focusItem !is null) focusItem.redraw (true);
+    return result;
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+    int x = (short) (lParam & 0xFFFF);
+    int y = (short) (lParam >> 16);
+    for (int i = 0; i < itemCount; i++) {
+        ExpandItem item = items[i];
+        bool hover = item.isHover (x, y);
+        if (hover && focusItem !is item) {
+            focusItem.redraw (true);
+            focusItem = item;
+            focusItem.redraw (true);
+            forceFocus ();
+            break;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_LBUTTONUP (int wParam, int lParam) {
+    LRESULT result = super.WM_LBUTTONUP (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+    if (focusItem is null) return result;
+    int x = (short) (lParam & 0xFFFF);
+    int y = (short) (lParam >> 16);
+    bool hover = focusItem.isHover (x, y);
+    if (hover) {
+        Event event = new Event ();
+        event.item = focusItem;
+        sendEvent (focusItem.expanded ? DWT.Collapse : DWT.Expand, event);
+        focusItem.expanded = !focusItem.expanded;
+        showItem (focusItem);
+    }
+    return result;
+}
+
+LRESULT WM_MOUSELEAVE (int wParam, int lParam) {
+    LRESULT result = super.WM_MOUSELEAVE (wParam, lParam);
+    if (result !is null) return result;
+    for (int i = 0; i < itemCount; i++) {
+        ExpandItem item = items [i];
+        if (item.hover) {
+            item.hover = false;
+            item.redraw (false);
+            break;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_MOUSEMOVE (int wParam, int lParam) {
+    LRESULT result = super.WM_MOUSEMOVE (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+    int x = (short) (lParam & 0xFFFF);
+    int y = (short) (lParam >> 16);
+    for (int i = 0; i < itemCount; i++) {
+        ExpandItem item = items [i];
+        bool hover = item.isHover (x, y);
+        if (item.hover !is hover) {
+            item.hover = hover;
+            item.redraw (false);
+        }
+    }
+    return result;
+}
+
+LRESULT WM_PAINT (int wParam, int lParam) {
+    PAINTSTRUCT ps = new PAINTSTRUCT ();
+    GCData data = new GCData ();
+    data.ps = ps;
+    data.hwnd = handle;
+    GC gc = new_GC (data);
+    if (gc !is null) {
+        int width = ps.right - ps.left;
+        int height = ps.bottom - ps.top;
+        if (width !is 0 && height !is 0) {
+            RECT rect = new RECT ();
+            OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+            drawWidget (gc, rect);
+            if (hooks (DWT.Paint) || filters (DWT.Paint)) {
+                Event event = new Event ();
+                event.gc = gc;
+                event.x = rect.left;
+                event.y = rect.top;
+                event.width = width;
+                event.height = height;
+                sendEvent (DWT.Paint, event);
+                event.gc = null;
+            }
+        }
+        gc.dispose ();
+    }
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_PRINTCLIENT (int wParam, int lParam) {
+    LRESULT result = super.WM_PRINTCLIENT (wParam, lParam);
+    RECT rect = new RECT ();
+    OS.GetClientRect (handle, rect);
+    GCData data = new GCData ();
+    data.device = display;
+    data.foreground = getForegroundPixel ();
+    GC gc = GC.win32_new (wParam, data);
+    drawWidget (gc, rect);
+    gc.dispose ();
+    return result;
+}
+
+LRESULT WM_SETCURSOR (int wParam, int lParam) {
+    LRESULT result = super.WM_SETCURSOR (wParam, lParam);
+    if (result !is null) return result;
+    int hitTest = lParam & 0xFFFF;
+    if (hitTest is OS.HTCLIENT) {
+        for (int i = 0; i < itemCount; i++) {
+            ExpandItem item = items [i];
+            if (item.hover) {
+                int hCursor = OS.LoadCursor (0, OS.IDC_HAND);
+                OS.SetCursor (hCursor);
+                return LRESULT.ONE;
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_SETFOCUS (wParam, lParam);
+    if (focusItem !is null) focusItem.redraw (true);
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    RECT rect = new RECT ();
+    OS.GetClientRect (handle, rect);
+    int width = Math.max (0, (rect.right - rect.left) - spacing * 2);
+    for (int i = 0; i < itemCount; i++) {
+        ExpandItem item = items[i];
+        if (item.width !is width) item.setBounds (0, 0, width, item.height, false, true);
+    }
+    setScrollbar ();
+    OS.InvalidateRect (handle, null, true);
+    return result;
+}
+
+LRESULT wmScroll (ScrollBar bar, bool update, int hwnd, int msg, int wParam, int lParam) {
+    LRESULT result = super.wmScroll (bar, true, hwnd, msg, wParam, lParam);
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_POS;
+    OS.GetScrollInfo (handle, OS.SB_VERT, info);
+    int updateY = yCurrentScroll - info.nPos;
+    OS.ScrollWindowEx (handle, 0, updateY, null, null, 0, null, OS.SW_SCROLLCHILDREN | OS.SW_INVALIDATE);
+    yCurrentScroll = info.nPos;
+    if (updateY !is 0) {
+        for (int i = 0; i < itemCount; i++) {
+            items [i].y += updateY;
+        }
+    }
+    return result;
+}
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/ExpandItem.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,504 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.ExpandItem;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+class ExpandItem : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class represent a selectable user interface object
+ * that represents a expandable item in a expand bar.
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see ExpandBar
+ *
+ * @since 3.2
+ */
+public class ExpandItem extends Item {
+    ExpandBar parent;
+    Control control;
+    bool expanded, hover;
+    int x, y, width, height;
+    int imageHeight, imageWidth;
+    static final int TEXT_INSET = 6;
+    static final int BORDER = 1;
+    static final int CHEVRON_SIZE = 24;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public ExpandItem (ExpandBar parent, int style) {
+    this (parent, style, checkNull (parent).getItemCount ());
+}
+
+/**
+ * Constructs a new instance of this class given its parent, a
+ * style value describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public ExpandItem (ExpandBar parent, int style, int index) {
+    super (parent, style);
+    this.parent = parent;
+    parent.createItem (this, style, index);
+}
+
+static ExpandBar checkNull (ExpandBar control) {
+    if (control is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    return control;
+}
+
+private void drawChevron (int hDC, RECT rect) {
+    int oldBrush = OS.SelectObject (hDC, OS.GetSysColorBrush (OS.COLOR_BTNFACE));
+    OS.PatBlt (hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
+    OS.SelectObject (hDC, oldBrush);
+    rect.left += 4;
+    rect.top += 4;
+    rect.right -= 4;
+    rect.bottom -= 4;
+    int hPen = OS.CreatePen (OS.PS_SOLID, 1, parent.foreground);
+    int oldPen = OS.SelectObject (hDC, hPen);
+    int [] polyline1, polyline2;
+    if (expanded) {
+        int px = rect.left + 5;
+        int py = rect.top + 7;
+        polyline1 = new int [] {
+                px,py, px+1,py, px+1,py-1, px+2,py-1, px+2,py-2, px+3,py-2, px+3,py-3,
+                px+3,py-2, px+4,py-2, px+4,py-1, px+5,py-1, px+5,py, px+7,py};
+        py += 4;
+        polyline2 = new int [] {
+                px,py, px+1,py, px+1,py-1, px+2,py-1, px+2,py-2, px+3,py-2, px+3,py-3,
+                px+3,py-2, px+4,py-2, px+4,py-1,  px+5,py-1, px+5,py, px+7,py};
+    } else {
+        int px = rect.left + 5;
+        int py = rect.top + 4;
+        polyline1 = new int[] {
+                px,py, px+1,py, px+1,py+1, px+2,py+1, px+2,py+2, px+3,py+2, px+3,py+3,
+                px+3,py+2, px+4,py+2, px+4,py+1,  px+5,py+1, px+5,py, px+7,py};
+        py += 4;
+        polyline2 = new int [] {
+                px,py, px+1,py, px+1,py+1, px+2,py+1, px+2,py+2, px+3,py+2, px+3,py+3,
+                px+3,py+2, px+4,py+2, px+4,py+1,  px+5,py+1, px+5,py, px+7,py};
+    }
+    OS.Polyline (hDC, polyline1, polyline1.length / 2);
+    OS.Polyline (hDC, polyline2, polyline2.length / 2);
+    if (hover) {
+        int whitePen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor (OS.COLOR_3DHILIGHT));
+        int darkGrayPen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor (OS.COLOR_3DSHADOW));
+        OS.SelectObject (hDC, whitePen);
+        int [] points1 = {
+                rect.left, rect.bottom,
+                rect.left, rect.top,
+                rect.right, rect.top};
+        OS.Polyline (hDC, points1, points1.length / 2);
+        OS.SelectObject (hDC, darkGrayPen);
+        int [] points2 = {
+                rect.right, rect.top,
+                rect.right, rect.bottom,
+                rect.left, rect.bottom};
+        OS.Polyline (hDC, points2, points2.length / 2);
+        OS.SelectObject (hDC, oldPen);
+        OS.DeleteObject (whitePen);
+        OS.DeleteObject (darkGrayPen);
+    } else {
+        OS.SelectObject (hDC, oldPen);
+    }
+    OS.DeleteObject (hPen);
+}
+
+void drawItem (GC gc, int hTheme, RECT clipRect, bool drawFocus) {
+    int hDC = gc.handle;
+    int headerHeight = parent.getBandHeight ();
+    RECT rect = new RECT ();
+    OS.SetRect (rect, x, y, x + width, y + headerHeight);
+    if (hTheme !is 0) {
+        OS.DrawThemeBackground (hTheme, hDC, OS.EBP_NORMALGROUPHEAD, 0, rect, clipRect);
+    } else {
+        int oldBrush = OS.SelectObject (hDC, OS.GetSysColorBrush (OS.COLOR_BTNFACE));
+        OS.PatBlt (hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
+        OS.SelectObject (hDC, oldBrush);
+    }
+    if (image !is null) {
+        rect.left += ExpandItem.TEXT_INSET;
+        if (imageHeight > headerHeight) {
+            gc.drawImage (image, rect.left, rect.top + headerHeight - imageHeight);
+        } else {
+            gc.drawImage (image, rect.left, rect.top + (headerHeight - imageHeight) / 2);
+        }
+        rect.left += imageWidth;
+    }
+    if (text.length () > 0) {
+        rect.left += ExpandItem.TEXT_INSET;
+        TCHAR buffer = new TCHAR (parent.getCodePage (), text, false);
+        if (hTheme !is 0) {
+            OS.DrawThemeText (hTheme, hDC, OS.EBP_NORMALGROUPHEAD, 0, buffer.chars, buffer.length(), OS.DT_VCENTER | OS.DT_SINGLELINE, 0, rect);
+        } else {
+            int oldBkMode = OS.SetBkMode (hDC, OS.TRANSPARENT);
+            OS.DrawText (hDC, buffer, buffer.length (), rect, OS.DT_VCENTER | OS.DT_SINGLELINE);
+            OS.SetBkMode (hDC, oldBkMode);
+        }
+    }
+    int chevronSize = ExpandItem.CHEVRON_SIZE;
+    rect.left = rect.right - chevronSize;
+    rect.top = y + (headerHeight - chevronSize) / 2;
+    rect.bottom = rect.top + chevronSize;
+    if (hTheme !is 0) {
+        int partID = expanded ? OS.EBP_NORMALGROUPCOLLAPSE : OS.EBP_NORMALGROUPEXPAND;
+        int stateID = hover ? OS.EBNGC_HOT : OS.EBNGC_NORMAL;
+        OS.DrawThemeBackground (hTheme, hDC, partID, stateID, rect, clipRect);
+    } else {
+        drawChevron (hDC, rect);
+    }
+    if (drawFocus) {
+        OS.SetRect (rect, x + 1, y + 1, x + width - 2, y + headerHeight - 2);
+        OS.DrawFocusRect (hDC, rect);
+    }
+    if (expanded) {
+        if (!parent.isAppThemed ()) {
+            int pen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor (OS.COLOR_BTNFACE));
+            int oldPen = OS.SelectObject (hDC, pen);
+            int [] points = {
+                    x, y + headerHeight,
+                    x, y + headerHeight + height,
+                    x + width - 1, y + headerHeight + height,
+                    x + width - 1, y + headerHeight - 1};
+            OS.Polyline (hDC, points, points.length / 2);
+            OS.SelectObject (hDC, oldPen);
+            OS.DeleteObject (pen);
+        }
+    }
+}
+
+void destroyWidget () {
+    parent.destroyItem (this);
+    releaseHandle ();
+}
+
+/**
+ * Returns the control that is shown when the item is expanded.
+ * If no control has been set, return <code>null</code>.
+ *
+ * @return the control
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Control getControl () {
+    checkWidget ();
+    return control;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is expanded,
+ * and false otherwise.
+ *
+ * @return the expanded state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getExpanded () {
+    checkWidget ();
+    return expanded;
+}
+
+/**
+ * Returns the height of the receiver's header
+ *
+ * @return the height of the header
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getHeaderHeight () {
+    checkWidget ();
+    return Math.max (parent.getBandHeight (), imageHeight);
+}
+
+/**
+ * Gets the height of the receiver.
+ *
+ * @return the height
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getHeight () {
+    checkWidget ();
+    return height;
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>ExpandBar</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public ExpandBar getParent () {
+    checkWidget ();
+    return parent;
+}
+
+int getPreferredWidth (int hTheme, int hDC) {
+    int width = ExpandItem.TEXT_INSET * 2 + ExpandItem.CHEVRON_SIZE;
+    if (image !is null) {
+        width += ExpandItem.TEXT_INSET + imageWidth;
+    }
+    if (text.length() > 0) {
+        RECT rect = new RECT ();
+        TCHAR buffer = new TCHAR (parent.getCodePage (), text, false);
+        if (hTheme !is 0) {
+            OS.GetThemeTextExtent (hTheme, hDC, OS.EBP_NORMALGROUPHEAD, 0, buffer.chars, buffer.length(), OS.DT_SINGLELINE, null, rect);
+        } else {
+            OS.DrawText (hDC, buffer, buffer.length (), rect, OS.DT_CALCRECT);
+        }
+        width += (rect.right - rect.left);
+    }
+    return width;
+}
+
+bool isHover (int x, int y) {
+    int bandHeight = parent.getBandHeight ();
+    return this.x < x && x < (this.x + width) && this.y < y && y < (this.y + bandHeight);
+}
+
+void redraw (bool all) {
+    int parentHandle = parent.handle;
+    int headerHeight = parent.getBandHeight ();
+    RECT rect = new RECT ();
+    int left = all ? x : x + width - headerHeight;
+    OS.SetRect (rect, left, y, x + width, y + headerHeight);
+    OS.InvalidateRect (parentHandle, rect, true);
+    if (imageHeight > headerHeight) {
+        OS.SetRect (rect, x + ExpandItem.TEXT_INSET, y + headerHeight - imageHeight, x + ExpandItem.TEXT_INSET + imageWidth, y);
+        OS.InvalidateRect (parentHandle, rect, true);
+    }
+    if (!parent.isAppThemed ()) {
+        OS.SetRect (rect, x, y + headerHeight, x + width, y + headerHeight + height + 1);
+        OS.InvalidateRect (parentHandle, rect, true);
+    }
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    control = null;
+}
+
+void setBounds (int x, int y, int width, int height, bool move, bool size) {
+    redraw (true);
+    int headerHeight = parent.getBandHeight ();
+    if (move) {
+        if (imageHeight > headerHeight) {
+            y += (imageHeight - headerHeight);
+        }
+        this.x = x;
+        this.y = y;
+        redraw (true);
+    }
+    if (size) {
+        this.width = width;
+        this.height = height;
+        redraw (true);
+    }
+    if (control !is null && !control.isDisposed ()) {
+        if (!parent.isAppThemed ()) {
+            x += BORDER;
+            width = Math.max (0, width - BORDER * 2);
+            height = Math.max (0, height - BORDER);
+        }
+        if (move && size) control.setBounds (x, y + headerHeight, width, height);
+        if (move && !size) control.setLocation (x, y + headerHeight);
+        if (!move && size) control.setSize (width, height);
+    }
+}
+
+/**
+ * Sets the control that is shown when the item is expanded.
+ *
+ * @param control the new control (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setControl (Control control) {
+    checkWidget ();
+    if (control !is null) {
+        if (control.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+        if (control.parent !is parent) error (DWT.ERROR_INVALID_PARENT);
+    }
+    this.control = control;
+    if (control !is null) {
+        int headerHeight = parent.getBandHeight ();
+        control.setVisible (expanded);
+        if (!parent.isAppThemed ()) {
+            int width = Math.max (0, this.width - BORDER * 2);
+            int height = Math.max (0, this.height - BORDER);
+            control.setBounds (x + BORDER, y + headerHeight, width, height);
+        } else {
+            control.setBounds (x, y + headerHeight, width, height);
+        }
+    }
+}
+
+/**
+ * Sets the expanded state of the receiver.
+ *
+ * @param expanded the new expanded state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setExpanded (bool expanded) {
+    checkWidget ();
+    this.expanded = expanded;
+    parent.showItem (this);
+}
+
+/**
+ * Sets the height of the receiver. This is height of the item when it is expanded,
+ * excluding the height of the header.
+ *
+ * @param height the new height
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setHeight (int height) {
+    checkWidget ();
+    if (height < 0) return;
+    setBounds (0, 0, width, height, false, true);
+    if (expanded) parent.layoutItems (parent.indexOf (this) + 1, true);
+}
+
+public void setImage (Image image) {
+    super.setImage (image);
+    int oldImageHeight = imageHeight;
+    if (image !is null) {
+        Rectangle bounds = image.getBounds ();
+        imageHeight = bounds.height;
+        imageWidth = bounds.width;
+    } else {
+        imageHeight = imageWidth = 0;
+    }
+    if (oldImageHeight !is imageHeight) {
+        parent.layoutItems (parent.indexOf (this), true);
+    } else {
+        redraw (true);
+    }
+}
+
+public void setText (String string) {
+    super.setText (string);
+    redraw (true);
+}
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/FileDialog.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,510 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.FileDialog;
+
+import dwt.widgets.Dialog;
+import dwt.widgets.Shell;
+
+class FileDialog : Dialog {
+    public this (Shell parent) {
+        this (parent, 0);
+    }
+    public this (Shell parent, int style) {
+        super (parent, style);
+    }
+
+}
+
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.internal.Callback;
+import dwt.internal.win32.OFNOTIFY;
+import dwt.internal.win32.OPENFILENAME;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class allow the user to navigate
+ * the file system and select or enter a file name.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SAVE, OPEN, MULTI</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles SAVE and OPEN may be specified.
+ * </p><p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+public class FileDialog extends Dialog {
+    String [] filterNames = new String [0];
+    String [] filterExtensions = new String [0];
+    String [] fileNames = new String [0];
+    String filterPath = "", fileName = "";
+    static final String FILTER = "*.*";
+    static int BUFFER_SIZE = 1024 * 32;
+    static bool USE_HOOK;
+
+/**
+ * Constructs a new instance of this class given only its parent.
+ *
+ * @param parent a shell which will be the parent of the new instance
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public FileDialog (Shell parent) {
+    this (parent, DWT.PRIMARY_MODAL);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a shell which will be the parent of the new instance
+ * @param style the style of dialog to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public FileDialog (Shell parent, int style) {
+    super (parent, style);
+    checkSubclass ();
+}
+
+/**
+ * Returns the path of the first file that was
+ * selected in the dialog relative to the filter path, or an
+ * empty string if no such file has been selected.
+ *
+ * @return the relative path of the file
+ */
+public String getFileName () {
+    return fileName;
+}
+
+/**
+ * Returns a (possibly empty) array with the paths of all files
+ * that were selected in the dialog relative to the filter path.
+ *
+ * @return the relative paths of the files
+ */
+public String [] getFileNames () {
+    return fileNames;
+}
+
+/**
+ * Returns the file extensions which the dialog will
+ * use to filter the files it shows.
+ *
+ * @return the file extensions filter
+ */
+public String [] getFilterExtensions () {
+    return filterExtensions;
+}
+
+/**
+ * Returns the names that describe the filter extensions
+ * which the dialog will use to filter the files it shows.
+ *
+ * @return the list of filter names
+ */
+public String [] getFilterNames () {
+    return filterNames;
+}
+
+/**
+ * Returns the directory path that the dialog will use, or an empty
+ * string if this is not set.  File names in this path will appear
+ * in the dialog, filtered according to the filter extensions.
+ *
+ * @return the directory path string
+ *
+ * @see #setFilterExtensions
+ */
+public String getFilterPath () {
+    return filterPath;
+}
+
+int OFNHookProc (int hdlg, int uiMsg, int wParam, int lParam) {
+    switch (uiMsg) {
+        case OS.WM_NOTIFY:
+            OFNOTIFY ofn = new OFNOTIFY ();
+            OS.MoveMemory (ofn, lParam, OFNOTIFY.sizeof);
+            if (ofn.code is OS.CDN_SELCHANGE) {
+                int lResult = OS.SendMessage (ofn.hwndFrom, OS.CDM_GETSPEC, 0, 0);
+                if (lResult > 0) {
+                    lResult += OS.MAX_PATH;
+                    OPENFILENAME lpofn = new OPENFILENAME ();
+                    OS.MoveMemory (lpofn, ofn.lpOFN, OPENFILENAME.sizeof);
+                    if (lpofn.nMaxFile < lResult) {
+                        int hHeap = OS.GetProcessHeap ();
+                        int lpstrFile = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, lResult * TCHAR.sizeof);
+                        if (lpstrFile !is 0) {
+                            if (lpofn.lpstrFile !is 0) OS.HeapFree (hHeap, 0, lpofn.lpstrFile);
+                            lpofn.lpstrFile = lpstrFile;
+                            lpofn.nMaxFile = lResult;
+                            OS.MoveMemory (ofn.lpOFN, lpofn, OPENFILENAME.sizeof);
+                        }
+                    }
+              }
+          }
+          break;
+    }
+    return 0;
+}
+
+/**
+ * Makes the dialog visible and brings it to the front
+ * of the display.
+ *
+ * @return a string describing the absolute path of the first selected file,
+ *         or null if the dialog was cancelled or an error occurred
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li>
+ * </ul>
+ */
+public String open () {
+    int hHeap = OS.GetProcessHeap ();
+
+    /* Get the owner HWND for the dialog */
+    int hwndOwner = 0;
+    if (parent !is null) hwndOwner = parent.handle;
+
+    /* Convert the title and copy it into lpstrTitle */
+    if (title is null) title = "";
+    /* Use the character encoding for the default locale */
+    TCHAR buffer3 = new TCHAR (0, title, true);
+    int byteCount3 = buffer3.length () * TCHAR.sizeof;
+    int lpstrTitle = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount3);
+    OS.MoveMemory (lpstrTitle, buffer3, byteCount3);
+
+    /* Compute filters and copy into lpstrFilter */
+    String strFilter = "";
+    if (filterNames is null) filterNames = new String [0];
+    if (filterExtensions is null) filterExtensions = new String [0];
+    for (int i=0; i<filterExtensions.length; i++) {
+        String filterName = filterExtensions [i];
+        if (i < filterNames.length) filterName = filterNames [i];
+        strFilter = strFilter + filterName + '\0' + filterExtensions [i] + '\0';
+    }
+    if (filterExtensions.length is 0) {
+        strFilter = strFilter + FILTER + '\0' + FILTER + '\0';
+    }
+    /* Use the character encoding for the default locale */
+    TCHAR buffer4 = new TCHAR (0, strFilter, true);
+    int byteCount4 = buffer4.length () * TCHAR.sizeof;
+    int lpstrFilter = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount4);
+    OS.MoveMemory (lpstrFilter, buffer4, byteCount4);
+
+    /* Convert the fileName and filterName to C strings */
+    if (fileName is null) fileName = "";
+    /* Use the character encoding for the default locale */
+    TCHAR name = new TCHAR (0, fileName, true);
+
+    /*
+    * Copy the name into lpstrFile and ensure that the
+    * last byte is NULL and the buffer does not overrun.
+    */
+    int nMaxFile = OS.MAX_PATH;
+    if ((style & DWT.MULTI) !is 0) nMaxFile = Math.max (nMaxFile, BUFFER_SIZE);
+    int byteCount = nMaxFile * TCHAR.sizeof;
+    int lpstrFile = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+    int byteCountFile = Math.min (name.length () * TCHAR.sizeof, byteCount - TCHAR.sizeof);
+    OS.MoveMemory (lpstrFile, name, byteCountFile);
+
+    /*
+    * Copy the path into lpstrInitialDir and ensure that
+    * the last byte is NULL and the buffer does not overrun.
+    */
+    if (filterPath is null) filterPath = "";
+    /* Use the character encoding for the default locale */
+    TCHAR path = new TCHAR (0, filterPath.replace ('/', '\\'), true);
+    int byteCount5 = OS.MAX_PATH * TCHAR.sizeof;
+    int lpstrInitialDir = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount5);
+    int byteCountDir = Math.min (path.length () * TCHAR.sizeof, byteCount5 - TCHAR.sizeof);
+    OS.MoveMemory (lpstrInitialDir, path, byteCountDir);
+
+    /* Create the file dialog struct */
+    OPENFILENAME struct = new OPENFILENAME ();
+    struct.lStructSize = OPENFILENAME.sizeof;
+    struct.Flags = OS.OFN_HIDEREADONLY | OS.OFN_NOCHANGEDIR;
+    Callback callback = null;
+    if ((style & DWT.MULTI) !is 0) {
+        struct.Flags |= OS.OFN_ALLOWMULTISELECT | OS.OFN_EXPLORER;
+        if (!OS.IsWinCE && USE_HOOK) {
+            callback = new Callback (this, "OFNHookProc", 4); //$NON-NLS-1$
+            int lpfnHook = callback.getAddress ();
+            if (lpfnHook is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS);
+            struct.lpfnHook = lpfnHook;
+            struct.Flags |= OS.OFN_ENABLEHOOK;
+        }
+    }
+    struct.hwndOwner = hwndOwner;
+    struct.lpstrTitle = lpstrTitle;
+    struct.lpstrFile = lpstrFile;
+    struct.nMaxFile = nMaxFile;
+    struct.lpstrInitialDir = lpstrInitialDir;
+    struct.lpstrFilter = lpstrFilter;
+    struct.nFilterIndex = 0;
+
+    /*
+    * Set the default extension to an empty string.  If the
+    * user fails to type an extension and this extension is
+    * empty, Windows uses the current value of the filter
+    * extension at the time that the dialog is closed.
+    */
+    int lpstrDefExt = 0;
+    bool save = (style & DWT.SAVE) !is 0;
+    if (save) {
+        lpstrDefExt = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
+        struct.lpstrDefExt = lpstrDefExt;
+    }
+
+    /* Make the parent shell be temporary modal */
+    Shell oldModal = null;
+    Display display = parent.getDisplay ();
+    if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) !is 0) {
+        oldModal = display.getModalDialogShell ();
+        display.setModalDialogShell (parent);
+    }
+
+    /*
+    * Feature in Windows.  For some reason, the WH_MSGFILTER filter
+    * does not run for GetSaveFileName() or GetOpenFileName().  The
+    * fix is to allow async messages to run in the WH_FOREGROUNDIDLE
+    * hook instead.
+    *
+    * Bug in Windows 98.  For some reason, when certain operating
+    * system calls such as Shell_NotifyIcon(), GetOpenFileName()
+    * and GetSaveFileName() are made during the WH_FOREGROUNDIDLE
+    * hook, Windows hangs.  The fix is to disallow async messages
+    * during WH_FOREGROUNDIDLE.
+    */
+    bool oldRunMessagesInIdle = display.runMessagesInIdle;
+    display.runMessagesInIdle = !OS.IsWin95;
+    /*
+    * Open the dialog.  If the open fails due to an invalid
+    * file name, use an empty file name and open it again.
+    */
+    bool success = (save) ? OS.GetSaveFileName (struct) : OS.GetOpenFileName (struct);
+    switch (OS.CommDlgExtendedError ()) {
+        case OS.FNERR_INVALIDFILENAME:
+            OS.MoveMemory (lpstrFile, new TCHAR (0, "", true), TCHAR.sizeof);
+            success = (save) ? OS.GetSaveFileName (struct) : OS.GetOpenFileName (struct);
+            break;
+        case OS.FNERR_BUFFERTOOSMALL:
+            USE_HOOK = true;
+            break;
+    }
+    display.runMessagesInIdle = oldRunMessagesInIdle;
+
+    /* Clear the temporary dialog modal parent */
+    if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) !is 0) {
+        display.setModalDialogShell (oldModal);
+    }
+
+    /* Dispose the callback and reassign the buffer */
+    if (callback !is null) callback.dispose ();
+    lpstrFile = struct.lpstrFile;
+
+    /* Set the new path, file name and filter */
+    fileNames = new String [0];
+    String fullPath = null;
+    if (success) {
+
+        /* Use the character encoding for the default locale */
+        TCHAR buffer = new TCHAR (0, struct.nMaxFile);
+        int byteCount1 = buffer.length () * TCHAR.sizeof;
+        OS.MoveMemory (buffer, lpstrFile, byteCount1);
+
+        /*
+        * Bug in WinCE.  For some reason, nFileOffset and nFileExtension
+        * are always zero on WinCE HPC. nFileOffset is always zero on
+        * WinCE PPC when using GetSaveFileName().  nFileOffset is correctly
+        * set on WinCE PPC when using OpenFileName().  The fix is to parse
+        * lpstrFile to calculate nFileOffset.
+        *
+        * Note: WinCE does not support multi-select file dialogs.
+        */
+        int nFileOffset = struct.nFileOffset;
+        if (OS.IsWinCE && nFileOffset is 0) {
+            int index = 0;
+            while (index < buffer.length ()) {
+                int ch = buffer.tcharAt (index);
+                if (ch is 0) break;
+                if (ch is '\\') nFileOffset = index + 1;
+                index++;
+            }
+        }
+        if (nFileOffset > 0) {
+
+            /* Use the character encoding for the default locale */
+            TCHAR prefix = new TCHAR (0, nFileOffset - 1);
+            int byteCount2 = prefix.length () * TCHAR.sizeof;
+            OS.MoveMemory (prefix, lpstrFile, byteCount2);
+            filterPath = prefix.toString (0, prefix.length ());
+
+            /*
+            * Get each file from the buffer.  Files are delimited
+            * by a NULL character with 2 NULL characters at the end.
+            */
+            int count = 0;
+            fileNames = new String [(style & DWT.MULTI) !is 0 ? 4 : 1];
+            int start = nFileOffset;
+            do {
+                int end = start;
+                while (end < buffer.length () && buffer.tcharAt (end) !is 0) end++;
+                String string = buffer.toString (start, end - start);
+                start = end;
+                if (count is fileNames.length) {
+                    String [] newFileNames = new String [fileNames.length + 4];
+                    System.arraycopy (fileNames, 0, newFileNames, 0, fileNames.length);
+                    fileNames = newFileNames;
+                }
+                fileNames [count++] = string;
+                if ((style & DWT.MULTI) is 0) break;
+                start++;
+            } while (start < buffer.length () && buffer.tcharAt (start) !is 0);
+
+            if (fileNames.length > 0) fileName = fileNames  [0];
+            String separator = "";
+            int length = filterPath.length ();
+            if (length > 0 && filterPath.charAt (length - 1) !is '\\') {
+                separator = "\\";
+            }
+            fullPath = filterPath + separator + fileName;
+            if (count < fileNames.length) {
+                String [] newFileNames = new String [count];
+                System.arraycopy (fileNames, 0, newFileNames, 0, count);
+                fileNames = newFileNames;
+            }
+        }
+    }
+
+    /* Free the memory that was allocated. */
+    OS.HeapFree (hHeap, 0, lpstrFile);
+    OS.HeapFree (hHeap, 0, lpstrFilter);
+    OS.HeapFree (hHeap, 0, lpstrInitialDir);
+    OS.HeapFree (hHeap, 0, lpstrTitle);
+    if (lpstrDefExt !is 0) OS.HeapFree (hHeap, 0, lpstrDefExt);
+
+    /*
+    * This code is intentionally commented.  On some
+    * platforms, the owner window is repainted right
+    * away when a dialog window exits.  This behavior
+    * is currently unspecified.
+    */
+//  if (hwndOwner !is 0) OS.UpdateWindow (hwndOwner);
+
+    /* Answer the full path or null */
+    return fullPath;
+}
+
+/**
+ * Set the initial filename which the dialog will
+ * select by default when opened to the argument,
+ * which may be null.  The name will be prefixed with
+ * the filter path when one is supplied.
+ *
+ * @param string the file name
+ */
+public void setFileName (String string) {
+    fileName = string;
+}
+
+/**
+ * Set the file extensions which the dialog will
+ * use to filter the files it shows to the argument,
+ * which may be null.
+ * <p>
+ * The strings are platform specific. For example, on
+ * Windows, an extension filter string is typically of
+ * the form "*.extension", where "*.*" matches all files.
+ * </p>
+ *
+ * @param extensions the file extension filter
+ *
+ * @see #setFilterNames to specify the user-friendly
+ * names corresponding to the extensions
+ */
+public void setFilterExtensions (String [] extensions) {
+    filterExtensions = extensions;
+}
+
+/**
+ * Sets the the names that describe the filter extensions
+ * which the dialog will use to filter the files it shows
+ * to the argument, which may be null.
+ * <p>
+ * Each name is a user-friendly short description shown for
+ * its corresponding filter. The <code>names</code> array must
+ * be the same length as the <code>extensions</code> array.
+ * </p>
+ *
+ * @param names the list of filter names, or null for no filter names
+ *
+ * @see #setFilterExtensions
+ */
+public void setFilterNames (String [] names) {
+    filterNames = names;
+}
+
+/**
+ * Sets the directory path that the dialog will use
+ * to the argument, which may be null. File names in this
+ * path will appear in the dialog, filtered according
+ * to the filter extensions. If the string is null,
+ * then the operating system's default filter path
+ * will be used.
+ * <p>
+ * Note that the path string is platform dependent.
+ * For convenience, either '/' or '\' can be used
+ * as a path separator.
+ * </p>
+ *
+ * @param string the directory path
+ *
+ * @see #setFilterExtensions
+ */
+public void setFilterPath (String string) {
+    filterPath = string;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/FontDialog.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,311 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.FontDialog;
+
+import dwt.widgets.Dialog;
+import dwt.widgets.Shell;
+
+class FontDialog : Dialog {
+    public this (Shell parent) {
+        this (parent, 0);
+    }
+    public this (Shell parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Font;
+import dwt.graphics.FontData;
+import dwt.graphics.PaletteData;
+import dwt.graphics.RGB;
+import dwt.internal.win32.CHOOSEFONT;
+import dwt.internal.win32.LOGFONT;
+import dwt.internal.win32.LOGFONTA;
+import dwt.internal.win32.LOGFONTW;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+
+/**
+ * Instances of this class allow the user to select a font
+ * from all available fonts in the system.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+public class FontDialog extends Dialog {
+    FontData fontData;
+    RGB rgb;
+
+/**
+ * Constructs a new instance of this class given only its parent.
+ *
+ * @param parent a shell which will be the parent of the new instance
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public FontDialog (Shell parent) {
+    this (parent, DWT.PRIMARY_MODAL);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a shell which will be the parent of the new instance
+ * @param style the style of dialog to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public FontDialog (Shell parent, int style) {
+    super (parent, style);
+    checkSubclass ();
+}
+
+/**
+ * Returns a FontData object describing the font that was
+ * selected in the dialog, or null if none is available.
+ *
+ * @return the FontData for the selected font, or null
+ * @deprecated use #getFontList ()
+ */
+public FontData getFontData () {
+    return fontData;
+}
+
+/**
+ * Returns a FontData set describing the font that was
+ * selected in the dialog, or null if none is available.
+ *
+ * @return the FontData for the selected font, or null
+ * @since 2.1.1
+ */
+public FontData [] getFontList () {
+    if (fontData is null) return null;
+    FontData [] result = new FontData [1];
+    result [0] = fontData;
+    return result;
+}
+
+/**
+ * Returns an RGB describing the color that was selected
+ * in the dialog, or null if none is available.
+ *
+ * @return the RGB value for the selected color, or null
+ *
+ * @see PaletteData#getRGBs
+ *
+ * @since 2.1
+ */
+public RGB getRGB () {
+    return rgb;
+}
+
+/**
+ * Makes the dialog visible and brings it to the front
+ * of the display.
+ *
+ * @return a FontData object describing the font that was selected,
+ *         or null if the dialog was cancelled or an error occurred
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li>
+ * </ul>
+ */
+public FontData open () {
+    if (OS.IsWinCE) DWT.error (DWT.ERROR_NOT_IMPLEMENTED);
+
+    /* Get the owner HWND for the dialog */
+    int hwndOwner = 0;
+    if (parent !is null) hwndOwner = parent.handle;
+
+    /* Open the dialog */
+    int hHeap = OS.GetProcessHeap ();
+    CHOOSEFONT lpcf = new CHOOSEFONT ();
+    lpcf.lStructSize = CHOOSEFONT.sizeof;
+    lpcf.hwndOwner = hwndOwner;
+    lpcf.Flags = OS.CF_SCREENFONTS | OS.CF_EFFECTS;
+    int lpLogFont = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, LOGFONT.sizeof);
+    if (fontData !is null && fontData.data !is null) {
+        LOGFONT logFont = fontData.data;
+        int lfHeight = logFont.lfHeight;
+        int hDC = OS.GetDC (0);
+        int pixels = -(int)(0.5f + (fontData.height * OS.GetDeviceCaps(hDC, OS.LOGPIXELSY) / 72));
+        OS.ReleaseDC (0, hDC);
+        logFont.lfHeight = pixels;
+        lpcf.Flags |= OS.CF_INITTOLOGFONTSTRUCT;
+        OS.MoveMemory (lpLogFont, logFont, LOGFONT.sizeof);
+        logFont.lfHeight = lfHeight;
+    }
+    lpcf.lpLogFont = lpLogFont;
+    if (rgb !is null) {
+        int red = rgb.red & 0xFF;
+        int green = (rgb.green << 8) & 0xFF00;
+        int blue = (rgb.blue << 16) & 0xFF0000;
+        lpcf.rgbColors = red | green | blue;
+    }
+
+    /* Make the parent shell be temporary modal */
+    Shell oldModal = null;
+    Display display = null;
+    if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) !is 0) {
+        display = parent.getDisplay ();
+        oldModal = display.getModalDialogShell ();
+        display.setModalDialogShell (parent);
+    }
+
+    /* Open the dialog */
+    bool success = OS.ChooseFont (lpcf);
+
+    /* Clear the temporary dialog modal parent */
+    if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) !is 0) {
+        display.setModalDialogShell (oldModal);
+    }
+
+    /* Compute the result */
+    if (success) {
+        LOGFONT logFont = OS.IsUnicode ? (LOGFONT) new LOGFONTW () : new LOGFONTA ();
+        OS.MoveMemory (logFont, lpLogFont, LOGFONT.sizeof);
+
+        /*
+         * This will not work on multiple screens or
+         * for printing. Should use DC for the proper device.
+         */
+        int hDC = OS.GetDC(0);
+        int logPixelsY = OS.GetDeviceCaps(hDC, OS.LOGPIXELSY);
+        int pixels = 0;
+        if (logFont.lfHeight > 0) {
+            /*
+             * Feature in Windows. If the lfHeight of the LOGFONT structure
+             * is positive, the lfHeight measures the height of the entire
+             * cell, including internal leading, in logical units. Since the
+             * height of a font in points does not include the internal leading,
+             * we must subtract the internal leading, which requires a TEXTMETRIC,
+             * which in turn requires font creation.
+             */
+            int hFont = OS.CreateFontIndirect(logFont);
+            int oldFont = OS.SelectObject(hDC, hFont);
+            TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+            OS.GetTextMetrics(hDC, lptm);
+            OS.SelectObject(hDC, oldFont);
+            OS.DeleteObject(hFont);
+            pixels = logFont.lfHeight - lptm.tmInternalLeading;
+        } else {
+            pixels = -logFont.lfHeight;
+        }
+        OS.ReleaseDC(0, hDC);
+
+        float points = pixels * 72f /logPixelsY;
+        fontData = FontData.win32_new (logFont, points);
+        int red = lpcf.rgbColors & 0xFF;
+        int green = (lpcf.rgbColors >> 8) & 0xFF;
+        int blue = (lpcf.rgbColors >> 16) & 0xFF;
+        rgb = new RGB (red, green, blue);
+    }
+
+    /* Free the OS memory */
+    if (lpLogFont !is 0) OS.HeapFree (hHeap, 0, lpLogFont);
+
+    /*
+    * This code is intentionally commented.  On some
+    * platforms, the owner window is repainted right
+    * away when a dialog window exits.  This behavior
+    * is currently unspecified.
+    */
+//  if (hwndOwner !is 0) OS.UpdateWindow (hwndOwner);
+
+    if (!success) return null;
+    return fontData;
+}
+
+/**
+ * Sets a FontData object describing the font to be
+ * selected by default in the dialog, or null to let
+ * the platform choose one.
+ *
+ * @param fontData the FontData to use initially, or null
+ * @deprecated use #setFontList (FontData [])
+ */
+public void setFontData (FontData fontData) {
+    this.fontData = fontData;
+}
+
+/**
+ * Sets the set of FontData objects describing the font to
+ * be selected by default in the dialog, or null to let
+ * the platform choose one.
+ *
+ * @param fontData the set of FontData objects to use initially, or null
+ *        to let the platform select a default when open() is called
+ *
+ * @see Font#getFontData
+ *
+ * @since 2.1.1
+ */
+public void setFontList (FontData [] fontData) {
+    if (fontData !is null && fontData.length > 0) {
+        this.fontData = fontData [0];
+    } else {
+        this.fontData = null;
+    }
+}
+
+/**
+ * Sets the RGB describing the color to be selected by default
+ * in the dialog, or null to let the platform choose one.
+ *
+ * @param rgb the RGB value to use initially, or null to let
+ *        the platform select a default when open() is called
+ *
+ * @see PaletteData#getRGBs
+ *
+ * @since 2.1
+ */
+public void setRGB (RGB rgb) {
+    this.rgb = rgb;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Group.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,505 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Group;
+
+import dwt.widgets.Composite;
+class Group : Composite {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Font;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+import dwt.internal.win32.WINDOWPOS;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class provide an etched border
+ * with an optional title.
+ * <p>
+ * Shadow styles are hints and may not be honoured
+ * by the platform.  To create a group with the
+ * default shadow style for the platform, do not
+ * specify a shadow style.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SHADOW_ETCHED_IN, SHADOW_ETCHED_OUT, SHADOW_IN, SHADOW_OUT, SHADOW_NONE</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the above styles may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class Group extends Composite {
+    String text = "";
+    static final int CLIENT_INSET = 3;
+    static final int GroupProc;
+    static final TCHAR GroupClass = new TCHAR (0, OS.IsWinCE ? "BUTTON" : "SWT_GROUP", true);
+    static {
+        /*
+        * Feature in Windows.  The group box window class
+        * uses the CS_HREDRAW and CS_VREDRAW style bits to
+        * force a full redraw of the control and all children
+        * when resized.  This causes flashing.  The fix is to
+        * register a new window class without these bits and
+        * implement special code that damages only the control.
+        *
+        * Feature in WinCE.  On certain devices, defining
+        * a new window class which looks like BUTTON causes
+        * CreateWindowEx() to crash.  The workaround is to use
+        * the class Button directly.
+        */
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        if (OS.IsWinCE) {
+            OS.GetClassInfo (0, GroupClass, lpWndClass);
+            GroupProc = lpWndClass.lpfnWndProc;
+        } else {
+            TCHAR WC_BUTTON = new TCHAR (0, "BUTTON", true);
+            OS.GetClassInfo (0, WC_BUTTON, lpWndClass);
+            GroupProc = lpWndClass.lpfnWndProc;
+            int hInstance = OS.GetModuleHandle (null);
+            if (!OS.GetClassInfo (hInstance, GroupClass, lpWndClass)) {
+                int hHeap = OS.GetProcessHeap ();
+                lpWndClass.hInstance = hInstance;
+                lpWndClass.style &= ~(OS.CS_HREDRAW | OS.CS_VREDRAW);
+                int byteCount = GroupClass.length () * TCHAR.sizeof;
+                int lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+                OS.MoveMemory (lpszClassName, GroupClass, byteCount);
+                lpWndClass.lpszClassName = lpszClassName;
+                OS.RegisterClass (lpWndClass);
+                OS.HeapFree (hHeap, 0, lpszClassName);
+            }
+        }
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#SHADOW_ETCHED_IN
+ * @see DWT#SHADOW_ETCHED_OUT
+ * @see DWT#SHADOW_IN
+ * @see DWT#SHADOW_OUT
+ * @see DWT#SHADOW_NONE
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Group (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    /*
+    * Feature in Windows.  When the user clicks on the group
+    * box label, the group box takes focus.  This is unwanted.
+    * The fix is to avoid calling the group box window proc.
+    */
+    switch (msg) {
+        case OS.WM_LBUTTONDOWN:
+        case OS.WM_LBUTTONDBLCLK:
+            return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+    }
+    return OS.CallWindowProc (GroupProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    style |= DWT.NO_FOCUS;
+    /*
+    * Even though it is legal to create this widget
+    * with scroll bars, they serve no useful purpose
+    * because they do not automatically scroll the
+    * widget's client area.  The fix is to clear
+    * the DWT style.
+    */
+    return style & ~(DWT.H_SCROLL | DWT.V_SCROLL);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    Point size = super.computeSize (wHint, hHint, changed);
+    int length = text.length ();
+    if (length !is 0) {
+        /*
+        * Bug in Windows.  When a group control is right-to-left and
+        * is disabled, the first pixel of the text is clipped.  The
+        * fix is to add a space to both sides of the text.  Note that
+        * the work around must run all the time to stop the preferred
+        * size from changing when a group is enabled and disabled.
+        */
+        String string = text;
+        if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+            if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+                string = " " + string + " ";
+            }
+        }
+        /*
+        * If the group has text, and the text is wider than the
+        * client area, pad the width so the text is not clipped.
+        */
+        TCHAR buffer = new TCHAR (getCodePage (), string, true);
+        int newFont, oldFont = 0;
+        int hDC = OS.GetDC (handle);
+        newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+        RECT rect = new RECT ();
+        int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE;
+        OS.DrawText (hDC, buffer, -1, rect, flags);
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+        size.x = Math.max (size.x, rect.right - rect.left + CLIENT_INSET * 6);
+    }
+    return size;
+}
+
+public Rectangle computeTrim (int x, int y, int width, int height) {
+    checkWidget ();
+    Rectangle trim = super.computeTrim (x, y, width, height);
+    int newFont, oldFont = 0;
+    int hDC = OS.GetDC (handle);
+    newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+    OS.GetTextMetrics (hDC, tm);
+    if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+    OS.ReleaseDC (handle, hDC);
+    trim.x -= CLIENT_INSET;
+    trim.y -= tm.tmHeight;
+    trim.width += CLIENT_INSET * 2;
+    trim.height += tm.tmHeight + CLIENT_INSET;
+    return trim;
+}
+
+void createHandle () {
+    super.createHandle ();
+    state |= DRAW_BACKGROUND;
+    state &= ~CANVAS;
+}
+
+void enableWidget (bool enabled) {
+    super.enableWidget (enabled);
+    /*
+    * Bug in Windows.  When a group control is right-to-left and
+    * is disabled, the first pixel of the text is clipped.  The
+    * fix is to add a space to both sides of the text.
+    */
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+        if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+            String string = enabled || text.length() is 0 ? text : " " + text + " ";
+            TCHAR buffer = new TCHAR (getCodePage (), string, true);
+            OS.SetWindowText (handle, buffer);
+        }
+    }
+}
+
+public Rectangle getClientArea () {
+    checkWidget ();
+    forceResize ();
+    RECT rect = new RECT ();
+    OS.GetClientRect (handle, rect);
+    int newFont, oldFont = 0;
+    int hDC = OS.GetDC (handle);
+    newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+    OS.GetTextMetrics (hDC, tm);
+    if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+    OS.ReleaseDC (handle, hDC);
+    int x = CLIENT_INSET, y = tm.tmHeight;
+    int width = Math.max (0, rect.right - CLIENT_INSET * 2);
+    int height = Math.max (0, rect.bottom - y - CLIENT_INSET);
+    return new Rectangle (x, y, width, height);
+}
+
+String getNameText () {
+    return getText ();
+}
+
+/**
+ * Returns the receiver's text, which is the string that the
+ * is used as the <em>title</em>. If the text has not previously
+ * been set, returns an empty string.
+ *
+ * @return the text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText () {
+    checkWidget ();
+    return text;
+}
+
+bool mnemonicHit (char key) {
+    return setFocus ();
+}
+
+bool mnemonicMatch (char key) {
+    char mnemonic = findMnemonic (getText ());
+    if (mnemonic is '\0') return false;
+    return Character.toUpperCase (key) is Character.toUpperCase (mnemonic);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    text = null;
+}
+
+public void setFont (Font font) {
+    checkWidget ();
+    Rectangle oldRect = getClientArea ();
+    super.setFont (font);
+    Rectangle newRect = getClientArea ();
+    if (!oldRect.equals (newRect)) sendResize ();
+}
+
+/**
+ * Sets the receiver's text, which is the string that will
+ * be displayed as the receiver's <em>title</em>, to the argument,
+ * which may not be null. The string may include the mnemonic character.
+ * </p>
+ * Mnemonics are indicated by an '&amp;' that causes the next
+ * character to be the mnemonic.  When the user presses a
+ * key sequence that matches the mnemonic, focus is assigned
+ * to the first child of the group. On most platforms, the
+ * mnemonic appears underlined but may be emphasised in a
+ * platform specific manner.  The mnemonic indicator character
+ * '&amp;' can be escaped by doubling it in the string, causing
+ * a single '&amp;' to be displayed.
+ * </p>
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    text = string;
+    /*
+    * Bug in Windows.  When a group control is right-to-left and
+    * is disabled, the first pixel of the text is clipped.  The
+    * fix is to add a space to both sides of the text.
+    */
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+        if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+            if (!OS.IsWindowEnabled (handle)) {
+                if (string.length() !is 0) string = " " + string + " ";
+            }
+        }
+    }
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    OS.SetWindowText (handle, buffer);
+}
+
+int widgetStyle () {
+    /*
+    * Bug in Windows.  When GetDCEx() is called with DCX_INTERSECTUPDATE,
+    * the HDC that is returned does not include the current update region.
+    * This was confirmed under DEBUG Windows when GetDCEx() complained about
+    * invalid flags.  Therefore, it is not easily possible to get an HDC from
+    * outside of WM_PAINT that includes the current damage and clips children.
+    * Because the receiver has children and draws a frame and label, it is
+    * necessary that the receiver always draw clipped, in the current damaged
+    * area.  The fix is to force the receiver to be fully clipped by including
+    * WS_CLIPCHILDREN and WS_CLIPSIBLINGS in the default style bits.
+    */
+    return super.widgetStyle () | OS.BS_GROUPBOX | OS.WS_CLIPCHILDREN | OS.WS_CLIPSIBLINGS;
+}
+
+TCHAR windowClass () {
+    return GroupClass;
+}
+
+int windowProc () {
+    return GroupProc;
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  Group boxes do not erase
+    * the background before drawing.  The fix is to
+    * fill the background.
+    */
+    drawBackground (wParam);
+    return LRESULT.ONE;
+}
+
+LRESULT WM_NCHITTEST (int wParam, int lParam) {
+    LRESULT result = super.WM_NCHITTEST (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  The window proc for the group box
+    * returns HTTRANSPARENT indicating that mouse messages
+    * should not be delivered to the receiver and any children.
+    * Normally, group boxes in Windows do not have children and
+    * this is the correct behavior for this case.  Because we
+    * allow children, answer HTCLIENT to allow mouse messages
+    * to be delivered to the children.
+    */
+    int code = callWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam);
+    if (code is OS.HTTRANSPARENT) code = OS.HTCLIENT;
+    return new LRESULT (code);
+}
+
+LRESULT WM_MOUSEMOVE (int wParam, int lParam) {
+    LRESULT result = super.WM_MOUSEMOVE (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  In version 6.00 of COMCTL32.DLL,
+    * every time the mouse moves, the group title redraws.
+    * This only happens when WM_NCHITTEST returns HTCLIENT.
+    * The fix is to avoid calling the group window proc.
+    */
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_PRINTCLIENT (int wParam, int lParam) {
+    LRESULT result = super.WM_PRINTCLIENT (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  In version 6.00 of COMCTL32.DLL,
+    * when WM_PRINTCLIENT is sent from a child BS_GROUP
+    * control to a parent BS_GROUP, the parent BS_GROUP
+    * clears the font from the HDC.  Normally, group boxes
+    * in Windows do not have children so this behavior is
+    * undefined.  When the parent of a BS_GROUP is not a
+    * BS_GROUP, there is no problem.  The fix is to save
+    * and restore the current font.
+    */
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        int nSavedDC = OS.SaveDC (wParam);
+        int code = callWindowProc (handle, OS.WM_PRINTCLIENT, wParam, lParam);
+        OS.RestoreDC (wParam, nSavedDC);
+        return new LRESULT (code);
+    }
+    return result;
+}
+
+LRESULT WM_UPDATEUISTATE (int wParam, int lParam) {
+    LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  When WM_UPDATEUISTATE is sent to
+    * a group, it sends WM_CTLCOLORBTN to get the foreground
+    * and background.  If drawing happens in WM_CTLCOLORBTN,
+    * it will overwrite the contents of the control.  The
+    * fix is draw the group without drawing the background
+    * and avoid the group window proc.
+    */
+    bool redraw = findImageControl () !is null;
+    if (!redraw) {
+        if ((state & THEME_BACKGROUND) !is 0) {
+            if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                redraw = findThemeControl () !is null;
+            }
+        }
+        if (!redraw) redraw = findBackgroundControl () !is null;
+    }
+    if (redraw) {
+        OS.InvalidateRect (handle, null, false);
+        int code = OS.DefWindowProc (handle, OS.WM_UPDATEUISTATE, wParam, lParam);
+        return new LRESULT (code);
+    }
+    return result;
+}
+
+LRESULT WM_WINDOWPOSCHANGING (int wParam, int lParam) {
+    LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Invalidate the portion of the group widget that needs to
+    * be redrawn.  Note that for some reason, invalidating the
+    * group from inside WM_SIZE causes pixel corruption for
+    * radio button children.
+    */
+    if (OS.IsWinCE) return result;
+    if (!OS.IsWindowVisible (handle)) return result;
+    WINDOWPOS lpwp = new WINDOWPOS ();
+    OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
+    if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) !is 0) {
+        return result;
+    }
+    RECT rect = new RECT ();
+    OS.SetRect (rect, 0, 0, lpwp.cx, lpwp.cy);
+    OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, rect);
+    int newWidth = rect.right - rect.left;
+    int newHeight = rect.bottom - rect.top;
+    OS.GetClientRect (handle, rect);
+    int oldWidth = rect.right - rect.left;
+    int oldHeight = rect.bottom - rect.top;
+    if (newWidth is oldWidth && newHeight is oldHeight) {
+        return result;
+    }
+    if (newWidth !is oldWidth) {
+        int left = oldWidth;
+        if (newWidth < oldWidth) left = newWidth;
+        OS.SetRect (rect, left - CLIENT_INSET, 0, newWidth, newHeight);
+        OS.InvalidateRect (handle, rect, true);
+    }
+    if (newHeight !is oldHeight) {
+        int bottom = oldHeight;
+        if (newHeight < oldHeight) bottom = newHeight;
+        if (newWidth < oldWidth) oldWidth -= CLIENT_INSET;
+        OS.SetRect (rect, 0, bottom - CLIENT_INSET, oldWidth, newHeight);
+        OS.InvalidateRect (handle, rect, true);
+    }
+    return result;
+}
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Item.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Item;
+
+
+import dwt.widgets.Widget;
+import dwt.DWT;
+import dwt.graphics.Image;
+
+/**
+ * This class is the abstract superclass of all non-windowed
+ * user interface objects that occur within specific controls.
+ * For example, a tree will contain tree items.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ */
+
+public abstract class Item : Widget {
+    char[] text;
+    Image image;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * The item is added to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of item to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#getStyle
+ */
+public this (Widget parent, int style) {
+    super (parent, style);
+    text = "";
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance,
+ * and the index at which to place it in the items maintained
+ * by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of item to construct
+ * @param index the zero-relative index at which to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#getStyle
+ */
+public this (Widget parent, int style, int index) {
+    this (parent, style);
+}
+
+protected void checkSubclass () {
+    /* Do Nothing - Subclassing is allowed */
+}
+
+/**
+ * Returns the receiver's image if it has one, or null
+ * if it does not.
+ *
+ * @return the receiver's image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getImage () {
+    checkWidget ();
+    return image;
+}
+
+char[] getNameText () {
+    return getText ();
+}
+
+/**
+ * Returns the receiver's text, which will be an empty
+ * string if it has never been set.
+ *
+ * @return the receiver's text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public char[] getText () {
+    checkWidget();
+    return text;
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    text = null;
+    image = null;
+}
+
+/**
+ * Sets the receiver's image to the argument, which may be
+ * null indicating that no image should be displayed.
+ *
+ * @param image the image to display on the receiver (may be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (Image image) {
+    checkWidget ();
+    if (image !is null && image.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    this.image = image;
+}
+
+/**
+ * Sets the receiver's text.
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (char[] string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    text = string;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Label.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,696 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Label;
+
+import dwt.widgets.Control;
+import dwt.widgets.Composite;
+class Label : Control {
+    this (Composite parent, int style) ;
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.DRAWITEMSTRUCT;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.PAINTSTRUCT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class represent a non-selectable
+ * user interface object that displays a string or image.
+ * When SEPARATOR is specified, displays a single
+ * vertical or horizontal line.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SEPARATOR, HORIZONTAL, VERTICAL</dd>
+ * <dd>SHADOW_IN, SHADOW_OUT, SHADOW_NONE</dd>
+ * <dd>CENTER, LEFT, RIGHT, WRAP</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of SHADOW_IN, SHADOW_OUT and SHADOW_NONE may be specified.
+ * SHADOW_NONE is a HINT. Only one of HORIZONTAL and VERTICAL may be specified.
+ * Only one of CENTER, LEFT and RIGHT may be specified.
+ * </p><p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+public class Label extends Control {
+    String text = "";
+    Image image;
+    static final int MARGIN = 4;
+    static final bool IMAGE_AND_TEXT = false;
+    static final int LabelProc;
+    static final TCHAR LabelClass = new TCHAR (0, "STATIC", true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, LabelClass, lpWndClass);
+        LabelProc = lpWndClass.lpfnWndProc;
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#SEPARATOR
+ * @see DWT#HORIZONTAL
+ * @see DWT#VERTICAL
+ * @see DWT#SHADOW_IN
+ * @see DWT#SHADOW_OUT
+ * @see DWT#SHADOW_NONE
+ * @see DWT#CENTER
+ * @see DWT#LEFT
+ * @see DWT#RIGHT
+ * @see DWT#WRAP
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Label (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.CallWindowProc (LabelProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    style |= DWT.NO_FOCUS;
+    if ((style & DWT.SEPARATOR) !is 0) {
+        style = checkBits (style, DWT.VERTICAL, DWT.HORIZONTAL, 0, 0, 0, 0);
+        return checkBits (style, DWT.SHADOW_OUT, DWT.SHADOW_IN, DWT.SHADOW_NONE, 0, 0, 0);
+    }
+    return checkBits (style, DWT.LEFT, DWT.CENTER, DWT.RIGHT, 0, 0, 0);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = 0, height = 0, border = getBorderWidth ();
+    if ((style & DWT.SEPARATOR) !is 0) {
+        int lineWidth = OS.GetSystemMetrics (OS.SM_CXBORDER);
+        if ((style & DWT.HORIZONTAL) !is 0) {
+            width = DEFAULT_WIDTH;  height = lineWidth * 2;
+        } else {
+            width = lineWidth * 2; height = DEFAULT_HEIGHT;
+        }
+        if (wHint !is DWT.DEFAULT) width = wHint;
+        if (hHint !is DWT.DEFAULT) height = hHint;
+        width += border * 2; height += border * 2;
+        return new Point (width, height);
+    }
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    bool drawText = true;
+    bool drawImage = (bits & OS.SS_OWNERDRAW) is OS.SS_OWNERDRAW;
+    if (drawImage) {
+        if (image !is null) {
+            Rectangle rect = image.getBounds();
+            width += rect.width;
+            height += rect.height;
+            if (IMAGE_AND_TEXT) {
+                if (text.length () !is 0) width += MARGIN;
+            } else {
+                drawText = false;
+            }
+        }
+    }
+    if (drawText) {
+        int hDC = OS.GetDC (handle);
+        int newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        int oldFont = OS.SelectObject (hDC, newFont);
+        int length = OS.GetWindowTextLength (handle);
+        if (length is 0) {
+            TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+            OS.GetTextMetrics (hDC, tm);
+            height = Math.max (height, tm.tmHeight);
+        } else {
+            RECT rect = new RECT ();
+            int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_EXPANDTABS;
+            if ((style & DWT.WRAP) !is 0 && wHint !is DWT.DEFAULT) {
+                flags |= OS.DT_WORDBREAK;
+                rect.right = Math.max (0, wHint - width);
+            }
+            TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+            OS.GetWindowText (handle, buffer, length + 1);
+            OS.DrawText (hDC, buffer, length, rect, flags);
+            width += rect.right - rect.left;
+            height = Math.max (height, rect.bottom - rect.top);
+        }
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+    }
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    width += border * 2;
+    height += border * 2;
+    /*
+    * Feature in WinCE PPC.  Text labels have a trim
+    * of one pixel wide on the right and left side.
+    * The fix is to increase the width to include
+    * this trim.
+    */
+    if (OS.IsWinCE && !drawImage) width += 2;
+    return new Point (width, height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state |= THEME_BACKGROUND;
+}
+
+/**
+ * Returns a value which describes the position of the
+ * text or image in the receiver. The value will be one of
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>
+ * unless the receiver is a <code>SEPARATOR</code> label, in
+ * which case, <code>NONE</code> is returned.
+ *
+ * @return the alignment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getAlignment () {
+    checkWidget ();
+    if ((style & DWT.SEPARATOR) !is 0) return 0;
+    if ((style & DWT.LEFT) !is 0) return DWT.LEFT;
+    if ((style & DWT.CENTER) !is 0) return DWT.CENTER;
+    if ((style & DWT.RIGHT) !is 0) return DWT.RIGHT;
+    return DWT.LEFT;
+}
+
+/**
+ * Returns the receiver's image if it has one, or null
+ * if it does not.
+ *
+ * @return the receiver's image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getImage () {
+    checkWidget ();
+    return image;
+}
+
+String getNameText () {
+    return getText ();
+}
+
+/**
+ * Returns the receiver's text, which will be an empty
+ * string if it has never been set or if the receiver is
+ * a <code>SEPARATOR</code> label.
+ *
+ * @return the receiver's text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText () {
+    checkWidget ();
+    if ((style & DWT.SEPARATOR) !is 0) return "";
+    return text;
+}
+
+bool mnemonicHit (char key) {
+    Composite control = this.parent;
+    while (control !is null) {
+        Control [] children = control._getChildren ();
+        int index = 0;
+        while (index < children.length) {
+            if (children [index] is this) break;
+            index++;
+        }
+        index++;
+        if (index < children.length) {
+            if (children [index].setFocus ()) return true;
+        }
+        control = control.parent;
+    }
+    return false;
+}
+
+bool mnemonicMatch (char key) {
+    char mnemonic = findMnemonic (getText ());
+    if (mnemonic is '\0') return false;
+    return Character.toUpperCase (key) is Character.toUpperCase (mnemonic);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    text = null;
+    image = null;
+}
+
+/**
+ * Controls how text and images will be displayed in the receiver.
+ * The argument should be one of <code>LEFT</code>, <code>RIGHT</code>
+ * or <code>CENTER</code>.  If the receiver is a <code>SEPARATOR</code>
+ * label, the argument is ignored and the alignment is not changed.
+ *
+ * @param alignment the new alignment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setAlignment (int alignment) {
+    checkWidget ();
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    if ((alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER)) is 0) return;
+    style &= ~(DWT.LEFT | DWT.RIGHT | DWT.CENTER);
+    style |= alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER);
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.SS_OWNERDRAW) !is OS.SS_OWNERDRAW) {
+        bits &= ~(OS.SS_LEFTNOWORDWRAP | OS.SS_CENTER | OS.SS_RIGHT);
+        if ((style & DWT.LEFT) !is 0) {
+            if ((style & DWT.WRAP) !is 0) {
+                bits |= OS.SS_LEFT;
+            } else {
+                bits |= OS.SS_LEFTNOWORDWRAP;
+            }
+        }
+        if ((style & DWT.CENTER) !is 0) bits |= OS.SS_CENTER;
+        if ((style & DWT.RIGHT) !is 0) bits |= OS.SS_RIGHT;
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+    }
+    OS.InvalidateRect (handle, null, true);
+}
+
+/**
+ * Sets the receiver's image to the argument, which may be
+ * null indicating that no image should be displayed.
+ *
+ * @param image the image to display on the receiver (may be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (Image image) {
+    checkWidget ();
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    if (image !is null && image.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    this.image = image;
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.SS_OWNERDRAW) !is OS.SS_OWNERDRAW) {
+        bits &= ~(OS.SS_LEFTNOWORDWRAP | OS.SS_CENTER | OS.SS_RIGHT);
+        bits |= OS.SS_OWNERDRAW;
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+    }
+    OS.InvalidateRect (handle, null, true);
+}
+
+/**
+ * Sets the receiver's text.
+ * <p>
+ * This method sets the widget label.  The label may include
+ * the mnemonic character and line delimiters.
+ * </p>
+ * <p>
+ * Mnemonics are indicated by an '&amp;' that causes the next
+ * character to be the mnemonic.  When the user presses a
+ * key sequence that matches the mnemonic, focus is assigned
+ * to the control that follows the label. On most platforms,
+ * the mnemonic appears underlined but may be emphasised in a
+ * platform specific manner.  The mnemonic indicator character
+ * '&amp;' can be escaped by doubling it in the string, causing
+ * a single '&amp;' to be displayed.
+ * </p>
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    /*
+    * Feature in Windows.  For some reason, SetWindowText() for
+    * static controls redraws the control, even when the text has
+    * has not changed.  The fix is to check for this case and do
+    * nothing.
+    */
+    if (string.equals (text)) return;
+    text = string;
+    if (image is null || !IMAGE_AND_TEXT) {
+        int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
+        newBits &= ~OS.SS_OWNERDRAW;
+        if ((style & DWT.LEFT) !is 0) {
+            if ((style & DWT.WRAP) !is 0) {
+                newBits |= OS.SS_LEFT;
+            } else {
+                newBits |= OS.SS_LEFTNOWORDWRAP;
+            }
+        }
+        if ((style & DWT.CENTER) !is 0) newBits |= OS.SS_CENTER;
+        if ((style & DWT.RIGHT) !is 0) newBits |= OS.SS_RIGHT;
+        if (oldBits !is newBits) OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+    }
+    string = Display.withCrLf (string);
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    OS.SetWindowText (handle, buffer);
+    /*
+    * Bug in Windows.  For some reason, the HBRUSH that
+    * is returned from WM_CTRLCOLOR is misaligned when
+    * the label uses it to draw.  If the brush is a solid
+    * color, this does not matter.  However, if the brush
+    * contains an image, the image is misaligned.  The
+    * fix is to draw the background in WM_ERASEBKGND.
+    */
+    if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+        if (findImageControl () !is null) OS.InvalidateRect (handle, null, true);
+    }
+}
+
+int widgetExtStyle () {
+    int bits = super.widgetExtStyle () & ~OS.WS_EX_CLIENTEDGE;
+    if ((style & DWT.BORDER) !is 0) return bits | OS.WS_EX_STATICEDGE;
+    return bits;
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.SS_NOTIFY;
+    if ((style & DWT.SEPARATOR) !is 0) return bits | OS.SS_OWNERDRAW;
+    if (OS.WIN32_VERSION >= OS.VERSION (5, 0)) {
+        if ((style & DWT.WRAP) !is 0) bits |= OS.SS_EDITCONTROL;
+    }
+    if ((style & DWT.CENTER) !is 0) return bits | OS.SS_CENTER;
+    if ((style & DWT.RIGHT) !is 0) return bits | OS.SS_RIGHT;
+    if ((style & DWT.WRAP) !is 0) return bits | OS.SS_LEFT;
+    return bits | OS.SS_LEFTNOWORDWRAP;
+}
+
+TCHAR windowClass () {
+    return LabelClass;
+}
+
+int windowProc () {
+    return LabelProc;
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    if (result !is null) return result;
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.SS_OWNERDRAW) is OS.SS_OWNERDRAW) {
+        return LRESULT.ONE;
+    }
+    /*
+    * Bug in Windows.  For some reason, the HBRUSH that
+    * is returned from WM_CTRLCOLOR is misaligned when
+    * the label uses it to draw.  If the brush is a solid
+    * color, this does not matter.  However, if the brush
+    * contains an image, the image is misaligned.  The
+    * fix is to draw the background in WM_ERASEBKGND.
+    */
+    if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+        if (findImageControl () !is null) {
+            drawBackground (wParam);
+            return LRESULT.ONE;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    if (isDisposed ()) return result;
+    if ((style & DWT.SEPARATOR) !is 0) {
+        OS.InvalidateRect (handle, null, true);
+        return result;
+    }
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.SS_OWNERDRAW) is OS.SS_OWNERDRAW) {
+        OS.InvalidateRect (handle, null, true);
+        return result;
+    }
+    /*
+    * Bug in Windows.  For some reason, a label with
+    * style SS_LEFT, SS_CENTER or SS_RIGHT does not
+    * redraw the text in the new position when resized.
+    * Note that SS_LEFTNOWORDWRAP does not have the
+    * problem.  The fix is to force the redraw.
+    */
+    if ((bits & OS.SS_LEFTNOWORDWRAP) !is OS.SS_LEFTNOWORDWRAP) {
+        OS.InvalidateRect (handle, null, true);
+        return result;
+    }
+    return result;
+}
+
+LRESULT WM_UPDATEUISTATE (int wParam, int lParam) {
+    LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam);
+    /*
+    * Feature in Windows.  When WM_UPDATEUISTATE is sent to
+    * a static control, it sends WM_CTLCOLORSTATIC to get the
+    * foreground and background.  If any drawing happens in
+    * WM_CTLCOLORSTATIC, it overwrites the contents of the control.
+    * The fix is draw the static without drawing the background
+    * and avoid the static window proc.
+    */
+    bool redraw = findImageControl () !is null;
+    if (!redraw) {
+        if ((state & THEME_BACKGROUND) !is 0) {
+            if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                redraw = findThemeControl () !is null;
+            }
+        }
+    }
+    if (redraw) {
+        OS.InvalidateRect (handle, null, false);
+        int code = OS.DefWindowProc (handle, OS.WM_UPDATEUISTATE, wParam, lParam);
+        return new LRESULT (code);
+    }
+    return result;
+}
+
+LRESULT wmColorChild (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  For some reason, the HBRUSH that
+    * is returned from WM_CTRLCOLOR is misaligned when
+    * the label uses it to draw.  If the brush is a solid
+    * color, this does not matter.  However, if the brush
+    * contains an image, the image is misaligned.  The
+    * fix is to draw the background in WM_ERASEBKGND.
+    */
+    LRESULT result = super.wmColorChild (wParam, lParam);
+    if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if ((bits & OS.SS_OWNERDRAW) !is OS.SS_OWNERDRAW) {
+            if (findImageControl () !is null) {
+                OS.SetBkMode (wParam, OS.TRANSPARENT);
+                return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH));
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_PAINT (int wParam, int lParam) {
+    if (OS.IsWinCE) {
+        bool drawImage = image !is null;
+        bool drawSeparator = (style & DWT.SEPARATOR) !is 0 && (style & DWT.SHADOW_NONE) is 0;
+        if (drawImage || drawSeparator) {
+            LRESULT result = null;
+            PAINTSTRUCT ps = new PAINTSTRUCT ();
+            GCData data = new GCData ();
+            data.ps = ps;
+            data.hwnd = handle;
+            GC gc = new_GC (data);
+            if (gc !is null) {
+                drawBackground (gc.handle);
+                RECT clientRect = new RECT();
+                OS.GetClientRect (handle, clientRect);
+                if (drawSeparator) {
+                    RECT rect = new RECT ();
+                    int lineWidth = OS.GetSystemMetrics (OS.SM_CXBORDER);
+                    int flags = (style & DWT.SHADOW_IN) !is 0 ? OS.EDGE_SUNKEN : OS.EDGE_ETCHED;
+                    if ((style & DWT.HORIZONTAL) !is 0) {
+                        int bottom = clientRect.top + Math.max (lineWidth * 2, (clientRect.bottom - clientRect.top) / 2);
+                        OS.SetRect (rect, clientRect.left, clientRect.top, clientRect.right, bottom);
+                        OS.DrawEdge (gc.handle, rect, flags, OS.BF_BOTTOM);
+                    } else {
+                        int right = clientRect.left + Math.max (lineWidth * 2, (clientRect.right - clientRect.left) / 2);
+                        OS.SetRect (rect, clientRect.left, clientRect.top, right, clientRect.bottom);
+                        OS.DrawEdge (gc.handle, rect, flags, OS.BF_RIGHT);
+                    }
+                    result = LRESULT.ONE;
+                }
+                if (drawImage) {
+                    Rectangle imageBounds = image.getBounds ();
+                    int x = 0;
+                    if ((style & DWT.CENTER) !is 0) {
+                        x = Math.max (0, (clientRect.right - imageBounds.width) / 2);
+                    } else {
+                        if ((style & DWT.RIGHT) !is 0) {
+                            x = Math.max (0, (clientRect.right - imageBounds.width));
+                        }
+                    }
+                    gc.drawImage (image, x, Math.max (0, (clientRect.bottom - imageBounds.height) / 2));
+                    result = LRESULT.ONE;
+                }
+                int width = ps.right - ps.left;
+                int height = ps.bottom - ps.top;
+                if (width !is 0 && height !is 0) {
+                    Event event = new Event ();
+                    event.gc = gc;
+                    event.x = ps.left;
+                    event.y = ps.top;
+                    event.width = width;
+                    event.height = height;
+                    sendEvent (DWT.Paint, event);
+                    // widget could be disposed at this point
+                    event.gc = null;
+                }
+                gc.dispose ();
+            }
+            return result;
+        }
+    }
+    return super.WM_PAINT(wParam, lParam);
+}
+
+LRESULT wmDrawChild (int wParam, int lParam) {
+    DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
+    OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof);
+    drawBackground (struct.hDC);
+    if ((style & DWT.SEPARATOR) !is 0) {
+        if ((style & DWT.SHADOW_NONE) !is 0) return null;
+        RECT rect = new RECT ();
+        int lineWidth = OS.GetSystemMetrics (OS.SM_CXBORDER);
+        int flags = (style & DWT.SHADOW_IN) !is 0 ? OS.EDGE_SUNKEN : OS.EDGE_ETCHED;
+        if ((style & DWT.HORIZONTAL) !is 0) {
+            int bottom = struct.top + Math.max (lineWidth * 2, (struct.bottom - struct.top) / 2);
+            OS.SetRect (rect, struct.left, struct.top, struct.right, bottom);
+            OS.DrawEdge (struct.hDC, rect, flags, OS.BF_BOTTOM);
+        } else {
+            int right = struct.left + Math.max (lineWidth * 2, (struct.right - struct.left) / 2);
+            OS.SetRect (rect, struct.left, struct.top, right, struct.bottom);
+            OS.DrawEdge (struct.hDC, rect, flags, OS.BF_RIGHT);
+        }
+    } else {
+        int width = struct.right - struct.left;
+        int height = struct.bottom - struct.top;
+        if (width !is 0 && height !is 0) {
+            bool drawImage = image !is null;
+            bool drawText = IMAGE_AND_TEXT && text.length () !is 0;
+            int margin = drawText && drawImage ? MARGIN : 0;
+            int imageWidth = 0, imageHeight = 0;
+            if (drawImage) {
+                Rectangle rect = image.getBounds ();
+                imageWidth = rect.width;
+                imageHeight = rect.height;
+            }
+            RECT rect = null;
+            TCHAR buffer = null;
+            int textWidth = 0, textHeight = 0, flags = 0;
+            if (drawText) {
+                rect = new RECT ();
+                flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_EXPANDTABS;
+                if ((style & DWT.LEFT) !is 0) flags |= OS.DT_LEFT;
+                if ((style & DWT.CENTER) !is 0) flags |= OS.DT_CENTER;
+                if ((style & DWT.RIGHT) !is 0) flags |= OS.DT_RIGHT;
+                if ((style & DWT.WRAP) !is 0) {
+                    flags |= OS.DT_WORDBREAK;
+                    rect.right = Math.max (0, width - imageWidth - margin);
+                }
+                buffer = new TCHAR (getCodePage (), text, true);
+                OS.DrawText (struct.hDC, buffer, -1, rect, flags);
+                textWidth = rect.right - rect.left;
+                textHeight = rect.bottom - rect.top;
+            }
+            int x = 0;
+            if ((style & DWT.CENTER) !is 0) {
+                x = Math.max (0, (width - imageWidth - textWidth - margin) / 2);
+            } else {
+                if ((style & DWT.RIGHT) !is 0) {
+                    x = width - imageWidth - textWidth - margin;
+                }
+            }
+            if (drawImage) {
+                GCData data = new GCData();
+                data.device = display;
+                GC gc = GC.win32_new (struct.hDC, data);
+                Image image = getEnabled () ? this.image : new Image (display, this.image, DWT.IMAGE_DISABLE);
+                gc.drawImage (image, x, Math.max (0, (height - imageHeight) / 2));
+                if (image !is this.image) image.dispose ();
+                gc.dispose ();
+                x += imageWidth + margin;
+            }
+            if (drawText) {
+                flags &= ~OS.DT_CALCRECT;
+                rect.left = x;
+                rect.right += rect.left;
+                rect.top = Math.max (0, (height - textHeight) / 2);
+                rect.bottom += rect.top;
+                OS.DrawText (struct.hDC, buffer, -1, rect, flags);
+            }
+        }
+    }
+    return null;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Layout.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Layout;
+
+
+import dwt.graphics.Point;
+import dwt.widgets.Control;
+import dwt.widgets.Composite;
+
+/**
+ * A layout controls the position and size
+ * of the children of a composite widget.
+ * This class is the abstract base class for
+ * layouts.
+ *
+ *  @see Composite#setLayout(Layout)
+ */
+public abstract class Layout {
+
+/**
+ * Computes and returns the size of the specified
+ * composite's client area according to this layout.
+ * <p>
+ * This method computes the size that the client area
+ * of the composite must be in order to position all
+ * children at their preferred size inside the
+ * composite according to the layout algorithm
+ * encoded by this layout.
+ * </p>
+ * <p>
+ * When a width or height hint is supplied, it is
+ * used to constrain the result. For example, if a
+ * width hint is provided that is less than the
+ * width of the client area, the layout may choose
+ * to wrap and increase height, clip, overlap, or
+ * otherwise constrain the children.
+ * </p>
+ *
+ * @param composite a composite widget using this layout
+ * @param wHint width (<code>DWT.DEFAULT</code> for preferred size)
+ * @param hHint height (<code>DWT.DEFAULT</code> for preferred size)
+ * @param flushCache <code>true</code> means flush cached layout values
+ * @return a point containing the computed size (width, height)
+ *
+ * @see #layout
+ * @see Control#getBorderWidth
+ * @see Control#getBounds
+ * @see Control#getSize
+ * @see Control#pack(boolean)
+ * @see "computeTrim, getClientArea for controls that implement them"
+ */
+abstract Point computeSize (Composite composite, int wHint, int hHint, bool flushCache);
+
+/**
+ * Instruct the layout to flush any cached values
+ * associated with the control specified in the argument
+ * <code>control</code>.
+ *
+ * @param control a control managed by this layout
+ * @return true if the Layout has flushed all cached information associated with control
+ *
+ * @since 3.1
+ */
+bool flushCache (Control control) {
+    return false;
+}
+
+/**
+ * Lays out the children of the specified composite
+ * according to this layout.
+ * <p>
+ * This method positions and sizes the children of a
+ * composite using the layout algorithm encoded by this
+ * layout. Children of the composite are positioned in
+ * the client area of the composite. The position of
+ * the composite is not altered by this method.
+ * </p>
+ * <p>
+ * When the flush cache hint is true, the layout is
+ * instructed to flush any cached values associated
+ * with the children. Typically, a layout will cache
+ * the preferred sizes of the children to avoid the
+ * expense of computing these values each time the
+ * widget is laid out.
+ * </p>
+ * <p>
+ * When layout is triggered explicitly by the programmer
+ * the flush cache hint is true. When layout is triggered
+ * by a resize, either caused by the programmer or by the
+ * user, the hint is false.
+ * </p>
+ *
+ * @param composite a composite widget using this layout
+ * @param flushCache <code>true</code> means flush cached layout values
+ */
+abstract void layout (Composite composite, bool flushCache);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Link.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,968 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Link;
+
+import dwt.widgets.Control;
+class Link : Control {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.accessibility.ACC;
+import dwt.accessibility.Accessible;
+import dwt.accessibility.AccessibleAdapter;
+import dwt.accessibility.AccessibleControlAdapter;
+import dwt.accessibility.AccessibleControlEvent;
+import dwt.accessibility.AccessibleEvent;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Point;
+import dwt.graphics.RGB;
+import dwt.graphics.Rectangle;
+import dwt.graphics.TextLayout;
+import dwt.graphics.TextStyle;
+import dwt.internal.win32.LITEM;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.NMLINK;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.PAINTSTRUCT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class represent a selectable
+ * user interface object that displays a text with
+ * links.
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class Link extends Control {
+    String text;
+    TextLayout layout;
+    Color linkColor, disabledColor;
+    Point [] offsets;
+    Point selection;
+    String [] ids;
+    int [] mnemonics;
+    int focusIndex, mouseDownIndex;
+    int font;
+    static final RGB LINK_FOREGROUND = new RGB (0, 51, 153);
+    static final int LinkProc;
+    static final TCHAR LinkClass = new TCHAR (0, OS.WC_LINK, true);
+    static {
+        if (OS.COMCTL32_MAJOR >= 6) {
+            WNDCLASS lpWndClass = new WNDCLASS ();
+            OS.GetClassInfo (0, LinkClass, lpWndClass);
+            LinkProc = lpWndClass.lpfnWndProc;
+        } else {
+            LinkProc = 0;
+        }
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Link (Composite parent, int style) {
+    super (parent, style);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the control is selected by the user.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection, typedListener);
+    addListener (DWT.DefaultSelection, typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    if (LinkProc !is 0) return OS.CallWindowProc (LinkProc, hwnd, msg, wParam, lParam);
+    return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    if (wHint !is DWT.DEFAULT && wHint < 0) wHint = 0;
+    if (hHint !is DWT.DEFAULT && hHint < 0) hHint = 0;
+    int width, height;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        int hDC = OS.GetDC (handle);
+        int newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        int oldFont = OS.SelectObject (hDC, newFont);
+        TCHAR buffer = new TCHAR (getCodePage (), parse (text), false);
+        RECT rect = new RECT ();
+        int flags = OS.DT_CALCRECT | OS.DT_NOPREFIX;
+        if (wHint !is DWT.DEFAULT) {
+            flags |= OS.DT_WORDBREAK;
+            rect.right = wHint;
+        }
+        OS.DrawText (hDC, buffer, buffer.length (), rect, flags);
+        width = rect.right - rect.left;
+        height = rect.bottom;
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+    } else {
+        int layoutWidth = layout.getWidth ();
+        //TEMPORARY CODE
+        if (wHint is 0) {
+            layout.setWidth (1);
+            Rectangle rect = layout.getBounds ();
+            width = 0;
+            height = rect.height;
+        } else {
+            layout.setWidth (wHint);
+            Rectangle rect = layout.getBounds ();
+            width = rect.width;
+            height = rect.height;
+        }
+        layout.setWidth (layoutWidth);
+    }
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    int border = getBorderWidth ();
+    width += border * 2;
+    height += border * 2;
+    return new Point (width, height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state |= THEME_BACKGROUND;
+    if (OS.COMCTL32_MAJOR < 6) {
+        layout = new TextLayout (display);
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+            linkColor = Color.win32_new (display, OS.GetSysColor (OS.COLOR_HOTLIGHT));
+        } else {
+            linkColor = new Color (display, LINK_FOREGROUND);
+        }
+        disabledColor = Color.win32_new (display, OS.GetSysColor (OS.COLOR_GRAYTEXT));
+        offsets = new Point [0];
+        ids = new String [0];
+        mnemonics = new int [0];
+        selection = new Point (-1, -1);
+        focusIndex = mouseDownIndex = -1;
+    }
+}
+
+void createWidget () {
+    super.createWidget ();
+    text = "";
+    if (OS.COMCTL32_MAJOR < 6) {
+        if ((style & DWT.MIRRORED) !is 0) {
+            layout.setOrientation (DWT.RIGHT_TO_LEFT);
+        }
+        initAccessible ();
+    }
+}
+
+void drawWidget (GC gc, RECT rect) {
+    drawBackground (gc.handle, rect);
+    int selStart = selection.x;
+    int selEnd = selection.y;
+    if (selStart > selEnd) {
+        selStart = selection.y;
+        selEnd = selection.x;
+    }
+    // temporary code to disable text selection
+    selStart = selEnd = -1;
+    if (!OS.IsWindowEnabled (handle)) gc.setForeground (disabledColor);
+    layout.draw (gc, 0, 0, selStart, selEnd, null, null);
+    if (hasFocus () && focusIndex !is -1) {
+        Rectangle [] rects = getRectangles (focusIndex);
+        for (int i = 0; i < rects.length; i++) {
+            Rectangle rectangle = rects [i];
+            gc.drawFocus (rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+        }
+    }
+    if (hooks (DWT.Paint) || filters (DWT.Paint)) {
+        Event event = new Event ();
+        event.gc = gc;
+        event.x = rect.left;
+        event.y = rect.top;
+        event.width = rect.right - rect.left;
+        event.height = rect.bottom - rect.top;
+        sendEvent (DWT.Paint, event);
+        event.gc = null;
+    }
+}
+
+void enableWidget (bool enabled) {
+    if (OS.COMCTL32_MAJOR >= 6) {
+        LITEM item = new LITEM ();
+        item.mask = OS.LIF_ITEMINDEX | OS.LIF_STATE;
+        item.stateMask = OS.LIS_ENABLED;
+        item.state = enabled ? OS.LIS_ENABLED : 0;
+        while (OS.SendMessage (handle, OS.LM_SETITEM, 0, item) !is 0) {
+            item.iLink++;
+        }
+    } else {
+        TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null);
+        linkStyle.underline = true;
+        for (int i = 0; i < offsets.length; i++) {
+            Point point = offsets [i];
+            layout.setStyle (linkStyle, point.x, point.y);
+        }
+        redraw ();
+    }
+    /*
+    * Feature in Windows.  For some reason, setting
+    * LIS_ENABLED state using LM_SETITEM causes the
+    * SysLink to become enabled.  To be specific,
+    * calling IsWindowEnabled() returns true.  The
+    * fix is disable the SysLink after LM_SETITEM.
+    */
+    super.enableWidget (enabled);
+}
+
+void initAccessible () {
+    Accessible accessible = getAccessible ();
+    accessible.addAccessibleListener (new AccessibleAdapter () {
+        public void getName (AccessibleEvent e) {
+            e.result = parse (text);
+        }
+    });
+
+    accessible.addAccessibleControlListener (new AccessibleControlAdapter () {
+        public void getChildAtPoint (AccessibleControlEvent e) {
+            e.childID = ACC.CHILDID_SELF;
+        }
+
+        public void getLocation (AccessibleControlEvent e) {
+            Rectangle rect = display.map (getParent (), null, getBounds ());
+            e.x = rect.x;
+            e.y = rect.y;
+            e.width = rect.width;
+            e.height = rect.height;
+        }
+
+        public void getChildCount (AccessibleControlEvent e) {
+            e.detail = 0;
+        }
+
+        public void getRole (AccessibleControlEvent e) {
+            e.detail = ACC.ROLE_LINK;
+        }
+
+        public void getState (AccessibleControlEvent e) {
+            e.detail = ACC.STATE_FOCUSABLE;
+            if (hasFocus ()) e.detail |= ACC.STATE_FOCUSED;
+        }
+
+        public void getDefaultAction (AccessibleControlEvent e) {
+            e.result = DWT.getMessage ("SWT_Press"); //$NON-NLS-1$
+        }
+
+        public void getSelection (AccessibleControlEvent e) {
+            if (hasFocus ()) e.childID = ACC.CHILDID_SELF;
+        }
+
+        public void getFocus (AccessibleControlEvent e) {
+            if (hasFocus ()) e.childID = ACC.CHILDID_SELF;
+        }
+    });
+}
+
+String getNameText () {
+    return getText ();
+}
+
+Rectangle [] getRectangles (int linkIndex) {
+    int lineCount = layout.getLineCount ();
+    Rectangle [] rects = new Rectangle [lineCount];
+    int [] lineOffsets = layout.getLineOffsets ();
+    Point point = offsets [linkIndex];
+    int lineStart = 1;
+    while (point.x > lineOffsets [lineStart]) lineStart++;
+    int lineEnd = 1;
+    while (point.y > lineOffsets [lineEnd]) lineEnd++;
+    int index = 0;
+    if (lineStart is lineEnd) {
+        rects [index++] = layout.getBounds (point.x, point.y);
+    } else {
+        rects [index++] = layout.getBounds (point.x, lineOffsets [lineStart]-1);
+        rects [index++] = layout.getBounds (lineOffsets [lineEnd-1], point.y);
+        if (lineEnd - lineStart > 1) {
+            for (int i = lineStart; i < lineEnd - 1; i++) {
+                rects [index++] = layout.getLineBounds (i);
+            }
+        }
+    }
+    if (rects.length !is index) {
+        Rectangle [] tmp = new Rectangle [index];
+        System.arraycopy (rects, 0, tmp, 0, index);
+        rects = tmp;
+    }
+    return rects;
+}
+
+/**
+ * Returns the receiver's text, which will be an empty
+ * string if it has never been set.
+ *
+ * @return the receiver's text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText () {
+    checkWidget ();
+    return text;
+}
+
+String parse (String string) {
+    int length = string.length ();
+    offsets = new Point [length / 4];
+    ids = new String [length / 4];
+    mnemonics = new int [length / 4 + 1];
+    StringBuffer result = new StringBuffer ();
+    char [] buffer = new char [length];
+    string.getChars (0, string.length (), buffer, 0);
+    int index = 0, state = 0, linkIndex = 0;
+    int start = 0, tagStart = 0, linkStart = 0, endtagStart = 0, refStart = 0;
+    while (index < length) {
+        char c = Character.toLowerCase (buffer [index]);
+        switch (state) {
+            case 0:
+                if (c is '<') {
+                    tagStart = index;
+                    state++;
+                }
+                break;
+            case 1:
+                if (c is 'a') state++;
+                break;
+            case 2:
+                switch (c) {
+                    case 'h':
+                        state = 7;
+                        break;
+                    case '>':
+                        linkStart = index  + 1;
+                        state++;
+                        break;
+                    default:
+                        if (Character.isWhitespace(c)) break;
+                        else state = 13;
+                }
+                break;
+            case 3:
+                if (c is '<') {
+                    endtagStart = index;
+                    state++;
+                }
+                break;
+            case 4:
+                state = c is '/' ? state + 1 : 3;
+                break;
+            case 5:
+                state = c is 'a' ? state + 1 : 3;
+                break;
+            case 6:
+                if (c is '>') {
+                    mnemonics [linkIndex] = parseMnemonics (buffer, start, tagStart, result);
+                    int offset = result.length ();
+                    parseMnemonics (buffer, linkStart, endtagStart, result);
+                    offsets [linkIndex] = new Point (offset, result.length () - 1);
+                    if (ids [linkIndex] is null) {
+                        ids [linkIndex] = new String (buffer, linkStart, endtagStart - linkStart);
+                    }
+                    linkIndex++;
+                    start = tagStart = linkStart = endtagStart = refStart = index + 1;
+                    state = 0;
+                } else {
+                    state = 3;
+                }
+                break;
+            case 7:
+                state = c is 'r' ? state + 1 : 0;
+                break;
+            case 8:
+                state = c is 'e' ? state + 1 : 0;
+                break;
+            case 9:
+                state = c is 'f' ? state + 1 : 0;
+                break;
+            case 10:
+                state = c is '=' ? state + 1 : 0;
+                break;
+            case 11:
+                if (c is '"') {
+                    state++;
+                    refStart = index + 1;
+                } else {
+                    state = 0;
+                }
+                break;
+            case 12:
+                if (c is '"') {
+                    ids[linkIndex] = new String (buffer, refStart, index - refStart);
+                    state = 2;
+                }
+                break;
+            case 13:
+                if (Character.isWhitespace (c)) {
+                    state = 0;
+                } else if (c is '='){
+                    state++;
+                }
+                break;
+            case 14:
+                state = c is '"' ? state + 1 : 0;
+                break;
+            case 15:
+                if (c is '"') state = 2;
+                break;
+            default:
+                state = 0;
+                break;
+        }
+        index++;
+    }
+    if (start < length) {
+        int tmp = parseMnemonics (buffer, start, tagStart, result);
+        int mnemonic = parseMnemonics (buffer, linkStart, index, result);
+        if (mnemonic is -1) mnemonic = tmp;
+        mnemonics [linkIndex] = mnemonic;
+    } else {
+        mnemonics [linkIndex] = -1;
+    }
+    if (offsets.length !is linkIndex) {
+        Point [] newOffsets = new Point [linkIndex];
+        System.arraycopy (offsets, 0, newOffsets, 0, linkIndex);
+        offsets = newOffsets;
+        String [] newIDs = new String [linkIndex];
+        System.arraycopy (ids, 0, newIDs, 0, linkIndex);
+        ids = newIDs;
+        int [] newMnemonics = new int [linkIndex + 1];
+        System.arraycopy (mnemonics, 0, newMnemonics, 0, linkIndex + 1);
+        mnemonics = newMnemonics;
+    }
+    return result.toString ();
+}
+
+int parseMnemonics (char[] buffer, int start, int end, StringBuffer result) {
+    int mnemonic = -1, index = start;
+    while (index < end) {
+        if (buffer [index] is '&') {
+            if (index + 1 < end && buffer [index + 1] is '&') {
+                result.append (buffer [index]);
+                index++;
+            } else {
+                mnemonic = result.length();
+            }
+        } else {
+            result.append (buffer [index]);
+        }
+        index++;
+    }
+    return mnemonic;
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if (layout !is null) layout.dispose ();
+    layout = null;
+    if (linkColor !is null) linkColor.dispose ();
+    linkColor = null;
+    disabledColor = null;
+    offsets = null;
+    ids = null;
+    mnemonics = null;
+    text = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection, listener);
+}
+
+/**
+ * Sets the receiver's text.
+ * <p>
+ * The string can contain both regular text and hyperlinks.  A hyperlink
+ * is delimited by an anchor tag, &lt;A&gt; and &lt;/A&gt;.  Within an
+ * anchor, a single HREF attribute is supported.  When a hyperlink is
+ * selected, the text field of the selection event contains either the
+ * text of the hyperlink or the value of its HREF, if one was specified.
+ * In the rare case of identical hyperlinks within the same string, the
+ * HREF tag can be used to distinguish between them.  The string may
+ * include the mnemonic character and line delimiters.
+ * </p>
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (string.equals (text)) return;
+    text = string;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        bool enabled = OS.IsWindowEnabled (handle);
+        /*
+        * Bug in Windows.  For some reason, when SetWindowText()
+        * is used to set the text of a link control to the empty
+        * string, the old text remains.  The fix is to set the
+        * text to a space instead.
+        */
+        if (string.length () is 0) string = " ";  //$NON-NLS-1$
+        TCHAR buffer = new TCHAR (getCodePage (), string, true);
+        OS.SetWindowText (handle, buffer);
+        parse (text);
+        enableWidget (enabled);
+    } else {
+        layout.setText (parse (text));
+        focusIndex = offsets.length > 0 ? 0 : -1;
+        selection.x = selection.y = -1;
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if (offsets.length > 0) {
+            bits |= OS.WS_TABSTOP;
+        } else {
+            bits &= ~OS.WS_TABSTOP;
+        }
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+        bool enabled = OS.IsWindowEnabled (handle);
+        TextStyle linkStyle = new TextStyle (null, enabled ? linkColor : disabledColor, null);
+        linkStyle.underline = true;
+        for (int i = 0; i < offsets.length; i++) {
+            Point point = offsets [i];
+            layout.setStyle (linkStyle, point.x, point.y);
+        }
+        TextStyle mnemonicStyle = new TextStyle (null, null, null);
+        mnemonicStyle.underline = true;
+        for (int i = 0; i < mnemonics.length; i++) {
+            int mnemonic  = mnemonics [i];
+            if (mnemonic !is -1) {
+                layout.setStyle (mnemonicStyle, mnemonic, mnemonic);
+            }
+        }
+        redraw ();
+    }
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle ();
+    return bits | OS.WS_TABSTOP;
+}
+
+TCHAR windowClass () {
+    return OS.COMCTL32_MAJOR >= 6 ? LinkClass : display.windowClass;
+}
+
+int windowProc () {
+    return LinkProc !is 0 ? LinkProc : display.windowProc;
+}
+
+LRESULT WM_CHAR (int wParam, int lParam) {
+    LRESULT result = super.WM_CHAR (wParam, lParam);
+    if (result !is null) return result;
+    if (OS.COMCTL32_MAJOR < 6) {
+        if (focusIndex is -1) return result;
+        switch (wParam) {
+            case ' ':
+            case DWT.CR:
+                Event event = new Event ();
+                event.text = ids [focusIndex];
+                sendEvent (DWT.Selection, event);
+                break;
+            case DWT.TAB:
+                bool next = OS.GetKeyState (OS.VK_SHIFT) >= 0;
+                if (next) {
+                    if (focusIndex < offsets.length - 1) {
+                        focusIndex++;
+                        redraw ();
+                    }
+                } else {
+                    if (focusIndex > 0) {
+                        focusIndex--;
+                        redraw ();
+                    }
+                }
+                break;
+        }
+    } else {
+        switch (wParam) {
+            case ' ':
+            case DWT.CR:
+            case DWT.TAB:
+                /*
+                * NOTE: Call the window proc with WM_KEYDOWN rather than WM_CHAR
+                * so that the key that was ignored during WM_KEYDOWN is processed.
+                * This allows the application to cancel an operation that is normally
+                * performed in WM_KEYDOWN from WM_CHAR.
+                */
+                int code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
+                return new LRESULT (code);
+        }
+
+    }
+    return result;
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
+    if (result !is null) return result;
+    int index, count, code = 0;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        LITEM item = new LITEM ();
+        item.mask = OS.LIF_ITEMINDEX | OS.LIF_STATE;
+        item.stateMask = OS.LIS_FOCUSED;
+        index = 0;
+        while (OS.SendMessage (handle, OS.LM_GETITEM, 0, item) !is 0) {
+            if ((item.state & OS.LIS_FOCUSED) !is 0) {
+                index = item.iLink;
+            }
+            item.iLink++;
+        }
+        count = item.iLink;
+        code = callWindowProc (handle, OS.WM_GETDLGCODE, wParam, lParam);
+    } else {
+        index = focusIndex;
+        count = offsets.length;
+    }
+    if (count is 0) {
+        return new LRESULT (code | OS.DLGC_STATIC);
+    }
+    bool next = OS.GetKeyState (OS.VK_SHIFT) >= 0;
+    if (next && index < count - 1) {
+        return new LRESULT (code | OS.DLGC_WANTTAB);
+    }
+    if (!next && index > 0) {
+        return new LRESULT (code | OS.DLGC_WANTTAB);
+    }
+    return result;
+}
+
+LRESULT WM_GETFONT (int wParam, int lParam) {
+    LRESULT result = super.WM_GETFONT (wParam, lParam);
+    if (result !is null) return result;
+    int code = callWindowProc (handle, OS.WM_GETFONT, wParam, lParam);
+    if (code !is 0) return new LRESULT (code);
+    if (font is 0) font = defaultFont ();
+    return new LRESULT (font);
+}
+
+LRESULT WM_KEYDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_KEYDOWN (wParam, lParam);
+    if (result !is null) return result;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        switch (wParam) {
+            case OS.VK_SPACE:
+            case OS.VK_RETURN:
+            case OS.VK_TAB:
+                /*
+                * Ensure that the window proc does not process VK_SPACE,
+                * VK_RETURN or VK_TAB so that it can be handled in WM_CHAR.
+                * This allows the application to cancel an operation that
+                * is normally performed in WM_KEYDOWN from WM_CHAR.
+                */
+                return LRESULT.ZERO;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
+    if (OS.COMCTL32_MAJOR < 6) redraw ();
+    return result;
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+    if (OS.COMCTL32_MAJOR < 6) {
+        if (focusIndex !is -1) setFocus ();
+        int x = lParam & 0xFFFF;
+        int y = lParam >> 16;
+        int offset = layout.getOffset (x, y, null);
+        int oldSelectionX = selection.x;
+        int oldSelectionY = selection.y;
+        selection.x = offset;
+        selection.y = -1;
+        if (oldSelectionX !is -1 && oldSelectionY !is -1) {
+            if (oldSelectionX > oldSelectionY) {
+                int temp = oldSelectionX;
+                oldSelectionX = oldSelectionY;
+                oldSelectionY = temp;
+            }
+            Rectangle rect = layout.getBounds (oldSelectionX, oldSelectionY);
+            redraw (rect.x, rect.y, rect.width, rect.height, false);
+        }
+        for (int j = 0; j < offsets.length; j++) {
+            Rectangle [] rects = getRectangles (j);
+            for (int i = 0; i < rects.length; i++) {
+                Rectangle rect = rects [i];
+                if (rect.contains (x, y)) {
+                    if (j !is focusIndex) {
+                        redraw ();
+                    }
+                    focusIndex = mouseDownIndex = j;
+                    return result;
+                }
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_LBUTTONUP (int wParam, int lParam) {
+    LRESULT result = super.WM_LBUTTONUP (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+    if (OS.COMCTL32_MAJOR < 6) {
+        if (mouseDownIndex is -1) return result;
+        int x = lParam & 0xFFFF;
+        int y = lParam >> 16;
+        Rectangle [] rects = getRectangles (mouseDownIndex);
+        for (int i = 0; i < rects.length; i++) {
+            Rectangle rect = rects [i];
+            if (rect.contains (x, y)) {
+                Event event = new Event ();
+                event.text = ids [mouseDownIndex];
+                sendEvent (DWT.Selection, event);
+                break;
+            }
+        }
+    }
+    mouseDownIndex = -1;
+    return result;
+}
+
+LRESULT WM_MOUSEMOVE (int wParam, int lParam) {
+    LRESULT result = super.WM_MOUSEMOVE (wParam, lParam);
+    if (OS.COMCTL32_MAJOR < 6) {
+        int x = lParam & 0xFFFF;
+        int y = lParam >> 16;
+        if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
+            int oldSelection = selection.y;
+            selection.y = layout.getOffset (x, y, null);
+            if (selection.y !is oldSelection) {
+                int newSelection = selection.y;
+                if (oldSelection > newSelection) {
+                    int temp = oldSelection;
+                    oldSelection = newSelection;
+                    newSelection = temp;
+                }
+                Rectangle rect = layout.getBounds (oldSelection, newSelection);
+                redraw (rect.x, rect.y, rect.width, rect.height, false);
+            }
+        } else {
+            for (int j = 0; j < offsets.length; j++) {
+                Rectangle [] rects = getRectangles (j);
+                for (int i = 0; i < rects.length; i++) {
+                    Rectangle rect = rects [i];
+                    if (rect.contains (x, y)) {
+                        setCursor (display.getSystemCursor (DWT.CURSOR_HAND));
+                        return result;
+                    }
+                }
+            }
+            setCursor (null);
+        }
+    }
+    return result;
+}
+
+LRESULT WM_PAINT (int wParam, int lParam) {
+    if (OS.COMCTL32_MAJOR >= 6) {
+        return super.WM_PAINT (wParam, lParam);
+    }
+    PAINTSTRUCT ps = new PAINTSTRUCT ();
+    GCData data = new GCData ();
+    data.ps = ps;
+    data.hwnd = handle;
+    GC gc = new_GC (data);
+    if (gc !is null) {
+        int width = ps.right - ps.left;
+        int height = ps.bottom - ps.top;
+        if (width !is 0 && height !is 0) {
+            RECT rect = new RECT ();
+            OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+            drawWidget (gc, rect);
+        }
+        gc.dispose ();
+    }
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_PRINTCLIENT (int wParam, int lParam) {
+    LRESULT result = super.WM_PRINTCLIENT (wParam, lParam);
+    if (OS.COMCTL32_MAJOR < 6) {
+        RECT rect = new RECT ();
+        OS.GetClientRect (handle, rect);
+        GCData data = new GCData ();
+        data.device = display;
+        data.foreground = getForegroundPixel ();
+        GC gc = GC.win32_new (wParam, data);
+        drawWidget (gc, rect);
+        gc.dispose ();
+    }
+    return result;
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_SETFOCUS (wParam, lParam);
+    if (OS.COMCTL32_MAJOR < 6) redraw ();
+    return result;
+}
+
+LRESULT WM_SETFONT (int wParam, int lParam) {
+    if (OS.COMCTL32_MAJOR < 6) {
+        layout.setFont (Font.win32_new (display, wParam));
+    }
+    if (lParam !is 0) OS.InvalidateRect (handle, null, true);
+    return super.WM_SETFONT (font = wParam, lParam);
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    if (OS.COMCTL32_MAJOR < 6) {
+        RECT rect = new RECT ();
+        OS.GetClientRect (handle, rect);
+        layout.setWidth (rect.right > 0 ? rect.right : -1);
+        redraw ();
+    }
+    return result;
+}
+
+LRESULT wmColorChild (int wParam, int lParam) {
+    LRESULT result = super.wmColorChild (wParam, lParam);
+    /*
+    * Feature in Windows.  When a SysLink is disabled, it does
+    * not gray out the non-link portion of the text.  The fix
+    * is to set the text color to the system gray color.
+    */
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if (!OS.IsWindowEnabled (handle)) {
+            OS.SetTextColor (wParam, OS.GetSysColor (OS.COLOR_GRAYTEXT));
+            if (result is null) {
+                int backPixel = getBackgroundPixel ();
+                OS.SetBkColor (wParam, backPixel);
+                int hBrush = findBrush (backPixel, OS.BS_SOLID);
+                return new LRESULT (hBrush);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
+    if (OS.COMCTL32_MAJOR >= 6) {
+        switch (hdr.code) {
+            case OS.NM_RETURN:
+            case OS.NM_CLICK:
+                NMLINK item = new NMLINK ();
+                OS.MoveMemory (item, lParam, NMLINK.sizeof);
+                Event event = new Event ();
+                event.text = ids [item.iLink];
+                sendEvent (DWT.Selection, event);
+                break;
+        }
+    }
+    return super.wmNotifyChild (hdr, wParam, lParam);
+}
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/List.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1557 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.List;
+
+import dwt.widgets.Scrollable;
+class List : Scrollable {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Font;
+import dwt.graphics.Point;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SCROLLINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class represent a selectable user interface
+ * object that displays a list of strings and issues notification
+ * when a string is selected.  A list may be single or multi select.
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SINGLE, MULTI</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection, DefaultSelection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of SINGLE and MULTI may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class List extends Scrollable {
+    static final int INSET = 3;
+    static final int ListProc;
+    static final TCHAR ListClass = new TCHAR (0, "LISTBOX", true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, ListClass, lpWndClass);
+        ListProc = lpWndClass.lpfnWndProc;
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#SINGLE
+ * @see DWT#MULTI
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public List (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+/**
+ * Adds the argument to the end of the receiver's list.
+ *
+ * @param string the new item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #add(String,int)
+ */
+public void add (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    int result = OS.SendMessage (handle, OS.LB_ADDSTRING, 0, buffer);
+    if (result is OS.LB_ERR) error (DWT.ERROR_ITEM_NOT_ADDED);
+    if (result is OS.LB_ERRSPACE) error (DWT.ERROR_ITEM_NOT_ADDED);
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth (buffer, true);
+}
+/**
+ * Adds the argument to the receiver's list at the given
+ * zero-relative index.
+ * <p>
+ * Note: To add an item at the end of the list, use the
+ * result of calling <code>getItemCount()</code> as the
+ * index or use <code>add(String)</code>.
+ * </p>
+ *
+ * @param string the new item
+ * @param index the index for the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #add(String)
+ */
+public void add (String string, int index) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (index is -1) error (DWT.ERROR_INVALID_RANGE);
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    int result = OS.SendMessage (handle, OS.LB_INSERTSTRING, index, buffer);
+    if (result is OS.LB_ERRSPACE) error (DWT.ERROR_ITEM_NOT_ADDED);
+    if (result is OS.LB_ERR) {
+        int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+        if (0 <= index && index <= count) {
+            error (DWT.ERROR_ITEM_NOT_ADDED);
+        } else {
+            error (DWT.ERROR_INVALID_RANGE);
+        }
+    }
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth (buffer, true);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's selection, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the selection changes.
+ * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the user changes the receiver's selection
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.CallWindowProc (ListProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.SINGLE, DWT.MULTI, 0, 0, 0, 0);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = 0, height = 0;
+    if (wHint is DWT.DEFAULT) {
+        if ((style & DWT.H_SCROLL) !is 0) {
+            width = OS.SendMessage (handle, OS.LB_GETHORIZONTALEXTENT, 0, 0);
+            width -= INSET;
+        } else {
+            int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+            int newFont, oldFont = 0;
+            int hDC = OS.GetDC (handle);
+            newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+            if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+            RECT rect = new RECT ();
+            int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+            int cp = getCodePage ();
+            TCHAR buffer = new TCHAR (cp, 64 + 1);
+            for (int i=0; i<count; i++) {
+                int length = OS.SendMessage (handle, OS.LB_GETTEXTLEN, i, 0);
+                if (length !is OS.LB_ERR) {
+                    if (length + 1 > buffer.length ()) {
+                        buffer = new TCHAR (cp, length + 1);
+                    }
+                    int result = OS.SendMessage (handle, OS.LB_GETTEXT, i, buffer);
+                    if (result !is OS.LB_ERR) {
+                        OS.DrawText (hDC, buffer, length, rect, flags);
+                        width = Math.max (width, rect.right - rect.left);
+                    }
+                }
+            }
+            if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+            OS.ReleaseDC (handle, hDC);
+        }
+    }
+    if (hHint is DWT.DEFAULT) {
+        int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+        int itemHeight = OS.SendMessage (handle, OS.LB_GETITEMHEIGHT, 0, 0);
+        height = count * itemHeight;
+    }
+    if (width is 0) width = DEFAULT_WIDTH;
+    if (height is 0) height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    int border = getBorderWidth ();
+    width += border * 2 + INSET;
+    height += border * 2;
+    if ((style & DWT.V_SCROLL) !is 0) {
+        width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+    }
+    if ((style & DWT.H_SCROLL) !is 0) {
+        height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    }
+    return new Point (width, height);
+}
+
+int defaultBackground () {
+    return OS.GetSysColor (OS.COLOR_WINDOW);
+}
+
+/**
+ * Deselects the items at the given zero-relative indices in the receiver.
+ * If the item at the given zero-relative index in the receiver
+ * is selected, it is deselected.  If the item at the index
+ * was not selected, it remains deselected. Indices that are out
+ * of range and duplicate indices are ignored.
+ *
+ * @param indices the array of indices for the items to deselect
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int [] indices) {
+    checkWidget ();
+    if (indices is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (indices.length is 0) return;
+    if ((style & DWT.SINGLE) !is 0) {
+        int oldIndex = OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0);
+        if (oldIndex is OS.LB_ERR) return;
+        for (int i=0; i<indices.length; i++) {
+            if (oldIndex is indices [i]) {
+                OS.SendMessage (handle, OS.LB_SETCURSEL, -1, 0);
+                return;
+            }
+        }
+        return;
+    }
+    for (int i=0; i<indices.length; i++) {
+        int index = indices [i];
+        if (index !is -1) {
+            OS.SendMessage (handle, OS.LB_SETSEL, 0, index);
+        }
+    }
+}
+
+/**
+ * Deselects the item at the given zero-relative index in the receiver.
+ * If the item at the index was already deselected, it remains
+ * deselected. Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to deselect
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int index) {
+    checkWidget ();
+    if (index is -1) return;
+    if ((style & DWT.SINGLE) !is 0) {
+        int oldIndex = OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0);
+        if (oldIndex is OS.LB_ERR) return;
+        if (oldIndex is index) OS.SendMessage (handle, OS.LB_SETCURSEL, -1, 0);
+        return;
+    }
+    OS.SendMessage (handle, OS.LB_SETSEL, 0, index);
+}
+
+/**
+ * Deselects the items at the given zero-relative indices in the receiver.
+ * If the item at the given zero-relative index in the receiver
+ * is selected, it is deselected.  If the item at the index
+ * was not selected, it remains deselected.  The range of the
+ * indices is inclusive. Indices that are out of range are ignored.
+ *
+ * @param start the start index of the items to deselect
+ * @param end the end index of the items to deselect
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int start, int end) {
+    checkWidget ();
+    if (start > end) return;
+    if ((style & DWT.SINGLE) !is 0) {
+        int oldIndex = OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0);
+        if (oldIndex is OS.LB_ERR) return;
+        if (start <= oldIndex && oldIndex <= end) {
+            OS.SendMessage (handle, OS.LB_SETCURSEL, -1, 0);
+        }
+        return;
+    }
+    /*
+    * Ensure that at least one item is contained in
+    * the range from start to end.  Note that when
+    * start = end, LB_SELITEMRANGEEX deselects the
+    * item.
+    */
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (start < 0 && end < 0) return;
+    if (start >= count && end >= count) return;
+    start = Math.min (count - 1, Math.max (0, start));
+    end = Math.min (count - 1, Math.max (0, end));
+    OS.SendMessage (handle, OS.LB_SELITEMRANGEEX, end, start);
+}
+
+/**
+ * Deselects all selected items in the receiver.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselectAll () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) {
+        OS.SendMessage (handle, OS.LB_SETCURSEL, -1, 0);
+    } else {
+        OS.SendMessage (handle, OS.LB_SETSEL, 0, -1);
+    }
+}
+
+/**
+ * Returns the zero-relative index of the item which currently
+ * has the focus in the receiver, or -1 if no item has focus.
+ *
+ * @return the index of the selected item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getFocusIndex () {
+    checkWidget ();
+    int result = OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0);
+    if (result is 0) {
+        int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+        if (count is 0) return -1;
+    }
+    return result;
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getItem (int index) {
+    checkWidget ();
+    int length = OS.SendMessage (handle, OS.LB_GETTEXTLEN, index, 0);
+    if (length !is OS.LB_ERR) {
+        TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+        int result = OS.SendMessage (handle, OS.LB_GETTEXT, index, buffer);
+        if (result !is OS.LB_ERR) return buffer.toString (0, length);
+    }
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (0 <= index && index < count) error (DWT.ERROR_CANNOT_GET_ITEM);
+    error (DWT.ERROR_INVALID_RANGE);
+    return "";
+}
+
+/**
+ * Returns the number of items contained in the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    int result = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (result is OS.LB_ERR) error (DWT.ERROR_CANNOT_GET_COUNT);
+    return result;
+}
+
+/**
+ * Returns the height of the area which would be used to
+ * display <em>one</em> of the items in the list.
+ *
+ * @return the height of one item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemHeight () {
+    checkWidget ();
+    int result = OS.SendMessage (handle, OS.LB_GETITEMHEIGHT, 0, 0);
+    if (result is OS.LB_ERR) error (DWT.ERROR_CANNOT_GET_ITEM_HEIGHT);
+    return result;
+}
+
+/**
+ * Returns a (possibly empty) array of <code>String</code>s which
+ * are the items in the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver's list
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String [] getItems () {
+    checkWidget ();
+    int count = getItemCount ();
+    String [] result = new String [count];
+    for (int i=0; i<count; i++) result [i] = getItem (i);
+    return result;
+}
+
+/**
+ * Returns an array of <code>String</code>s that are currently
+ * selected in the receiver.  The order of the items is unspecified.
+ * An empty array indicates that no items are selected.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its selection, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ * @return an array representing the selection
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String [] getSelection () {
+    checkWidget ();
+    int [] indices = getSelectionIndices ();
+    String [] result = new String [indices.length];
+    for (int i=0; i<indices.length; i++) {
+        result [i] = getItem (indices [i]);
+    }
+    return result;
+}
+
+/**
+ * Returns the number of selected items contained in the receiver.
+ *
+ * @return the number of selected items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionCount () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) {
+        int result = OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0);
+        if (result is OS.LB_ERR) return 0;
+        return 1;
+    }
+    int result = OS.SendMessage (handle, OS.LB_GETSELCOUNT, 0, 0);
+    if (result is OS.LB_ERR) error (DWT.ERROR_CANNOT_GET_COUNT);
+    return result;
+}
+
+/**
+ * Returns the zero-relative index of the item which is currently
+ * selected in the receiver, or -1 if no item is selected.
+ *
+ * @return the index of the selected item or -1
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionIndex () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) {
+        return OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0);
+    }
+    int count = OS.SendMessage (handle, OS.LB_GETSELCOUNT, 0, 0);
+    if (count is OS.LB_ERR) error (DWT.ERROR_CANNOT_GET_SELECTION);
+    if (count is 0) return -1;
+    int index = OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0);
+    int result = OS.SendMessage (handle, OS.LB_GETSEL, index, 0);
+    if (result is OS.LB_ERR) error (DWT.ERROR_CANNOT_GET_SELECTION);
+    if (result !is 0) return index;
+    int [] buffer = new int [1];
+    result = OS.SendMessage (handle, OS.LB_GETSELITEMS, 1, buffer);
+    if (result !is 1) error (DWT.ERROR_CANNOT_GET_SELECTION);
+    return buffer [0];
+}
+
+/**
+ * Returns the zero-relative indices of the items which are currently
+ * selected in the receiver.  The order of the indices is unspecified.
+ * The array is empty if no items are selected.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its selection, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ * @return the array of indices of the selected items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int [] getSelectionIndices () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) {
+        int result = OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0);
+        if (result is OS.LB_ERR) return new int [0];
+        return new int [] {result};
+    }
+    int length = OS.SendMessage (handle, OS.LB_GETSELCOUNT, 0, 0);
+    if (length is OS.LB_ERR) error (DWT.ERROR_CANNOT_GET_SELECTION);
+    int [] indices = new int [length];
+    int result = OS.SendMessage (handle, OS.LB_GETSELITEMS, length, indices);
+    if (result !is length) error (DWT.ERROR_CANNOT_GET_SELECTION);
+    return indices;
+}
+
+/**
+ * Returns the zero-relative index of the item which is currently
+ * at the top of the receiver. This index can change when items are
+ * scrolled or new items are added or removed.
+ *
+ * @return the index of the top item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTopIndex () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0);
+}
+
+/**
+ * Gets the index of an item.
+ * <p>
+ * The list is searched starting at 0 until an
+ * item is found that is equal to the search item.
+ * If no item is found, -1 is returned.  Indexing
+ * is zero based.
+ *
+ * @param string the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (String string) {
+    return indexOf (string, 0);
+}
+
+/**
+ * Searches the receiver's list starting at the given,
+ * zero-relative index until an item is found that is equal
+ * to the argument, and returns the index of that item. If
+ * no item is found or the starting index is out of range,
+ * returns -1.
+ *
+ * @param string the search item
+ * @param start the zero-relative index at which to start the search
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (String string, int start) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+
+    /*
+    * Bug in Windows.  For some reason, LB_FINDSTRINGEXACT
+    * will not find empty strings even though it is legal
+    * to insert an empty string into a list.  The fix is
+    * to search the list, an item at a time.
+    */
+    if (string.length () is 0) {
+        int count = getItemCount ();
+        for (int i=start; i<count; i++) {
+            if (string.equals (getItem (i))) return i;
+        }
+        return -1;
+    }
+
+    /* Use LB_FINDSTRINGEXACT to search for the item */
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (!(0 <= start && start < count)) return -1;
+    int index = start - 1, last;
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    do {
+        index = OS.SendMessage (handle, OS.LB_FINDSTRINGEXACT, last = index, buffer);
+        if (index is OS.LB_ERR || index <= last) return -1;
+    } while (!string.equals (getItem (index)));
+    return index;
+}
+
+/**
+ * Returns <code>true</code> if the item is selected,
+ * and <code>false</code> otherwise.  Indices out of
+ * range are ignored.
+ *
+ * @param index the index of the item
+ * @return the selection state of the item at the index
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool isSelected (int index) {
+    checkWidget ();
+    int result = OS.SendMessage (handle, OS.LB_GETSEL, index, 0);
+    return (result !is 0) && (result !is OS.LB_ERR);
+}
+
+/**
+ * Removes the items from the receiver at the given
+ * zero-relative indices.
+ *
+ * @param indices the array of indices of the items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int [] indices) {
+    checkWidget ();
+    if (indices is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (indices.length is 0) return;
+    int [] newIndices = new int [indices.length];
+    System.arraycopy (indices, 0, newIndices, 0, indices.length);
+    sort (newIndices);
+    int start = newIndices [newIndices.length - 1], end = newIndices [0];
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (!(0 <= start && start <= end && end < count)) {
+        error (DWT.ERROR_INVALID_RANGE);
+    }
+    int topIndex = OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0);
+    RECT rect = null;
+    int hDC = 0, oldFont = 0, newFont = 0, newWidth = 0;
+    if ((style & DWT.H_SCROLL) !is 0) {
+        rect = new RECT ();
+        hDC = OS.GetDC (handle);
+        newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    }
+    int cp = getCodePage ();
+    int i = 0, topCount = 0, last = -1;
+    while (i < newIndices.length) {
+        int index = newIndices [i];
+        if (index !is last) {
+            TCHAR buffer = null;
+            if ((style & DWT.H_SCROLL) !is 0) {
+                int length = OS.SendMessage (handle, OS.LB_GETTEXTLEN, index, 0);
+                if (length is OS.LB_ERR) break;
+                buffer = new TCHAR (cp, length + 1);
+                int result = OS.SendMessage (handle, OS.LB_GETTEXT, index, buffer);
+                if (result is OS.LB_ERR) break;
+            }
+            int result = OS.SendMessage (handle, OS.LB_DELETESTRING, index, 0);
+            if (result is OS.LB_ERR) break;
+            if ((style & DWT.H_SCROLL) !is 0) {
+                int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+                OS.DrawText (hDC, buffer, -1, rect, flags);
+                newWidth = Math.max (newWidth, rect.right - rect.left);
+            }
+            if (index < topIndex) topCount++;
+            last = index;
+        }
+        i++;
+    }
+    if ((style & DWT.H_SCROLL) !is 0) {
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+        setScrollWidth (newWidth, false);
+    }
+    if (topCount > 0) {
+        topIndex -= topCount;
+        OS.SendMessage (handle, OS.LB_SETTOPINDEX, topIndex, 0);
+    }
+    if (i < newIndices.length) error (DWT.ERROR_ITEM_NOT_REMOVED);
+}
+
+/**
+ * Removes the item from the receiver at the given
+ * zero-relative index.
+ *
+ * @param index the index for the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int index) {
+    checkWidget ();
+    TCHAR buffer = null;
+    if ((style & DWT.H_SCROLL) !is 0) {
+        int length = OS.SendMessage (handle, OS.LB_GETTEXTLEN, index, 0);
+        if (length is OS.LB_ERR) {
+            int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+            if (0 <= index && index < count) error (DWT.ERROR_ITEM_NOT_REMOVED);
+            error (DWT.ERROR_INVALID_RANGE);
+        }
+        buffer = new TCHAR (getCodePage (), length + 1);
+        int result = OS.SendMessage (handle, OS.LB_GETTEXT, index, buffer);
+        if (result is OS.LB_ERR) {
+            int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+            if (0 <= index && index < count) error (DWT.ERROR_ITEM_NOT_REMOVED);
+            error (DWT.ERROR_INVALID_RANGE);
+        }
+    }
+    int topIndex = OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0);
+    int result = OS.SendMessage (handle, OS.LB_DELETESTRING, index, 0);
+    if (result is OS.LB_ERR) {
+        int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+        if (0 <= index && index < count) error (DWT.ERROR_ITEM_NOT_REMOVED);
+        error (DWT.ERROR_INVALID_RANGE);
+    }
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth (buffer, false);
+    if (index < topIndex) {
+        OS.SendMessage (handle, OS.LB_SETTOPINDEX, topIndex - 1, 0);
+    }
+}
+
+/**
+ * Removes the items from the receiver which are
+ * between the given zero-relative start and end
+ * indices (inclusive).
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int start, int end) {
+    checkWidget ();
+    if (start > end) return;
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (!(0 <= start && start <= end && end < count)) {
+        error (DWT.ERROR_INVALID_RANGE);
+    }
+    if (start is 0 && end is count - 1) {
+        removeAll ();
+        return;
+    }
+    int topIndex = OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0);
+    RECT rect = null;
+    int hDC = 0, oldFont = 0, newFont = 0, newWidth = 0;
+    if ((style & DWT.H_SCROLL) !is 0) {
+        rect = new RECT ();
+        hDC = OS.GetDC (handle);
+        newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    }
+    int cp = getCodePage ();
+    int index = start;
+    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+    while (index <= end) {
+        TCHAR buffer = null;
+        if ((style & DWT.H_SCROLL) !is 0) {
+            int length = OS.SendMessage (handle, OS.LB_GETTEXTLEN, start, 0);
+            if (length is OS.LB_ERR) break;
+            buffer = new TCHAR (cp, length + 1);
+            int result = OS.SendMessage (handle, OS.LB_GETTEXT, start, buffer);
+            if (result is OS.LB_ERR) break;
+        }
+        int result = OS.SendMessage (handle, OS.LB_DELETESTRING, start, 0);
+        if (result is OS.LB_ERR) break;
+        if ((style & DWT.H_SCROLL) !is 0) {
+            OS.DrawText (hDC, buffer, -1, rect, flags);
+            newWidth = Math.max (newWidth, rect.right - rect.left);
+        }
+        index++;
+    }
+    if ((style & DWT.H_SCROLL) !is 0) {
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+        setScrollWidth (newWidth, false);
+    }
+    if (end < topIndex) {
+        topIndex -= end - start + 1;
+        OS.SendMessage (handle, OS.LB_SETTOPINDEX, topIndex, 0);
+    }
+    if (index <= end) error (DWT.ERROR_ITEM_NOT_REMOVED);
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * until an item is found that is equal to the argument,
+ * and removes that item from the list.
+ *
+ * @param string the item to remove
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int index = indexOf (string, 0);
+    if (index is -1) error (DWT.ERROR_INVALID_ARGUMENT);
+    remove (index);
+}
+
+/**
+ * Removes all of the items from the receiver.
+ * <p>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void removeAll () {
+    checkWidget ();
+    OS.SendMessage (handle, OS.LB_RESETCONTENT, 0, 0);
+    if ((style & DWT.H_SCROLL) !is 0) {
+        OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, 0, 0);
+    }
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's selection.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Selects the items at the given zero-relative indices in the receiver.
+ * The current selection is not cleared before the new items are selected.
+ * <p>
+ * If the item at a given index is not selected, it is selected.
+ * If the item at a given index was already selected, it remains selected.
+ * Indices that are out of range and duplicate indices are ignored.
+ * If the receiver is single-select and multiple indices are specified,
+ * then all indices are ignored.
+ *
+ * @param indices the array of indices for the items to select
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see List#setSelection(int[])
+ */
+public void select (int [] indices) {
+    checkWidget ();
+    if (indices is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int length = indices.length;
+    if (length is 0 || ((style & DWT.SINGLE) !is 0 && length > 1)) return;
+    select (indices, false);
+}
+
+void select (int [] indices, bool scroll) {
+    int i = 0;
+    while (i < indices.length) {
+        int index = indices [i];
+        if (index !is -1) {
+            select (index, false);
+        }
+        i++;
+    }
+    if (scroll) showSelection ();
+}
+
+/**
+ * Selects the item at the given zero-relative index in the receiver's
+ * list.  If the item at the index was already selected, it remains
+ * selected. Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to select
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void select (int index) {
+    checkWidget ();
+    select (index, false);
+}
+
+void select (int index, bool scroll) {
+    if (index < 0) return;
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (index >= count) return;
+    if (scroll) {
+        if ((style & DWT.SINGLE) !is 0) {
+            OS.SendMessage (handle, OS.LB_SETCURSEL, index, 0);
+        } else {
+            OS.SendMessage (handle, OS.LB_SETSEL, 1, index);
+        }
+        return;
+    }
+    int topIndex = OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0);
+    RECT itemRect = new RECT (), selectedRect = null;
+    OS.SendMessage (handle, OS.LB_GETITEMRECT, index, itemRect);
+    bool redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+    if (redraw) {
+        OS.UpdateWindow (handle);
+        OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+    }
+    int focusIndex = -1;
+    if ((style & DWT.SINGLE) !is 0) {
+        int oldIndex = OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0);
+        if (oldIndex !is -1) {
+            selectedRect = new RECT ();
+            OS.SendMessage (handle, OS.LB_GETITEMRECT, oldIndex, selectedRect);
+        }
+        OS.SendMessage (handle, OS.LB_SETCURSEL, index, 0);
+    } else {
+        focusIndex = OS.SendMessage (handle, OS.LB_GETCARETINDEX, 0, 0);
+        OS.SendMessage (handle, OS.LB_SETSEL, 1, index);
+    }
+    if ((style & DWT.MULTI) !is 0) {
+        if (focusIndex !is -1) {
+            OS.SendMessage (handle, OS.LB_SETCARETINDEX, focusIndex, 0);
+        }
+    }
+    OS.SendMessage (handle, OS.LB_SETTOPINDEX, topIndex, 0);
+    if (redraw) {
+        OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+        OS.ValidateRect (handle, null);
+        OS.InvalidateRect (handle, itemRect, true);
+        if (selectedRect !is null) {
+            OS.InvalidateRect (handle, selectedRect, true);
+        }
+    }
+}
+
+/**
+ * Selects the items in the range specified by the given zero-relative
+ * indices in the receiver. The range of indices is inclusive.
+ * The current selection is not cleared before the new items are selected.
+ * <p>
+ * If an item in the given range is not selected, it is selected.
+ * If an item in the given range was already selected, it remains selected.
+ * Indices that are out of range are ignored and no items will be selected
+ * if start is greater than end.
+ * If the receiver is single-select and there is more than one item in the
+ * given range, then all indices are ignored.
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see List#setSelection(int,int)
+ */
+public void select (int start, int end) {
+    checkWidget ();
+    if (end < 0 || start > end || ((style & DWT.SINGLE) !is 0 && start !is end)) return;
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (count is 0 || start >= count) return;
+    start = Math.max (0, start);
+    end = Math.min (end, count - 1);
+    if ((style & DWT.SINGLE) !is 0) {
+        select (start, false);
+    } else {
+        select (start, end, false);
+    }
+}
+
+void select (int start, int end, bool scroll) {
+    /*
+    * Note that when start = end, LB_SELITEMRANGEEX
+    * deselects the item.
+    */
+    if (start is end) {
+        select (start, scroll);
+        return;
+    }
+    OS.SendMessage (handle, OS.LB_SELITEMRANGEEX, start, end);
+    if (scroll) showSelection ();
+}
+
+/**
+ * Selects all of the items in the receiver.
+ * <p>
+ * If the receiver is single-select, do nothing.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void selectAll () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) return;
+    OS.SendMessage (handle, OS.LB_SETSEL, 1, -1);
+}
+
+void setFocusIndex (int index) {
+//  checkWidget ();
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (!(0 <= index && index < count)) return;
+    OS.SendMessage (handle, OS.LB_SETCARETINDEX, index, 0);
+}
+
+public void setFont (Font font) {
+    checkWidget ();
+    super.setFont (font);
+    if ((style & DWT.H_SCROLL) !is 0) setScrollWidth ();
+}
+
+/**
+ * Sets the text of the item in the receiver's list at the given
+ * zero-relative index to the string argument.
+ *
+ * @param index the index for the item
+ * @param string the new text for the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setItem (int index, String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int topIndex = getTopIndex ();
+    bool isSelected = isSelected (index);
+    remove (index);
+    add (string, index);
+    if (isSelected) select (index, false);
+    setTopIndex (topIndex);
+}
+
+/**
+ * Sets the receiver's items to be the given array of items.
+ *
+ * @param items the array of items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the items array is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setItems (String [] items) {
+    checkWidget ();
+    if (items is null) error (DWT.ERROR_NULL_ARGUMENT);
+    for (int i=0; i<items.length; i++) {
+        if (items [i] is null) error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC);
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, ListProc);
+    bool redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+    if (redraw) {
+        OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+    }
+    RECT rect = null;
+    int hDC = 0, oldFont = 0, newFont = 0, newWidth = 0;
+    if ((style & DWT.H_SCROLL) !is 0) {
+        rect = new RECT ();
+        hDC = OS.GetDC (handle);
+        newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+        OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, 0, 0);
+    }
+    int length = items.length;
+    OS.SendMessage (handle, OS.LB_RESETCONTENT, 0, 0);
+    OS.SendMessage (handle, OS.LB_INITSTORAGE, length, length * 32);
+    int index = 0;
+    int cp = getCodePage ();
+    while (index < length) {
+        String string = items [index];
+        TCHAR buffer = new TCHAR (cp, string, true);
+        int result = OS.SendMessage (handle, OS.LB_ADDSTRING, 0, buffer);
+        if (result is OS.LB_ERR || result is OS.LB_ERRSPACE) break;
+        if ((style & DWT.H_SCROLL) !is 0) {
+            int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+            OS.DrawText (hDC, buffer, -1, rect, flags);
+            newWidth = Math.max (newWidth, rect.right - rect.left);
+        }
+        index++;
+    }
+    if ((style & DWT.H_SCROLL) !is 0) {
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+        OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, newWidth + INSET, 0);
+    }
+    if (redraw) {
+        OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+        /*
+        * This code is intentionally commented.  The window proc
+        * for the list box implements WM_SETREDRAW to invalidate
+        * and erase the widget.  This is undocumented behavior.
+        * The commented code below shows what is actually happening
+        * and reminds us that we are relying on this undocumented
+        * behavior.
+        */
+//      int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
+//      OS.RedrawWindow (handle, null, 0, flags);
+    }
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc);
+    if (index < items.length) error (DWT.ERROR_ITEM_NOT_ADDED);
+}
+
+void setScrollWidth () {
+    int newWidth = 0;
+    RECT rect = new RECT ();
+    int newFont, oldFont = 0;
+    int hDC = OS.GetDC (handle);
+    newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    int cp = getCodePage ();
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+    for (int i=0; i<count; i++) {
+        int length = OS.SendMessage (handle, OS.LB_GETTEXTLEN, i, 0);
+        if (length !is OS.LB_ERR) {
+            TCHAR buffer = new TCHAR (cp, length + 1);
+            int result = OS.SendMessage (handle, OS.LB_GETTEXT, i, buffer);
+            if (result !is OS.LB_ERR) {
+                OS.DrawText (hDC, buffer, -1, rect, flags);
+                newWidth = Math.max (newWidth, rect.right - rect.left);
+            }
+        }
+    }
+    if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+    OS.ReleaseDC (handle, hDC);
+    OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, newWidth + INSET, 0);
+}
+
+void setScrollWidth (TCHAR buffer, bool grow) {
+    RECT rect = new RECT ();
+    int newFont, oldFont = 0;
+    int hDC = OS.GetDC (handle);
+    newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+    OS.DrawText (hDC, buffer, -1, rect, flags);
+    if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+    OS.ReleaseDC (handle, hDC);
+    setScrollWidth (rect.right - rect.left, grow);
+}
+
+void setScrollWidth (int newWidth, bool grow) {
+    newWidth += INSET;
+    int width = OS.SendMessage (handle, OS.LB_GETHORIZONTALEXTENT, 0, 0);
+    if (grow) {
+        if (newWidth <= width) return;
+        OS.SendMessage (handle, OS.LB_SETHORIZONTALEXTENT, newWidth, 0);
+    } else {
+        if (newWidth < width) return;
+        setScrollWidth ();
+    }
+}
+
+/**
+ * Selects the items at the given zero-relative indices in the receiver.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Indices that are out of range and duplicate indices are ignored.
+ * If the receiver is single-select and multiple indices are specified,
+ * then all indices are ignored.
+ *
+ * @param indices the indices of the items to select
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see List#deselectAll()
+ * @see List#select(int[])
+ */
+public void setSelection(int [] indices) {
+    checkWidget ();
+    if (indices is null) error (DWT.ERROR_NULL_ARGUMENT);
+    deselectAll ();
+    int length = indices.length;
+    if (length is 0 || ((style & DWT.SINGLE) !is 0 && length > 1)) return;
+    select (indices, true);
+    if ((style & DWT.MULTI) !is 0) {
+        int focusIndex = indices [0];
+        if (focusIndex >= 0) setFocusIndex (focusIndex);
+    }
+}
+
+/**
+ * Sets the receiver's selection to be the given array of items.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Items that are not in the receiver are ignored.
+ * If the receiver is single-select and multiple items are specified,
+ * then all items are ignored.
+ *
+ * @param items the array of items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see List#deselectAll()
+ * @see List#select(int[])
+ * @see List#setSelection(int[])
+ */
+public void setSelection (String [] items) {
+    checkWidget ();
+    if (items is null) error (DWT.ERROR_NULL_ARGUMENT);
+    deselectAll ();
+    int length = items.length;
+    if (length is 0 || ((style & DWT.SINGLE) !is 0 && length > 1)) return;
+    int focusIndex = -1;
+    for (int i=length-1; i>=0; --i) {
+        String string = items [i];
+        int index = 0;
+        if (string !is null) {
+            int localFocus = -1;
+            while ((index = indexOf (string, index)) !is -1) {
+                if (localFocus is -1) localFocus = index;
+                select (index, false);
+                if ((style & DWT.SINGLE) !is 0 && isSelected (index)) {
+                    showSelection ();
+                    return;
+                }
+                index++;
+            }
+            if (localFocus !is -1) focusIndex = localFocus;
+        }
+    }
+    if ((style & DWT.MULTI) !is 0) {
+        if (focusIndex >= 0) setFocusIndex (focusIndex);
+    }
+}
+
+/**
+ * Selects the item at the given zero-relative index in the receiver.
+ * If the item at the index was already selected, it remains selected.
+ * The current selection is first cleared, then the new item is selected.
+ * Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to select
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @see List#deselectAll()
+ * @see List#select(int)
+ */
+public void setSelection (int index) {
+    checkWidget ();
+    deselectAll ();
+    select (index, true);
+    if ((style & DWT.MULTI) !is 0) {
+        if (index >= 0) setFocusIndex (index);
+    }
+}
+
+/**
+ * Selects the items in the range specified by the given zero-relative
+ * indices in the receiver. The range of indices is inclusive.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Indices that are out of range are ignored and no items will be selected
+ * if start is greater than end.
+ * If the receiver is single-select and there is more than one item in the
+ * given range, then all indices are ignored.
+ *
+ * @param start the start index of the items to select
+ * @param end the end index of the items to select
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see List#deselectAll()
+ * @see List#select(int,int)
+ */
+public void setSelection (int start, int end) {
+    checkWidget ();
+    deselectAll ();
+    if (end < 0 || start > end || ((style & DWT.SINGLE) !is 0 && start !is end)) return;
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (count is 0 || start >= count) return;
+    start = Math.max (0, start);
+    end = Math.min (end, count - 1);
+    if ((style & DWT.SINGLE) !is 0) {
+        select (start, true);
+    } else {
+        select (start, end, true);
+        setFocusIndex (start);
+    }
+}
+
+/**
+ * Sets the zero-relative index of the item which is currently
+ * at the top of the receiver. This index can change when items
+ * are scrolled or new items are added and removed.
+ *
+ * @param index the index of the top item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setTopIndex (int index) {
+    checkWidget ();
+    int result = OS.SendMessage (handle, OS.LB_SETTOPINDEX, index, 0);
+    if (result is OS.LB_ERR) {
+        int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+        index = Math.min (count - 1, Math.max (0, index));
+        OS.SendMessage (handle, OS.LB_SETTOPINDEX, index, 0);
+    }
+}
+
+/**
+ * Shows the selection.  If the selection is already showing in the receiver,
+ * this method simply returns.  Otherwise, the items are scrolled until
+ * the selection is visible.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void showSelection () {
+    checkWidget ();
+    int index;
+    if ((style & DWT.SINGLE) !is 0) {
+        index = OS.SendMessage (handle, OS.LB_GETCURSEL, 0, 0);
+    } else {
+        int [] indices = new int [1];
+        int result = OS.SendMessage (handle, OS.LB_GETSELITEMS, 1, indices);
+        index = indices [0];
+        if (result !is 1) index = -1;
+    }
+    if (index is -1) return;
+    int count = OS.SendMessage (handle, OS.LB_GETCOUNT, 0, 0);
+    if (count is 0) return;
+    int height = OS.SendMessage (handle, OS.LB_GETITEMHEIGHT, 0, 0);
+    forceResize ();
+    RECT rect = new RECT ();
+    OS.GetClientRect (handle, rect);
+    int topIndex = OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0);
+    int visibleCount = Math.max (rect.bottom / height, 1);
+    int bottomIndex = Math.min (topIndex + visibleCount, count) - 1;
+    if (topIndex <= index && index <= bottomIndex) return;
+    int newTop = Math.min (Math.max (index - (visibleCount / 2), 0), count - 1);
+    OS.SendMessage (handle, OS.LB_SETTOPINDEX, newTop, 0);
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.LBS_NOTIFY | OS.LBS_NOINTEGRALHEIGHT;
+    if ((style & DWT.SINGLE) !is 0) return bits;
+    if ((style & DWT.MULTI) !is 0) {
+        if ((style & DWT.SIMPLE) !is 0) return bits | OS.LBS_MULTIPLESEL;
+        return bits | OS.LBS_EXTENDEDSEL;
+    }
+    return bits;
+}
+
+TCHAR windowClass () {
+    return ListClass;
+}
+
+int windowProc () {
+    return ListProc;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  If the top index is changed while the
+    * list is being resized, Windows does not redraw properly
+    * when their is white space at the bottom of the control.
+    * The fix is to detect when the top index has changed and
+    * redraw the control.
+    *
+    * Bug in Windows.  If the receiver is scrolled horizontally
+    * and is resized, the list does not redraw properly.  The fix
+    * is to redraw the control when the horizontal scroll bar is
+    * not at the beginning.
+    */
+    int oldIndex = OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0);
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    if (!isDisposed ()) {
+        SCROLLINFO info = new SCROLLINFO ();
+        info.cbSize = SCROLLINFO.sizeof;
+        info.fMask = OS.SIF_POS;
+        if (OS.GetScrollInfo (handle, OS.SB_HORZ, info)) {
+            if (info.nPos !is 0) OS.InvalidateRect (handle, null, true);
+        }
+        int newIndex = OS.SendMessage (handle, OS.LB_GETTOPINDEX, 0, 0);
+        if (oldIndex !is newIndex) OS.InvalidateRect (handle, null, true);
+    }
+    return result;
+}
+
+LRESULT wmCommandChild (int wParam, int lParam) {
+    int code = wParam >> 16;
+    switch (code) {
+        case OS.LBN_SELCHANGE:
+            postEvent (DWT.Selection);
+            break;
+        case OS.LBN_DBLCLK:
+            postEvent (DWT.DefaultSelection);
+            break;
+    }
+    return super.wmCommandChild (wParam, lParam);
+}
+
+
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Listener.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Listener;
+
+import dwt.widgets.Event;
+
+/**
+ * Implementers of <code>Listener</code> provide a simple
+ * <code>handleEvent()</code> method that is used internally
+ * by DWT to dispatch events.
+ * <p>
+ * After creating an instance of a class that implements this interface
+ * it can be added to a widget using the
+ * <code>addListener(int eventType, Listener handler)</code> method and
+ * removed using the
+ * <code>removeListener (int eventType, Listener handler)</code> method.
+ * When the specified event occurs, <code>handleEvent(...)</code> will
+ * be sent to the instance.
+ * </p>
+ * <p>
+ * Classes which implement this interface are described within DWT as
+ * providing the <em>untyped listener</em> API. Typically, widgets will
+ * also provide a higher-level <em>typed listener</em> API, that is based
+ * on the standard <code>java.util.EventListener</code> pattern.
+ * </p>
+ * <p>
+ * Note that, since all internal DWT event dispatching is based on untyped
+ * listeners, it is simple to build subsets of DWT for use on memory
+ * constrained, small footprint devices, by removing the classes and
+ * methods which implement the typed listener API.
+ * </p>
+ *
+ * @see Widget#addListener
+ * @see java.util.EventListener
+ * @see dwt.events
+ */
+public interface Listener {
+
+/**
+ * Sent when an event that the receiver has registered for occurs.
+ *
+ * @param event the event which occurred
+ */
+void handleEvent (Event event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Menu.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1555 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Menu;
+
+import dwt.widgets.Widget;
+class Menu : Widget {
+    this( Widget, int );
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.HelpListener;
+import dwt.events.MenuListener;
+import dwt.graphics.Color;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.ImageList;
+import dwt.internal.win32.MENUBARINFO;
+import dwt.internal.win32.MENUINFO;
+import dwt.internal.win32.MENUITEMINFO;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SHMENUBARINFO;
+import dwt.internal.win32.TBBUTTON;
+import dwt.internal.win32.TBBUTTONINFO;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class are user interface objects that contain
+ * menu items.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>BAR, DROP_DOWN, POP_UP, NO_RADIO_GROUP</dd>
+ * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Help, Hide, Show </dd>
+ * </dl>
+ * <p>
+ * Note: Only one of BAR, DROP_DOWN and POP_UP may be specified.
+ * Only one of LEFT_TO_RIGHT or RIGHT_TO_LEFT may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class Menu extends Widget {
+    /**
+     * the handle to the OS 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 int handle;
+
+    int x, y, hBrush, hwndCB, id0, id1;
+    int foreground = -1, background = -1;
+    Image backgroundImage;
+    bool hasLocation;
+    MenuItem cascade;
+    Decorations parent;
+    ImageList imageList;
+
+    /* Resource ID for SHMENUBARINFO */
+    static final int ID_PPC = 100;
+
+    /* SmartPhone SoftKeyBar resource ids */
+    static final int ID_SPMM = 102;
+    static final int ID_SPBM = 103;
+    static final int ID_SPMB = 104;
+    static final int ID_SPBB = 105;
+    static final int ID_SPSOFTKEY0 = 106;
+    static final int ID_SPSOFTKEY1 = 107;
+
+/**
+ * Constructs a new instance of this class given its parent,
+ * and sets the style for the instance so that the instance
+ * will be a popup menu on the given parent's shell.
+ *
+ * @param parent a control which will be the parent of the new instance (cannot be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#POP_UP
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Menu (Control parent) {
+    this (checkNull (parent).menuShell (), DWT.POP_UP);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Decorations</code>) and a style value
+ * describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a decorations control which will be the parent of the new instance (cannot be null)
+ * @param style the style of menu to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#BAR
+ * @see DWT#DROP_DOWN
+ * @see DWT#POP_UP
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Menu (Decorations parent, int style) {
+    this (parent, checkStyle (style), 0);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Menu</code>) and sets the style
+ * for the instance so that the instance will be a drop-down
+ * menu on the given parent's parent.
+ *
+ * @param parentMenu a menu which will be the parent of the new instance (cannot be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#DROP_DOWN
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Menu (Menu parentMenu) {
+    this (checkNull (parentMenu).parent, DWT.DROP_DOWN);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>MenuItem</code>) and sets the style
+ * for the instance so that the instance will be a drop-down
+ * menu on the given parent's parent menu.
+ *
+ * @param parentItem a menu item which will be the parent of the new instance (cannot be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#DROP_DOWN
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Menu (MenuItem parentItem) {
+    this (checkNull (parentItem).parent);
+}
+
+Menu (Decorations parent, int style, int handle) {
+    super (parent, checkStyle (style));
+    this.parent = parent;
+    this.handle = handle;
+    /*
+    * Bug in IBM JVM 1.3.1.  For some reason, when the checkOrientation() is
+    * called from createWidget(), the JVM issues this error:
+    *
+    * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8)
+    *
+    * In addition, on Windows XP, a dialog appears with following error message,
+    * indicating that the problem may be in the JIT:
+    *
+    * AppName: java.exe  AppVer: 0.0.0.0     ModName: jitc.dll
+    * ModVer: 0.0.0.0    Offset: 000b6912
+    *
+    * The fix is to call checkOrientation() from here.
+    */
+    checkOrientation (parent);
+    createWidget ();
+}
+
+void _setVisible (bool visible) {
+    if ((style & (DWT.BAR | DWT.DROP_DOWN)) !is 0) return;
+    int hwndParent = parent.handle;
+    if (visible) {
+        int flags = OS.TPM_LEFTBUTTON;
+        if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) flags |= OS.TPM_RIGHTBUTTON;
+        if ((style & DWT.RIGHT_TO_LEFT) !is 0) flags |= OS.TPM_RIGHTALIGN;
+        if ((parent.style & DWT.MIRRORED) !is 0) {
+            flags &= ~OS.TPM_RIGHTALIGN;
+            if ((style & DWT.LEFT_TO_RIGHT) !is 0) flags |= OS.TPM_RIGHTALIGN;
+        }
+        int nX = x, nY = y;
+        if (!hasLocation) {
+            int pos = OS.GetMessagePos ();
+            nX = (short) (pos & 0xFFFF);
+            nY = (short) (pos >> 16);
+        }
+        /*
+        * Feature in Windows.  It is legal use TrackPopupMenu()
+        * to display an empty menu as long as menu items are added
+        * inside of WM_INITPOPUPMENU.  If no items are added, then
+        * TrackPopupMenu() fails and does not send an indication
+        * that the menu has been closed.  This is not strictly a
+        * bug but leads to unwanted behavior when application code
+        * assumes that every WM_INITPOPUPMENU will eventually result
+        * in a WM_MENUSELECT, wParam=0xFFFF0000, lParam=0 to indicate
+        * that the menu has been closed.  The fix is to detect the
+        * case when TrackPopupMenu() fails and the number of items in
+        * the menu is zero and issue a fake WM_MENUSELECT.
+        */
+        bool success = OS.TrackPopupMenu (handle, flags, nX, nY, 0, hwndParent, null);
+        if (!success && GetMenuItemCount (handle) is 0) {
+            OS.SendMessage (hwndParent, OS.WM_MENUSELECT, 0xFFFF0000, 0);
+        }
+    } else {
+        OS.SendMessage (hwndParent, OS.WM_CANCELMODE, 0, 0);
+    }
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when help events are generated for the control,
+ * by sending it one of the messages defined in the
+ * <code>HelpListener</code> interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see HelpListener
+ * @see #removeHelpListener
+ */
+public void addHelpListener (HelpListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Help, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when menus are hidden or shown, by sending it
+ * one of the messages defined in the <code>MenuListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MenuListener
+ * @see #removeMenuListener
+ */
+public void addMenuListener (MenuListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Hide,typedListener);
+    addListener (DWT.Show,typedListener);
+}
+
+static Control checkNull (Control control) {
+    if (control is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    return control;
+}
+
+static Menu checkNull (Menu menu) {
+    if (menu is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    return menu;
+}
+
+static MenuItem checkNull (MenuItem item) {
+    if (item is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    return item;
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.POP_UP, DWT.BAR, DWT.DROP_DOWN, 0, 0, 0);
+}
+
+void createHandle () {
+    if (handle !is 0) return;
+    if ((style & DWT.BAR) !is 0) {
+        if (OS.IsPPC) {
+            int hwndShell = parent.handle;
+            SHMENUBARINFO mbi = new SHMENUBARINFO ();
+            mbi.cbSize = SHMENUBARINFO.sizeof;
+            mbi.hwndParent = hwndShell;
+            mbi.dwFlags = OS.SHCMBF_HIDDEN;
+            mbi.nToolBarId = ID_PPC;
+            mbi.hInstRes = OS.GetLibraryHandle ();
+            bool success = OS.SHCreateMenuBar (mbi);
+            hwndCB = mbi.hwndMB;
+            if (!success) error (DWT.ERROR_NO_HANDLES);
+            /* Remove the item from the resource file */
+            OS.SendMessage (hwndCB, OS.TB_DELETEBUTTON, 0, 0);
+            return;
+        }
+        /*
+        * Note in WinCE SmartPhone.  The SoftBar contains only 2 items.
+        * An item can either be a menu or a button.
+        * DWT.BAR: creates a SoftBar with 2 menus
+        * DWT.BAR | DWT.BUTTON1: creates a SoftBar with 1 button
+        *    for button1, and a menu for button2
+        * DWT.BAR | DWT.BUTTON1 | DWT.BUTTON2: creates a SoftBar with
+        *    2 buttons
+        */
+        if (OS.IsSP) {
+            /* Determine type of menubar */
+            int nToolBarId;
+            if ((style & DWT.BUTTON1) !is 0) {
+                nToolBarId = ((style & DWT.BUTTON2) !is 0) ? ID_SPBB : ID_SPBM;
+            } else {
+                nToolBarId = ((style & DWT.BUTTON2) !is 0) ? ID_SPMB : ID_SPMM;
+            }
+
+            /* Create SHMENUBAR */
+            SHMENUBARINFO mbi = new SHMENUBARINFO ();
+            mbi.cbSize = SHMENUBARINFO.sizeof;
+            mbi.hwndParent = parent.handle;
+            mbi.dwFlags = OS.SHCMBF_HIDDEN;
+            mbi.nToolBarId = nToolBarId; /* as defined in .rc file */
+            mbi.hInstRes = OS.GetLibraryHandle ();
+            if (!OS.SHCreateMenuBar (mbi)) error (DWT.ERROR_NO_HANDLES);
+            hwndCB = mbi.hwndMB;
+
+            /*
+            * Feature on WinCE SmartPhone.  The SHCMBF_HIDDEN flag causes the
+            * SHMENUBAR to not be drawn. However the keyboard events still go
+            * through it.  The workaround is to also hide the SHMENUBAR with
+            * ShowWindow ().
+            */
+            OS.ShowWindow (hwndCB, OS.SW_HIDE);
+
+            TBBUTTONINFO info = new TBBUTTONINFO ();
+            info.cbSize = TBBUTTONINFO.sizeof;
+            info.dwMask = OS.TBIF_COMMAND;
+            MenuItem item;
+
+            /* Set first item */
+            if (nToolBarId is ID_SPMM || nToolBarId is ID_SPMB) {
+                int hMenu = OS.SendMessage (hwndCB, OS.SHCMBM_GETSUBMENU, 0, ID_SPSOFTKEY0);
+                /* Remove the item from the resource file */
+                OS.RemoveMenu (hMenu, 0, OS.MF_BYPOSITION);
+                Menu menu = new Menu (parent, DWT.DROP_DOWN, hMenu);
+                item = new MenuItem (this, menu, DWT.CASCADE, 0);
+            } else {
+                item = new MenuItem (this, null, DWT.PUSH, 0);
+            }
+            info.idCommand = id0 = item.id;
+            OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, ID_SPSOFTKEY0, info);
+
+            /* Set second item */
+            if (nToolBarId is ID_SPMM || nToolBarId is ID_SPBM) {
+                int hMenu = OS.SendMessage (hwndCB, OS.SHCMBM_GETSUBMENU, 0, ID_SPSOFTKEY1);
+                OS.RemoveMenu (hMenu, 0, OS.MF_BYPOSITION);
+                Menu menu = new Menu (parent, DWT.DROP_DOWN, hMenu);
+                item = new MenuItem (this, menu, DWT.CASCADE, 1);
+            } else {
+                item = new MenuItem (this, null, DWT.PUSH, 1);
+            }
+            info.idCommand = id1 = item.id;
+            OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, ID_SPSOFTKEY1, info);
+
+            /*
+            * Override the Back key.  For some reason, the owner of the menubar
+            * must be a Dialog or it won't receive the WM_HOTKEY message.  As
+            * a result, Shell on WinCE SP must use the class Dialog.
+            */
+            int dwMask = OS.SHMBOF_NODEFAULT | OS.SHMBOF_NOTIFY;
+            int lParam = dwMask << 16 | dwMask;
+            OS.SendMessage (hwndCB, OS.SHCMBM_OVERRIDEKEY, OS.VK_ESCAPE, lParam);
+            return;
+        }
+        handle = OS.CreateMenu ();
+        if (handle is 0) error (DWT.ERROR_NO_HANDLES);
+        if (OS.IsHPC) {
+            int hwndShell = parent.handle;
+            hwndCB = OS.CommandBar_Create (OS.GetModuleHandle (null), hwndShell, 1);
+            if (hwndCB is 0) error (DWT.ERROR_NO_HANDLES);
+            OS.CommandBar_Show (hwndCB, false);
+            OS.CommandBar_InsertMenubarEx (hwndCB, 0, handle, 0);
+            /*
+            * The command bar hosts the 'close' button when the window does not
+            * have a caption.
+            */
+            if ((parent.style & DWT.CLOSE) !is 0 && (parent.style & DWT.TITLE) is 0) {
+                OS.CommandBar_AddAdornments (hwndCB, 0, 0);
+            }
+        }
+    } else {
+        handle = OS.CreatePopupMenu ();
+        if (handle is 0) error (DWT.ERROR_NO_HANDLES);
+    }
+}
+
+void createItem (MenuItem item, int index) {
+    int count = GetMenuItemCount (handle);
+    if (!(0 <= index && index <= count)) error (DWT.ERROR_INVALID_RANGE);
+    display.addMenuItem (item);
+    bool success = false;
+    if ((OS.IsPPC || OS.IsSP) && hwndCB !is 0) {
+        if (OS.IsSP) return;
+        TBBUTTON lpButton = new TBBUTTON ();
+        lpButton.idCommand = item.id;
+        lpButton.fsStyle = (byte) OS.TBSTYLE_AUTOSIZE;
+        if ((item.style & DWT.CASCADE) !is 0) lpButton.fsStyle |= OS.TBSTYLE_DROPDOWN | 0x80;
+        if ((item.style & DWT.SEPARATOR) !is 0) lpButton.fsStyle = (byte) OS.BTNS_SEP;
+        lpButton.fsState = (byte) OS.TBSTATE_ENABLED;
+        lpButton.iBitmap = OS.I_IMAGENONE;
+        success = OS.SendMessage (hwndCB, OS.TB_INSERTBUTTON, index, lpButton) !is 0;
+    } else {
+        if (OS.IsWinCE) {
+            int uFlags = OS.MF_BYPOSITION;
+            TCHAR lpNewItem = null;
+            if ((item.style & DWT.SEPARATOR) !is 0) {
+                uFlags |= OS.MF_SEPARATOR;
+            } else {
+                lpNewItem = new TCHAR (0, " ", true);
+            }
+            success = OS.InsertMenu (handle, index, uFlags, item.id, lpNewItem);
+            if (success) {
+                MENUITEMINFO info = new MENUITEMINFO ();
+                info.cbSize = MENUITEMINFO.sizeof;
+                info.fMask = OS.MIIM_DATA;
+                info.dwItemData = item.id;
+                success = OS.SetMenuItemInfo (handle, index, true, info);
+            }
+        } else {
+            /*
+            * Bug in Windows.  For some reason, when InsertMenuItem()
+            * is used to insert an item without text, it is not possible
+            * to use SetMenuItemInfo() to set the text at a later time.
+            * The fix is to insert the item with some text.
+            *
+            * Feature in Windows.  When an empty string is used instead
+            * of a space and InsertMenuItem() is used to set a submenu
+            * before setting text to a non-empty string, the menu item
+            * becomes unexpectedly disabled.  The fix is to insert a
+            * space.
+            */
+            int hHeap = OS.GetProcessHeap ();
+            TCHAR buffer = new TCHAR (0, " ", true);
+            int byteCount = buffer.length () * TCHAR.sizeof;
+            int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+            OS.MoveMemory (pszText, buffer, byteCount);
+            MENUITEMINFO info = new MENUITEMINFO ();
+            info.cbSize = MENUITEMINFO.sizeof;
+            info.fMask = OS.MIIM_ID | OS.MIIM_TYPE | OS.MIIM_DATA;
+            info.wID = info.dwItemData = item.id;
+            info.fType = item.widgetStyle ();
+            info.dwTypeData = pszText;
+            success = OS.InsertMenuItem (handle, index, true, info);
+            if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+        }
+    }
+    if (!success) {
+        display.removeMenuItem (item);
+        error (DWT.ERROR_ITEM_NOT_ADDED);
+    }
+    redraw ();
+}
+
+void createWidget () {
+    /*
+    * Bug in IBM JVM 1.3.1.  For some reason, when the following code is called
+    * from this method, the JVM issues this error:
+    *
+    * JVM Exception 0x2 (subcode 0x0) occurred in thread "main" (TID:0x9F19D8)
+    *
+    * In addition, on Windows XP, a dialog appears with following error message,
+    * indicating that the problem may be in the JIT:
+    *
+    * AppName: java.exe  AppVer: 0.0.0.0     ModName: jitc.dll
+    * ModVer: 0.0.0.0    Offset: 000b6912
+    *
+    * The fix is to move the code to the caller of this method.
+    */
+//  checkOrientation (parent);
+    createHandle ();
+    parent.addMenu (this);
+}
+
+int defaultBackground () {
+    return OS.GetSysColor (OS.COLOR_MENU);
+}
+
+int defaultForeground () {
+    return OS.GetSysColor (OS.COLOR_MENUTEXT);
+}
+
+void destroyAccelerators () {
+    parent.destroyAccelerators ();
+}
+
+void destroyItem (MenuItem item) {
+    if (OS.IsWinCE) {
+        if ((OS.IsPPC || OS.IsSP) && hwndCB !is 0) {
+            if (OS.IsSP) {
+                redraw();
+                return;
+            }
+            int index = OS.SendMessage (hwndCB, OS.TB_COMMANDTOINDEX, item.id, 0);
+            if (OS.SendMessage (hwndCB, OS.TB_DELETEBUTTON, index, 0) is 0) {
+                error (DWT.ERROR_ITEM_NOT_REMOVED);
+            }
+            int count = OS.SendMessage (hwndCB, OS.TB_BUTTONCOUNT, 0, 0);
+            if (count is 0) {
+                if (imageList !is null) {
+                    OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0);
+                    display.releaseImageList (imageList);
+                    imageList = null;
+                }
+            }
+        } else {
+            int index = 0;
+            MENUITEMINFO info = new MENUITEMINFO ();
+            info.cbSize = MENUITEMINFO.sizeof;
+            info.fMask = OS.MIIM_DATA;
+            while (OS.GetMenuItemInfo (handle, index, true, info)) {
+                if (info.dwItemData is item.id) break;
+                index++;
+            }
+            if (info.dwItemData !is item.id) {
+                error (DWT.ERROR_ITEM_NOT_REMOVED);
+            }
+            if (!OS.DeleteMenu (handle, index, OS.MF_BYPOSITION)) {
+                error (DWT.ERROR_ITEM_NOT_REMOVED);
+            }
+        }
+    } else {
+        if (!OS.DeleteMenu (handle, item.id, OS.MF_BYCOMMAND)) {
+            error (DWT.ERROR_ITEM_NOT_REMOVED);
+        }
+    }
+    redraw ();
+}
+
+void destroyWidget () {
+    int hMenu = handle, hCB = hwndCB;
+    releaseHandle ();
+    if (OS.IsWinCE && hCB !is 0) {
+        OS.CommandBar_Destroy (hCB);
+    } else {
+        if (hMenu !is 0) OS.DestroyMenu (hMenu);
+    }
+}
+
+void fixMenus (Decorations newParent) {
+    MenuItem [] items = getItems ();
+    for (int i=0; i<items.length; i++) {
+        items [i].fixMenus (newParent);
+    }
+    parent.removeMenu (this);
+    newParent.addMenu (this);
+    this.parent = newParent;
+}
+
+/**
+ * Returns the receiver's background color.
+ *
+ * @return the background color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+/*public*/ Color getBackground () {
+    checkWidget ();
+    return Color.win32_new (display, background !is -1 ? background : defaultBackground ());
+}
+
+/**
+ * Returns the receiver's background image.
+ *
+ * @return the background image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+/*public*/ Image getBackgroundImage () {
+    checkWidget ();
+    return backgroundImage;
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent (or its display if its parent is null),
+ * unless the receiver is a menu or a shell. In this case, the
+ * location is relative to the display.
+ * <p>
+ * Note that the bounds of a menu or menu item are undefined when
+ * the menu is not visible.  This is because most platforms compute
+ * the bounds of a menu dynamically just before it is displayed.
+ * </p>
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+/*public*/ Rectangle getBounds () {
+    checkWidget ();
+    if (OS.IsWinCE) return new Rectangle (0, 0, 0, 0);
+    if ((style & DWT.BAR) !is 0) {
+        if (parent.menuBar !is this) {
+            return new Rectangle (0, 0, 0, 0);
+        }
+        int hwndShell = parent.handle;
+        MENUBARINFO info = new MENUBARINFO ();
+        info.cbSize = MENUBARINFO.sizeof;
+        if (OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, 0, info)) {
+            int width = info.right - info.left;
+            int height = info.bottom - info.top;
+            return new Rectangle (info.left, info.top, width, height);
+        }
+    } else {
+        int count = GetMenuItemCount (handle);
+        if (count !is 0) {
+            RECT rect1 = new RECT ();
+            if (OS.GetMenuItemRect (0, handle, 0, rect1)) {
+                RECT rect2 = new RECT ();
+                if (OS.GetMenuItemRect (0, handle, count - 1, rect2)) {
+                    int x = rect1.left - 2, y = rect1.top - 2;
+                    int width = (rect2.right - rect2.left) + 4;
+                    int height = (rect2.bottom - rect1.top) + 4;
+                    return new Rectangle (x, y, width, height);
+                }
+            }
+        }
+    }
+    return new Rectangle (0, 0, 0, 0);
+}
+
+/**
+ * Returns the default menu item or null if none has
+ * been previously set.
+ *
+ * @return the default menu item.
+ *
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public MenuItem getDefaultItem () {
+    checkWidget ();
+    if (OS.IsWinCE) return null;
+    int id = OS.GetMenuDefaultItem (handle, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED);
+    if (id is -1) return null;
+    MENUITEMINFO info = new MENUITEMINFO ();
+    info.cbSize = MENUITEMINFO.sizeof;
+    info.fMask = OS.MIIM_ID;
+    if (OS.GetMenuItemInfo (handle, id, false, info)) {
+        return display.getMenuItem (info.wID);
+    }
+    return null;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled, and
+ * <code>false</code> otherwise. A disabled menu is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #isEnabled
+ */
+public bool getEnabled () {
+    checkWidget ();
+    return (state & DISABLED) is 0;
+}
+
+/**
+ * Returns the foreground color that the receiver will use to draw.
+ *
+ * @return the receiver's foreground color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+/*public*/ Color getForeground () {
+    checkWidget ();
+    return Color.win32_new (display, foreground !is -1 ? foreground : defaultForeground ());
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public MenuItem getItem (int index) {
+    checkWidget ();
+    int id = 0;
+    if ((OS.IsPPC || OS.IsSP) && hwndCB !is 0) {
+        if (OS.IsPPC) {
+            TBBUTTON lpButton = new TBBUTTON ();
+            int result = OS.SendMessage (hwndCB, OS.TB_GETBUTTON, index, lpButton);
+            if (result is 0) error (DWT.ERROR_CANNOT_GET_ITEM);
+            id = lpButton.idCommand;
+        }
+        if (OS.IsSP) {
+            if (!(0 <= index && index <= 1)) error (DWT.ERROR_CANNOT_GET_ITEM);
+            id = index is 0 ? id0 : id1;
+        }
+    } else {
+        MENUITEMINFO info = new MENUITEMINFO ();
+        info.cbSize = MENUITEMINFO.sizeof;
+        info.fMask = OS.MIIM_DATA;
+        if (!OS.GetMenuItemInfo (handle, index, true, info)) {
+            error (DWT.ERROR_INVALID_RANGE);
+        }
+        id = info.dwItemData;
+    }
+    return display.getMenuItem (id);
+}
+
+/**
+ * Returns the number of items contained in the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    return GetMenuItemCount (handle);
+}
+
+/**
+ * Returns a (possibly empty) array of <code>MenuItem</code>s which
+ * are the items in the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public MenuItem [] getItems () {
+    checkWidget ();
+    if ((OS.IsPPC || OS.IsSP) && hwndCB !is 0) {
+        if (OS.IsSP) {
+            MenuItem [] result = new MenuItem [2];
+            result[0] = display.getMenuItem (id0);
+            result[1] = display.getMenuItem (id1);
+            return result;
+        }
+        int count = OS.SendMessage (hwndCB, OS.TB_BUTTONCOUNT, 0, 0);
+        TBBUTTON lpButton = new TBBUTTON ();
+        MenuItem [] result = new MenuItem [count];
+        for (int i=0; i<count; i++) {
+            OS.SendMessage (hwndCB, OS.TB_GETBUTTON, i, lpButton);
+            result [i] = display.getMenuItem (lpButton.idCommand);
+        }
+        return result;
+    }
+    int index = 0, count = 0;
+    int length = OS.IsWinCE ? 4 : OS.GetMenuItemCount (handle);
+    MenuItem [] items = new MenuItem [length];
+    MENUITEMINFO info = new MENUITEMINFO ();
+    info.cbSize = MENUITEMINFO.sizeof;
+    info.fMask = OS.MIIM_DATA;
+    while (OS.GetMenuItemInfo (handle, index, true, info)) {
+        if (count is items.length) {
+            MenuItem [] newItems = new MenuItem [count + 4];
+            System.arraycopy (items, 0, newItems, 0, count);
+            items = newItems;
+        }
+        MenuItem item = display.getMenuItem (info.dwItemData);
+        if (item !is null) items [count++] = item;
+        index++;
+    }
+    if (count is items.length) return items;
+    MenuItem [] result = new MenuItem [count];
+    System.arraycopy (items, 0, result, 0, count);
+    return result;
+}
+
+int GetMenuItemCount (int handle) {
+    if (OS.IsWinCE) {
+        if ((OS.IsPPC || OS.IsSP) && hwndCB !is 0) {
+            return OS.IsSP ? 2 : OS.SendMessage (hwndCB, OS.TB_BUTTONCOUNT, 0, 0);
+        }
+        int count = 0;
+        MENUITEMINFO info = new MENUITEMINFO ();
+        info.cbSize = MENUITEMINFO.sizeof;
+        while (OS.GetMenuItemInfo (handle, count, true, info)) count++;
+        return count;
+    }
+    return OS.GetMenuItemCount (handle);
+}
+
+String getNameText () {
+    String result = "";
+    MenuItem [] items = getItems ();
+    int length = items.length;
+    if (length > 0) {
+        for (int i=0; i<length-1; i++) {
+            result = result + items [i].getNameText() + ", ";
+        }
+        result = result + items [length-1].getNameText ();
+    }
+    return result;
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Decorations</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Decorations getParent () {
+    checkWidget ();
+    return parent;
+}
+
+/**
+ * Returns the receiver's parent item, which must be a
+ * <code>MenuItem</code> or null when the receiver is a
+ * root.
+ *
+ * @return the receiver's parent item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public MenuItem getParentItem () {
+    checkWidget ();
+    return cascade;
+}
+
+/**
+ * Returns the receiver's parent item, which must be a
+ * <code>Menu</code> or null when the receiver is a
+ * root.
+ *
+ * @return the receiver's parent item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Menu getParentMenu () {
+    checkWidget ();
+    if (cascade !is null) return cascade.parent;
+    return null;
+}
+
+/**
+ * Returns the receiver's shell. For all controls other than
+ * shells, this simply returns the control's nearest ancestor
+ * shell. Shells return themselves, even if they are children
+ * of other shells.
+ *
+ * @return the receiver's shell
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getParent
+ */
+public Shell getShell () {
+    checkWidget ();
+    return parent.getShell ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible, and
+ * <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getVisible () {
+    checkWidget ();
+    if ((style & DWT.BAR) !is 0) {
+        return this is parent.menuShell ().menuBar;
+    }
+    if ((style & DWT.POP_UP) !is 0) {
+        Menu [] popups = display.popups;
+        if (popups is null) return false;
+        for (int i=0; i<popups.length; i++) {
+            if (popups [i] is this) return true;
+        }
+    }
+    Shell shell = getShell ();
+    Menu menu = shell.activeMenu;
+    while (menu !is null && menu !is this) {
+        menu = menu.getParentMenu ();
+    }
+    return this is menu;
+}
+
+int imageIndex (Image image) {
+    if (hwndCB is 0 || image is null) return OS.I_IMAGENONE;
+    if (imageList is null) {
+        Rectangle bounds = image.getBounds ();
+        imageList = display.getImageList (style & DWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
+        int index = imageList.add (image);
+        int hImageList = imageList.getHandle ();
+        OS.SendMessage (hwndCB, OS.TB_SETIMAGELIST, 0, hImageList);
+        return index;
+    }
+    int index = imageList.indexOf (image);
+    if (index is -1) {
+        index = imageList.add (image);
+    } else {
+        imageList.put (index, image);
+    }
+    return index;
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (MenuItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (item.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    if (item.parent !is this) return -1;
+    if ((OS.IsPPC || OS.IsSP) && hwndCB !is 0) {
+        if (OS.IsPPC) {
+            return OS.SendMessage (hwndCB, OS.TB_COMMANDTOINDEX, item.id, 0);
+        }
+        if (OS.IsSP) {
+            if (item.id is id0) return 0;
+            if (item.id is id1) return 1;
+            return -1;
+        }
+    }
+    int index = 0;
+    MENUITEMINFO info = new MENUITEMINFO ();
+    info.cbSize = MENUITEMINFO.sizeof;
+    info.fMask = OS.MIIM_DATA;
+    while (OS.GetMenuItemInfo (handle, index, true, info)) {
+        if (info.dwItemData is item.id) return index;
+        index++;
+    }
+    return -1;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled and all
+ * of the receiver's ancestors are enabled, and <code>false</code>
+ * otherwise. A disabled menu is typically not selectable from the
+ * user interface and draws with an inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getEnabled
+ */
+public bool isEnabled () {
+    checkWidget ();
+    Menu parentMenu = getParentMenu ();
+    if (parentMenu is null) {
+        return getEnabled () && parent.isEnabled ();
+    }
+    return getEnabled () && parentMenu.isEnabled ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible and all
+ * of the receiver's ancestors are visible and <code>false</code>
+ * otherwise.
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getVisible
+ */
+public bool isVisible () {
+    checkWidget ();
+    return getVisible ();
+}
+
+void redraw () {
+    if (!isVisible ()) return;
+    if ((style & DWT.BAR) !is 0) {
+        display.addBar (this);
+    } else {
+        update ();
+    }
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    handle = hwndCB = 0;
+}
+
+void releaseChildren (bool destroy) {
+    MenuItem [] items = getItems ();
+    for (int i=0; i<items.length; i++) {
+        MenuItem item = items [i];
+        if (item !is null && !item.isDisposed ()) {
+            if (OS.IsPPC && hwndCB !is 0) {
+                item.dispose ();
+            } else {
+                item.release (false);
+            }
+        }
+    }
+    super.releaseChildren (destroy);
+}
+
+void releaseParent () {
+    super.releaseParent ();
+    if (cascade !is null) cascade.releaseMenu ();
+    if ((style & DWT.BAR) !is 0) {
+        display.removeBar (this);
+        if (this is parent.menuBar) {
+            parent.setMenuBar (null);
+        }
+    } else {
+        if ((style & DWT.POP_UP) !is 0) {
+            display.removePopup (this);
+        }
+    }
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    backgroundImage = null;
+    if (hBrush is 0) OS.DeleteObject (hBrush);
+    hBrush = 0;
+    if (OS.IsPPC && hwndCB !is 0) {
+        if (imageList !is null) {
+            OS.SendMessage (hwndCB, OS.TB_SETIMAGELIST, 0, 0);
+            display.releaseToolImageList (imageList);
+            imageList = null;
+        }
+    }
+    if (parent !is null) parent.removeMenu (this);
+    parent = null;
+    cascade = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the help events are generated for the control.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see HelpListener
+ * @see #addHelpListener
+ */
+public void removeHelpListener (HelpListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Help, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the menu events are generated for the control.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MenuListener
+ * @see #addMenuListener
+ */
+public void removeMenuListener (MenuListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Hide, listener);
+    eventTable.unhook (DWT.Show, listener);
+}
+
+/**
+ * Sets the receiver's background color to the color specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+/*public*/ void setBackground (Color color) {
+    checkWidget ();
+    int pixel = -1;
+    if (color !is null) {
+        if (color.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        pixel = color.handle;
+    }
+    if (pixel is background) return;
+    background = pixel;
+    updateBackground ();
+}
+
+/**
+ * Sets the receiver's background image to the image specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.  The background image is tiled to fill
+ * the available space.
+ *
+ * @param image the new image (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument is not a bitmap</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+/*public*/ void setBackgroundImage (Image image) {
+    checkWidget ();
+    if (image !is null) {
+        if (image.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+        if (image.type !is DWT.BITMAP) error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    if (backgroundImage is image) return;
+    backgroundImage = image;
+    updateBackground ();
+}
+
+/**
+ * Sets the receiver's foreground color to the color specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+/*public*/ void setForeground (Color color) {
+    checkWidget ();
+    int pixel = -1;
+    if (color !is null) {
+        if (color.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+        pixel = color.handle;
+    }
+    if (pixel is foreground) return;
+    foreground = pixel;
+    updateForeground ();
+}
+
+/**
+ * Sets the default menu item to the argument or removes
+ * the default emphasis when the argument is <code>null</code>.
+ *
+ * @param item the default menu item or null
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the menu item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setDefaultItem (MenuItem item) {
+    checkWidget ();
+    int newID = -1;
+    if (item !is null) {
+        if (item.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+        if (item.parent !is this) return;
+        newID = item.id;
+    }
+    if (OS.IsWinCE) return;
+    int oldID = OS.GetMenuDefaultItem (handle, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED);
+    if (newID is oldID) return;
+    OS.SetMenuDefaultItem (handle, newID, OS.MF_BYCOMMAND);
+    redraw ();
+}
+
+/**
+ * Enables the receiver if the argument is <code>true</code>,
+ * and disables it otherwise. A disabled menu is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ *
+ * @param enabled the new enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setEnabled (bool enabled) {
+    checkWidget ();
+    state &= ~DISABLED;
+    if (!enabled) state |= DISABLED;
+}
+
+/**
+ * Sets the location of the receiver, which must be a popup,
+ * to the point specified by the arguments which are relative
+ * to the display.
+ * <p>
+ * Note that this is different from most widgets where the
+ * location of the widget is relative to the parent.
+ * </p><p>
+ * Note that the platform window manager ultimately has control
+ * over the location of popup menus.
+ * </p>
+ *
+ * @param x the new x coordinate for the receiver
+ * @param y the new y coordinate for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLocation (int x, int y) {
+    checkWidget ();
+    if ((style & (DWT.BAR | DWT.DROP_DOWN)) !is 0) return;
+    this.x = x;
+    this.y = y;
+    hasLocation = true;
+}
+
+/**
+ * Sets the location of the receiver, which must be a popup,
+ * to the point specified by the argument which is relative
+ * to the display.
+ * <p>
+ * Note that this is different from most widgets where the
+ * location of the widget is relative to the parent.
+ * </p><p>
+ * Note that the platform window manager ultimately has control
+ * over the location of popup menus.
+ * </p>
+ *
+ * @param location the new location for the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public void setLocation (Point location) {
+    checkWidget ();
+    if (location is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    setLocation (location.x, location.y);
+}
+
+/**
+ * Marks the receiver as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param visible the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setVisible (bool visible) {
+    checkWidget ();
+    if ((style & (DWT.BAR | DWT.DROP_DOWN)) !is 0) return;
+    if (visible) {
+        display.addPopup (this);
+    } else {
+        display.removePopup (this);
+        _setVisible (false);
+    }
+}
+
+void update () {
+    if (OS.IsPPC || OS.IsSP) return;
+    if (OS.IsHPC) {
+        /*
+        * Each time a menu has been modified, the command menu bar
+        * must be redrawn or it won't update properly.  For example,
+        * a submenu will not drop down.
+        */
+        Menu menuBar = parent.menuBar;
+        if (menuBar !is null) {
+            Menu menu = this;
+            while (menu !is null && menu !is menuBar) {
+                menu = menu.getParentMenu ();
+            }
+            if (menu is menuBar) {
+                OS.CommandBar_DrawMenuBar (menuBar.hwndCB, 0);
+                OS.CommandBar_Show (menuBar.hwndCB, true);
+            }
+        }
+        return;
+    }
+    if (OS.IsWinCE) return;
+    if ((style & DWT.BAR) !is 0) {
+        if (this is parent.menuBar) OS.DrawMenuBar (parent.handle);
+        return;
+    }
+    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) {
+        return;
+    }
+    bool hasCheck = false, hasImage = false;
+    MenuItem [] items = getItems ();
+    for (int i=0; i<items.length; i++) {
+        MenuItem item = items [i];
+        if (item.image !is null) {
+            if ((hasImage = true) && hasCheck) break;
+        }
+        if ((item.style & (DWT.CHECK | DWT.RADIO)) !is 0) {
+            if ((hasCheck = true) && hasImage) break;
+        }
+    }
+
+    /*
+    * Bug in Windows.  If a menu contains items that have
+    * images and can be checked, Windows does not include
+    * the width of the image and the width of the check when
+    * computing the width of the menu.  When the longest item
+    * does not have an image, the label and the accelerator
+    * text can overlap.  The fix is to use SetMenuItemInfo()
+    * to indicate that all items have a bitmap and then include
+    * the width of the widest bitmap in WM_MEASURECHILD.
+    *
+    * NOTE:  This work around causes problems on Windows 98.
+    * Under certain circumstances that have yet to be isolated,
+    * some menus can become huge and blank.  For now, do not
+    * run the code on Windows 98.
+    *
+    * NOTE:  This work around doesn't run on Vista because
+    * WM_MEASURECHILD and WM_DRAWITEM cause Vista to lose
+    * the menu theme.
+    */
+    if (!OS.IsWin95) {
+        if (OS.WIN32_VERSION < OS.VERSION (6, 0)) {
+            MENUITEMINFO info = new MENUITEMINFO ();
+            info.cbSize = MENUITEMINFO.sizeof;
+            info.fMask = OS.MIIM_BITMAP;
+            for (int i=0; i<items.length; i++) {
+                MenuItem item = items [i];
+                if ((style & DWT.SEPARATOR) is 0) {
+                    if (item.image is null || foreground !is -1) {
+                        info.hbmpItem = hasImage || foreground !is -1 ? OS.HBMMENU_CALLBACK : 0;
+                        OS.SetMenuItemInfo (handle, item.id, false, info);
+                    }
+                }
+            }
+        }
+    }
+
+    /* Update the menu to hide or show the space for bitmaps */
+    MENUINFO lpcmi = new MENUINFO ();
+    lpcmi.cbSize = MENUINFO.sizeof;
+    lpcmi.fMask = OS.MIM_STYLE;
+    OS.GetMenuInfo (handle, lpcmi);
+    if (hasImage && !hasCheck) {
+        lpcmi.dwStyle |= OS.MNS_CHECKORBMP;
+    } else {
+        lpcmi.dwStyle &= ~OS.MNS_CHECKORBMP;
+    }
+    OS.SetMenuInfo (handle, lpcmi);
+}
+
+void updateBackground () {
+    if (hBrush is 0) OS.DeleteObject (hBrush);
+    hBrush = 0;
+    if (backgroundImage !is null) {
+        hBrush = OS.CreatePatternBrush (backgroundImage.handle);
+    } else {
+        if (background !is -1) hBrush = OS.CreateSolidBrush (background);
+    }
+    MENUINFO lpcmi = new MENUINFO ();
+    lpcmi.cbSize = MENUINFO.sizeof;
+    lpcmi.fMask = OS.MIM_BACKGROUND;
+    lpcmi.hbrBack = hBrush;
+    OS.SetMenuInfo (handle, lpcmi);
+}
+
+void updateForeground () {
+    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return;
+    MENUITEMINFO info = new MENUITEMINFO ();
+    info.cbSize = MENUITEMINFO.sizeof;
+    int index = 0;
+    while (OS.GetMenuItemInfo (handle, index, true, info)) {
+        info.fMask = OS.MIIM_BITMAP;
+        info.hbmpItem = OS.HBMMENU_CALLBACK;
+        OS.SetMenuItemInfo (handle, index, true, info);
+        index++;
+    }
+    redraw ();
+}
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/MenuItem.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1221 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.MenuItem;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+class MenuItem : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.ArmListener;
+import dwt.events.HelpListener;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Image;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.ACCEL;
+import dwt.internal.win32.DRAWITEMSTRUCT;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MEASUREITEMSTRUCT;
+import dwt.internal.win32.MENUBARINFO;
+import dwt.internal.win32.MENUINFO;
+import dwt.internal.win32.MENUITEMINFO;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TBBUTTONINFO;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class represent a selectable user interface object
+ * that issues notification when pressed and released.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>CHECK, CASCADE, PUSH, RADIO, SEPARATOR</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Arm, Help, Selection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles CHECK, CASCADE, PUSH, RADIO and SEPARATOR
+ * may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class MenuItem extends Item {
+    Menu parent, menu;
+    int hBitmap, id, accelerator;
+    /*
+    * Feature in Windows.  On Windows 98, it is necessary
+    * to add 4 pixels to the width of the image or the image
+    * and text are too close.  On other Windows platforms,
+    * this causes the text of the longest item to touch the
+    * accelerator text.  The fix is to use smaller margins
+    * everywhere but on Windows 98.
+    */
+    final static int MARGIN_WIDTH = OS.IsWin95 ? 2 : 1;
+    final static int MARGIN_HEIGHT = OS.IsWin95 ? 2 : 1;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Menu</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a menu control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#CHECK
+ * @see DWT#CASCADE
+ * @see DWT#PUSH
+ * @see DWT#RADIO
+ * @see DWT#SEPARATOR
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public MenuItem (Menu parent, int style) {
+    super (parent, checkStyle (style));
+    this.parent = parent;
+    parent.createItem (this, parent.getItemCount ());
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Menu</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a menu control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#CHECK
+ * @see DWT#CASCADE
+ * @see DWT#PUSH
+ * @see DWT#RADIO
+ * @see DWT#SEPARATOR
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public MenuItem (Menu parent, int style, int index) {
+    super (parent, checkStyle (style));
+    this.parent = parent;
+    parent.createItem (this, index);
+}
+
+MenuItem (Menu parent, Menu menu, int style, int index) {
+    super (parent, checkStyle (style));
+    this.parent = parent;
+    this.menu = menu;
+    if (menu !is null) menu.cascade = this;
+    display.addMenuItem (this);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the arm events are generated for the control, by sending
+ * it one of the messages defined in the <code>ArmListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ArmListener
+ * @see #removeArmListener
+ */
+public void addArmListener (ArmListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Arm, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the help events are generated for the control, by sending
+ * it one of the messages defined in the <code>HelpListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see HelpListener
+ * @see #removeHelpListener
+ */
+public void addHelpListener (HelpListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Help, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the menu item is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the stateMask field of the event object is valid.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the menu item is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener(listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.PUSH, DWT.CHECK, DWT.RADIO, DWT.SEPARATOR, DWT.CASCADE, 0);
+}
+
+void destroyWidget () {
+    parent.destroyItem (this);
+    releaseHandle ();
+}
+
+void fillAccel (ACCEL accel) {
+    accel.fVirt = 0;
+    accel.cmd = accel.key = 0;
+    if (accelerator is 0 || !getEnabled ()) return;
+    int fVirt = OS.FVIRTKEY;
+    int key = accelerator & DWT.KEY_MASK;
+    int vKey = Display.untranslateKey (key);
+    if (vKey !is 0) {
+        key = vKey;
+    } else {
+        switch (key) {
+            /*
+            * Bug in Windows.  For some reason, VkKeyScan
+            * fails to map ESC to VK_ESCAPE and DEL to
+            * VK_DELETE.  The fix is to map these keys
+            * as a special case.
+            */
+            case 27: key = OS.VK_ESCAPE; break;
+            case 127: key = OS.VK_DELETE; break;
+            default: {
+                key = Display.wcsToMbcs ((char) key);
+                if (key is 0) return;
+                if (OS.IsWinCE) {
+                    key = OS.CharUpper ((short) key);
+                } else {
+                    vKey = OS.VkKeyScan ((short) key) & 0xFF;
+                    if (vKey is -1) {
+                        fVirt = 0;
+                    } else {
+                        key = vKey;
+                    }
+                }
+            }
+        }
+    }
+    accel.key = (short) key;
+    accel.cmd = (short) id;
+    accel.fVirt = (byte) fVirt;
+    if ((accelerator & DWT.ALT) !is 0) accel.fVirt |= OS.FALT;
+    if ((accelerator & DWT.SHIFT) !is 0) accel.fVirt |= OS.FSHIFT;
+    if ((accelerator & DWT.CONTROL) !is 0) accel.fVirt |= OS.FCONTROL;
+}
+
+void fixMenus (Decorations newParent) {
+    if (menu !is null) menu.fixMenus (newParent);
+}
+
+/**
+ * Returns the widget accelerator.  An accelerator is the bit-wise
+ * OR of zero or more modifier masks and a key. Examples:
+ * <code>DWT.CONTROL | DWT.SHIFT | 'T', DWT.ALT | DWT.F2</code>.
+ * The default value is zero, indicating that the menu item does
+ * not have an accelerator.
+ *
+ * @return the accelerator or 0
+ *
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getAccelerator () {
+    checkWidget ();
+    return accelerator;
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent (or its display if its parent is null).
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+/*public*/ Rectangle getBounds () {
+    checkWidget ();
+    if (OS.IsWinCE) return new Rectangle (0, 0, 0, 0);
+    int index = parent.indexOf (this);
+    if (index is -1) return new Rectangle (0, 0, 0, 0);
+    if ((parent.style & DWT.BAR) !is 0) {
+        Decorations shell = parent.parent;
+        if (shell.menuBar !is parent) {
+            return new Rectangle (0, 0, 0, 0);
+        }
+        int hwndShell = shell.handle;
+        MENUBARINFO info1 = new MENUBARINFO ();
+        info1.cbSize = MENUBARINFO.sizeof;
+        if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, 1, info1)) {
+            return new Rectangle (0, 0, 0, 0);
+        }
+        MENUBARINFO info2 = new MENUBARINFO ();
+        info2.cbSize = MENUBARINFO.sizeof;
+        if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, index + 1, info2)) {
+            return new Rectangle (0, 0, 0, 0);
+        }
+        int x = info2.left - info1.left;
+        int y = info2.top - info1.top;
+        int width = info2.right - info2.left;
+        int height = info2.bottom - info2.top;
+        return new Rectangle (x, y, width, height);
+    } else {
+        int hMenu = parent.handle;
+        RECT rect1 = new RECT ();
+        if (!OS.GetMenuItemRect (0, hMenu, 0, rect1)) {
+            return new Rectangle (0, 0, 0, 0);
+        }
+        RECT rect2 = new RECT ();
+        if (!OS.GetMenuItemRect (0, hMenu, index, rect2)) {
+            return new Rectangle (0, 0, 0, 0);
+        }
+        int x = rect2.left - rect1.left + 2;
+        int y = rect2.top - rect1.top + 2;
+        int width = rect2.right - rect2.left;
+        int height = rect2.bottom - rect2.top;
+        return new Rectangle (x, y, width, height);
+    }
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled, and
+ * <code>false</code> otherwise. A disabled menu item is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #isEnabled
+ */
+public bool getEnabled () {
+    checkWidget ();
+    if ((OS.IsPPC || OS.IsSP) && parent.hwndCB !is 0) {
+        int hwndCB = parent.hwndCB;
+        TBBUTTONINFO info = new TBBUTTONINFO ();
+        info.cbSize = TBBUTTONINFO.sizeof;
+        info.dwMask = OS.TBIF_STATE;
+        OS.SendMessage (hwndCB, OS.TB_GETBUTTONINFO, id, info);
+        return (info.fsState & OS.TBSTATE_ENABLED) !is 0;
+    }
+    int hMenu = parent.handle;
+    MENUITEMINFO info = new MENUITEMINFO ();
+    info.cbSize = MENUITEMINFO.sizeof;
+    info.fMask = OS.MIIM_STATE;
+    bool success;
+    if (OS.IsWinCE) {
+        int index = parent.indexOf (this);
+        if (index is -1) error (DWT.ERROR_CANNOT_GET_ENABLED);
+        success = OS.GetMenuItemInfo (hMenu, index, true, info);
+    } else {
+        success = OS.GetMenuItemInfo (hMenu, id, false, info);
+    }
+    if (!success) error (DWT.ERROR_CANNOT_GET_ENABLED);
+    return (info.fState & (OS.MFS_DISABLED | OS.MFS_GRAYED)) is 0;
+}
+
+/**
+ * Returns the receiver's cascade menu if it has one or null
+ * if it does not. Only <code>CASCADE</code> menu items can have
+ * a pull down menu. The sequence of key strokes, button presses
+ * and/or button releases that are used to request a pull down
+ * menu is platform specific.
+ *
+ * @return the receiver's menu
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Menu getMenu () {
+    checkWidget ();
+    return menu;
+}
+
+String getNameText () {
+    if ((style & DWT.SEPARATOR) !is 0) return "|";
+    return super.getNameText ();
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Menu</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Menu getParent () {
+    checkWidget ();
+    return parent;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is selected,
+ * and false otherwise.
+ * <p>
+ * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
+ * it is selected when it is checked.
+ *
+ * @return the selection state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getSelection () {
+    checkWidget ();
+    if ((style & (DWT.CHECK | DWT.RADIO)) is 0) return false;
+    if ((OS.IsPPC || OS.IsSP) && parent.hwndCB !is 0) return false;
+    int hMenu = parent.handle;
+    MENUITEMINFO info = new MENUITEMINFO ();
+    info.cbSize = MENUITEMINFO.sizeof;
+    info.fMask = OS.MIIM_STATE;
+    bool success = OS.GetMenuItemInfo (hMenu, id, false, info);
+    if (!success) error (DWT.ERROR_CANNOT_GET_SELECTION);
+    return (info.fState & OS.MFS_CHECKED) !is0;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled and all
+ * of the receiver's ancestors are enabled, and <code>false</code>
+ * otherwise. A disabled menu item is typically not selectable from the
+ * user interface and draws with an inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getEnabled
+ */
+public bool isEnabled () {
+    return getEnabled () && parent.isEnabled ();
+}
+
+void releaseChildren (bool destroy) {
+    if (menu !is null) {
+        menu.release (false);
+        menu = null;
+    }
+    super.releaseChildren (destroy);
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+    id = -1;
+}
+
+void releaseMenu () {
+    if (!OS.IsSP) setMenu (null);
+    menu = null;
+}
+
+void releaseParent () {
+    super.releaseParent ();
+    if (menu !is null) menu.dispose ();
+    menu = null;
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if (hBitmap !is 0) OS.DeleteObject (hBitmap);
+    hBitmap = 0;
+    if (accelerator !is 0) {
+        parent.destroyAccelerators ();
+    }
+    accelerator = 0;
+    display.removeMenuItem (this);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the arm events are generated for the control.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ArmListener
+ * @see #addArmListener
+ */
+public void removeArmListener (ArmListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Arm, listener);
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the help events are generated for the control.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see HelpListener
+ * @see #addHelpListener
+ */
+public void removeHelpListener (HelpListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Help, listener);
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+void selectRadio () {
+    int index = 0;
+    MenuItem [] items = parent.getItems ();
+    while (index < items.length && items [index] !is this) index++;
+    int i = index - 1;
+    while (i >= 0 && items [i].setRadioSelection (false)) --i;
+    int j = index + 1;
+    while (j < items.length && items [j].setRadioSelection (false)) j++;
+    setSelection (true);
+}
+
+/**
+ * Sets the widget accelerator.  An accelerator is the bit-wise
+ * OR of zero or more modifier masks and a key. Examples:
+ * <code>DWT.MOD1 | DWT.MOD2 | 'T', DWT.MOD3 | DWT.F2</code>.
+ * <code>DWT.CONTROL | DWT.SHIFT | 'T', DWT.ALT | DWT.F2</code>.
+ * The default value is zero, indicating that the menu item does
+ * not have an accelerator.
+ *
+ * @param accelerator an integer that is the bit-wise OR of masks and a key
+ *
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setAccelerator (int accelerator) {
+    checkWidget ();
+    if (this.accelerator is accelerator) return;
+    this.accelerator = accelerator;
+    parent.destroyAccelerators ();
+}
+
+/**
+ * Enables the receiver if the argument is <code>true</code>,
+ * and disables it otherwise. A disabled menu item is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ *
+ * @param enabled the new enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setEnabled (bool enabled) {
+    checkWidget ();
+    if ((OS.IsPPC || OS.IsSP) && parent.hwndCB !is 0) {
+        int hwndCB = parent.hwndCB;
+        TBBUTTONINFO info = new TBBUTTONINFO ();
+        info.cbSize = TBBUTTONINFO.sizeof;
+        info.dwMask = OS.TBIF_STATE;
+        OS.SendMessage (hwndCB, OS.TB_GETBUTTONINFO, id, info);
+        info.fsState &= ~OS.TBSTATE_ENABLED;
+        if (enabled) info.fsState |= OS.TBSTATE_ENABLED;
+        OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, id, info);
+    } else {
+        int hMenu = parent.handle;
+        if (OS.IsWinCE) {
+            int index = parent.indexOf (this);
+            if (index is -1) return;
+            int uEnable = OS.MF_BYPOSITION | (enabled ? OS.MF_ENABLED : OS.MF_GRAYED);
+            OS.EnableMenuItem (hMenu, index, uEnable);
+        } else {
+            MENUITEMINFO info = new MENUITEMINFO ();
+            info.cbSize = MENUITEMINFO.sizeof;
+            info.fMask = OS.MIIM_STATE;
+            bool success = OS.GetMenuItemInfo (hMenu, id, false, info);
+            if (!success) error (DWT.ERROR_CANNOT_SET_ENABLED);
+            int bits = OS.MFS_DISABLED | OS.MFS_GRAYED;
+            if (enabled) {
+                if ((info.fState & bits) is 0) return;
+                info.fState &= ~bits;
+            } else {
+                if ((info.fState & bits) is bits) return;
+                info.fState |= bits;
+            }
+            success = OS.SetMenuItemInfo (hMenu, id, false, info);
+            if (!success) {
+                /*
+                * Bug in Windows.  For some reason SetMenuItemInfo(),
+                * returns a fail code when setting the enabled or
+                * selected state of a default item, but sets the
+                * state anyway.  The fix is to ignore the error.
+                *
+                * NOTE:  This only happens on Vista.
+                */
+                if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                    success = id is OS.GetMenuDefaultItem (hMenu, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED);
+                }
+                if (!success) error (DWT.ERROR_CANNOT_SET_ENABLED);
+            }
+        }
+    }
+    parent.destroyAccelerators ();
+    parent.redraw ();
+}
+
+/**
+ * Sets the image the receiver will display to the argument.
+ * <p>
+ * Note: This operation is a hint and is not supported on
+ * platforms that do not have this concept (for example, Windows NT).
+ * </p>
+ *
+ * @param image the image to display
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (Image image) {
+    checkWidget ();
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    super.setImage (image);
+    if (OS.IsWinCE) {
+        if ((OS.IsPPC || OS.IsSP) && parent.hwndCB !is 0) {
+            int hwndCB = parent.hwndCB;
+            TBBUTTONINFO info = new TBBUTTONINFO ();
+            info.cbSize = TBBUTTONINFO.sizeof;
+            info.dwMask = OS.TBIF_IMAGE;
+            info.iImage = parent.imageIndex (image);
+            OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, id, info);
+        }
+        return;
+    }
+    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return;
+    MENUITEMINFO info = new MENUITEMINFO ();
+    info.cbSize = MENUITEMINFO.sizeof;
+    info.fMask = OS.MIIM_BITMAP;
+    if (parent.foreground !is -1) {
+        info.hbmpItem = OS.HBMMENU_CALLBACK;
+    } else {
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+            if (hBitmap !is 0) OS.DeleteObject (hBitmap);
+            info.hbmpItem = hBitmap = image !is null ? Display.create32bitDIB (image) : 0;
+        } else {
+            info.hbmpItem = OS.HBMMENU_CALLBACK;
+        }
+    }
+    int hMenu = parent.handle;
+    OS.SetMenuItemInfo (hMenu, id, false, info);
+    parent.redraw ();
+}
+
+/**
+ * Sets the receiver's pull down menu to the argument.
+ * Only <code>CASCADE</code> menu items can have a
+ * pull down menu. The sequence of key strokes, button presses
+ * and/or button releases that are used to request a pull down
+ * menu is platform specific.
+ * <p>
+ * Note: Disposing of a menu item that has a pull down menu
+ * will dispose of the menu.  To avoid this behavior, set the
+ * menu to null before the menu item is disposed.
+ * </p>
+ *
+ * @param menu the new pull down menu
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_MENU_NOT_DROP_DOWN - if the menu is not a drop down menu</li>
+ *    <li>ERROR_MENUITEM_NOT_CASCADE - if the menu item is not a <code>CASCADE</code></li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMenu (Menu menu) {
+    checkWidget ();
+
+    /* Check to make sure the new menu is valid */
+    if ((style & DWT.CASCADE) is 0) {
+        error (DWT.ERROR_MENUITEM_NOT_CASCADE);
+    }
+    if (menu !is null) {
+        if (menu.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+        if ((menu.style & DWT.DROP_DOWN) is 0) {
+            error (DWT.ERROR_MENU_NOT_DROP_DOWN);
+        }
+        if (menu.parent !is parent.parent) {
+            error (DWT.ERROR_INVALID_PARENT);
+        }
+    }
+
+    /* Assign the new menu */
+    Menu oldMenu = this.menu;
+    if (oldMenu is menu) return;
+    if (oldMenu !is null) oldMenu.cascade = null;
+    this.menu = menu;
+
+    /* Assign the new menu in the OS */
+    if ((OS.IsPPC || OS.IsSP) && parent.hwndCB !is 0) {
+        if (OS.IsPPC) {
+            int hwndCB = parent.hwndCB;
+            int hMenu = menu is null ? 0 : menu.handle;
+            OS.SendMessage (hwndCB, OS.SHCMBM_SETSUBMENU, id, hMenu);
+        }
+        if (OS.IsSP) error (DWT.ERROR_CANNOT_SET_MENU);
+    } else {
+        /*
+        * Feature in Windows.  When SetMenuItemInfo () is used to
+        * set a submenu and the menu item already has a submenu,
+        * Windows destroys the previous menu.  This is undocumented
+        * and unexpected but not necessarily wrong.  The fix is to
+        * remove the item with RemoveMenu () which does not destroy
+        * the submenu and then insert the item with InsertMenuItem ().
+        */
+        int hMenu = parent.handle;
+        MENUITEMINFO info = new MENUITEMINFO ();
+        info.cbSize = MENUITEMINFO.sizeof;
+        info.fMask = OS.MIIM_DATA;
+        int index = 0;
+        while (OS.GetMenuItemInfo (hMenu, index, true, info)) {
+            if (info.dwItemData is id) break;
+            index++;
+        }
+        if (info.dwItemData !is id) return;
+        bool restoreBitmap = false, success = false;
+
+        /*
+        * Bug in Windows.  When GetMenuItemInfo() is used to get the text,
+        * for an item that has a bitmap set using MIIM_BITMAP, the text is
+        * not returned.  This means that when SetMenuItemInfo() is used to
+        * set the submenu and the current menu state, the text is lost.
+        * The fix is to temporarily remove the bitmap and restore it after
+        * the text and submenu have been set.
+        */
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+            info.fMask = OS.MIIM_BITMAP;
+            OS.GetMenuItemInfo (hMenu, index, true, info);
+            restoreBitmap = info.hbmpItem !is 0 || parent.foreground !is -1;
+            if (restoreBitmap) {
+                info.hbmpItem = 0;
+                success = OS.SetMenuItemInfo (hMenu, id, false, info);
+            }
+        }
+
+        int cch = 128;
+        int hHeap = OS.GetProcessHeap ();
+        int byteCount = cch * TCHAR.sizeof;
+        int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+        info.fMask = OS.MIIM_STATE | OS.MIIM_ID | OS.MIIM_TYPE | OS.MIIM_DATA;
+        info.dwTypeData = pszText;
+        info.cch = cch;
+        success = OS.GetMenuItemInfo (hMenu, index, true, info);
+        if (menu !is null) {
+            menu.cascade = this;
+            info.fMask |= OS.MIIM_SUBMENU;
+            info.hSubMenu = menu.handle;
+        }
+        OS.RemoveMenu (hMenu, index, OS.MF_BYPOSITION);
+        if (OS.IsWinCE) {
+            /*
+            * On WinCE, InsertMenuItem() is not available.  The fix is to
+            * use SetMenuItemInfo() but this call does not set the menu item
+            * state and submenu.  The fix is to use InsertMenu() to insert
+            * the item, SetMenuItemInfo() to set the string and EnableMenuItem()
+            * and CheckMenuItem() to set the state.
+            */
+            int uIDNewItem = id;
+            int uFlags = OS.MF_BYPOSITION;
+            if (menu !is null) {
+                uFlags |= OS.MF_POPUP;
+                uIDNewItem = menu.handle;
+            }
+            TCHAR lpNewItem = new TCHAR (0, " ", true);
+            success = OS.InsertMenu (hMenu, index, uFlags, uIDNewItem, lpNewItem);
+            if (success) {
+                info.fMask = OS.MIIM_DATA | OS.MIIM_TYPE;
+                success = OS.SetMenuItemInfo (hMenu, index, true, info);
+                if ((info.fState & (OS.MFS_DISABLED | OS.MFS_GRAYED)) !is 0) {
+                    OS.EnableMenuItem (hMenu, index, OS.MF_BYPOSITION | OS.MF_GRAYED);
+                }
+                if ((info.fState & OS.MFS_CHECKED) !is 0) {
+                    OS.CheckMenuItem (hMenu, index, OS.MF_BYPOSITION | OS.MF_CHECKED);
+                }
+            }
+        } else {
+            success = OS.InsertMenuItem (hMenu, index, true, info);
+            /*
+            * Restore the bitmap that was removed to work around a problem
+            * in GetMenuItemInfo() and menu items that have bitmaps set with
+            * MIIM_BITMAP.
+            */
+            if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+                if (restoreBitmap) {
+                    info.fMask = OS.MIIM_BITMAP;
+                    if (parent.foreground !is -1) {
+                        info.hbmpItem = OS.HBMMENU_CALLBACK;
+                    } else {
+                        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                            info.hbmpItem = hBitmap;
+                        } else {
+                            info.hbmpItem = OS.HBMMENU_CALLBACK;
+                        }
+                    }
+                    success = OS.SetMenuItemInfo (hMenu, id, false, info);
+                }
+            }
+        }
+        if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+        if (!success) error (DWT.ERROR_CANNOT_SET_MENU);
+    }
+    parent.destroyAccelerators ();
+}
+
+bool setRadioSelection (bool value) {
+    if ((style & DWT.RADIO) is 0) return false;
+    if (getSelection () !is value) {
+        setSelection (value);
+        postEvent (DWT.Selection);
+    }
+    return true;
+}
+
+/**
+ * Sets the selection state of the receiver.
+ * <p>
+ * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
+ * it is selected when it is checked.
+ *
+ * @param selected the new selection state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (bool selected) {
+    checkWidget ();
+    if ((style & (DWT.CHECK | DWT.RADIO)) is 0) return;
+    if ((OS.IsPPC || OS.IsSP) && parent.hwndCB !is 0) return;
+    int hMenu = parent.handle;
+    if (OS.IsWinCE) {
+        int index = parent.indexOf (this);
+        if (index is -1) return;
+        int uCheck = OS.MF_BYPOSITION | (selected ? OS.MF_CHECKED : OS.MF_UNCHECKED);
+        OS.CheckMenuItem (hMenu, index, uCheck);
+    } else {
+        MENUITEMINFO info = new MENUITEMINFO ();
+        info.cbSize = MENUITEMINFO.sizeof;
+        info.fMask = OS.MIIM_STATE;
+        bool success = OS.GetMenuItemInfo (hMenu, id, false, info);
+        if (!success) error (DWT.ERROR_CANNOT_SET_SELECTION);
+        info.fState &= ~OS.MFS_CHECKED;
+        if (selected) info.fState |= OS.MFS_CHECKED;
+        success = OS.SetMenuItemInfo (hMenu, id, false, info);
+        if (!success) {
+            /*
+            * Bug in Windows.  For some reason SetMenuItemInfo(),
+            * returns a fail code when setting the enabled or
+            * selected state of a default item, but sets the
+            * state anyway.  The fix is to ignore the error.
+            *
+            * NOTE:  This only happens on Vista.
+            */
+            if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                success = id is OS.GetMenuDefaultItem (hMenu, OS.MF_BYCOMMAND, OS.GMDI_USEDISABLED);
+            }
+            if (!success) error (DWT.ERROR_CANNOT_SET_SELECTION);
+        }
+    }
+    parent.redraw ();
+}
+/**
+ * Sets the receiver's text. The string may include
+ * the mnemonic character and accelerator text.
+ * <p>
+ * Mnemonics are indicated by an '&amp;' that causes the next
+ * character to be the mnemonic.  When the user presses a
+ * key sequence that matches the mnemonic, a selection
+ * event occurs. On most platforms, the mnemonic appears
+ * underlined but may be emphasised in a platform specific
+ * manner.  The mnemonic indicator character '&amp;' can be
+ * escaped by doubling it in the string, causing a single
+ * '&amp;' to be displayed.
+ * </p>
+ * <p>
+ * Accelerator text is indicated by the '\t' character.
+ * On platforms that support accelerator text, the text
+ * that follows the '\t' character is displayed to the user,
+ * typically indicating the key stroke that will cause
+ * the item to become selected.  On most platforms, the
+ * accelerator text appears right aligned in the menu.
+ * Setting the accelerator text does not install the
+ * accelerator key sequence. The accelerator key sequence
+ * is installed using #setAccelerator.
+ * </p>
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setAccelerator
+ */
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    if (text.equals (string)) return;
+    super.setText (string);
+    int hHeap = OS.GetProcessHeap ();
+    int pszText = 0;
+    bool success = false;
+    if ((OS.IsPPC || OS.IsSP) && parent.hwndCB !is 0) {
+        /*
+        * Bug in WinCE PPC.  Tool items on the menubar don't resize
+        * correctly when the character '&' is used (even when it
+        * is a sequence '&&').  The fix is to remove all '&' from
+        * the string.
+        */
+        if (string.indexOf ('&') !is -1) {
+            int length = string.length ();
+            char[] text = new char [length];
+            string.getChars( 0, length, text, 0);
+            int i = 0, j = 0;
+            for (i=0; i<length; i++) {
+                if (text[i] !is '&') text [j++] = text [i];
+            }
+            if (j < i) string = new String (text, 0, j);
+        }
+        /* Use the character encoding for the default locale */
+        TCHAR buffer = new TCHAR (0, string, true);
+        int byteCount = buffer.length () * TCHAR.sizeof;
+        pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+        OS.MoveMemory (pszText, buffer, byteCount);
+        int hwndCB = parent.hwndCB;
+        TBBUTTONINFO info2 = new TBBUTTONINFO ();
+        info2.cbSize = TBBUTTONINFO.sizeof;
+        info2.dwMask = OS.TBIF_TEXT;
+        info2.pszText = pszText;
+        success = OS.SendMessage (hwndCB, OS.TB_SETBUTTONINFO, id, info2) !is 0;
+    } else {
+        MENUITEMINFO info = new MENUITEMINFO ();
+        info.cbSize = MENUITEMINFO.sizeof;
+        int hMenu = parent.handle;
+
+        /*
+        * Bug in Windows 2000.  For some reason, when MIIM_TYPE is set
+        * on a menu item that also has MIIM_BITMAP, the MIIM_TYPE clears
+        * the MIIM_BITMAP style.  The fix is to reset both MIIM_BITMAP.
+        * Note, this does not happen on Windows 98.
+        */
+        bool restoreBitmap = false;
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+            info.fMask = OS.MIIM_BITMAP;
+            OS.GetMenuItemInfo (hMenu, id, false, info);
+            restoreBitmap = info.hbmpItem !is 0 || parent.foreground !is -1;
+        }
+
+        /* Use the character encoding for the default locale */
+        TCHAR buffer = new TCHAR (0, string, true);
+        int byteCount = buffer.length () * TCHAR.sizeof;
+        pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+        OS.MoveMemory (pszText, buffer, byteCount);
+        info.fMask = OS.MIIM_TYPE;
+        info.fType = widgetStyle ();
+        info.dwTypeData = pszText;
+        success = OS.SetMenuItemInfo (hMenu, id, false, info);
+
+        /*
+        * Restore the bitmap that was removed to work around a problem
+        * in GetMenuItemInfo() and menu items that have bitmaps set with
+        * MIIM_BITMAP.
+        */
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+            if (restoreBitmap) {
+                info.fMask = OS.MIIM_BITMAP;
+                if (parent.foreground !is -1) {
+                    info.hbmpItem = OS.HBMMENU_CALLBACK;
+                } else {
+                    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                        info.hbmpItem = hBitmap;
+                    } else {
+                        info.hbmpItem = OS.HBMMENU_CALLBACK;
+                    }
+                }
+                success = OS.SetMenuItemInfo (hMenu, id, false, info);
+            }
+        }
+    }
+    if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+    if (!success) error (DWT.ERROR_CANNOT_SET_TEXT);
+    parent.redraw ();
+}
+
+int widgetStyle () {
+    int bits = 0;
+    Decorations shell = parent.parent;
+    if ((shell.style & DWT.MIRRORED) !is 0) {
+        if ((parent.style & DWT.LEFT_TO_RIGHT) !is 0) {
+            bits |= OS.MFT_RIGHTJUSTIFY | OS.MFT_RIGHTORDER;
+        }
+    } else {
+        if ((parent.style & DWT.RIGHT_TO_LEFT) !is 0) {
+            bits |= OS.MFT_RIGHTJUSTIFY | OS.MFT_RIGHTORDER;
+        }
+    }
+    if ((style & DWT.SEPARATOR) !is 0) return bits | OS.MFT_SEPARATOR;
+    if ((style & DWT.RADIO) !is 0) return bits | OS.MFT_RADIOCHECK;
+    return bits | OS.MFT_STRING;
+}
+
+LRESULT wmCommandChild (int wParam, int lParam) {
+    if ((style & DWT.CHECK) !is 0) {
+        setSelection (!getSelection ());
+    } else {
+        if ((style & DWT.RADIO) !is 0) {
+            if ((parent.getStyle () & DWT.NO_RADIO_GROUP) !is 0) {
+                setSelection (!getSelection ());
+            } else {
+                selectRadio ();
+            }
+        }
+    }
+    Event event = new Event ();
+    setInputState (event, DWT.Selection);
+    postEvent (DWT.Selection, event);
+    return null;
+}
+
+LRESULT wmDrawChild (int wParam, int lParam) {
+    DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
+    OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof);
+    if (image !is null) {
+        GCData data = new GCData();
+        data.device = display;
+        GC gc = GC.win32_new (struct.hDC, data);
+        /*
+        * Bug in Windows.  When a bitmap is included in the
+        * menu bar, the HDC seems to already include the left
+        * coordinate.  The fix is to ignore this value when
+        * the item is in a menu bar.
+        */
+        int x = (parent.style & DWT.BAR) !is 0 ? MARGIN_WIDTH * 2 : struct.left;
+        Image image = getEnabled () ? this.image : new Image (display, this.image, DWT.IMAGE_DISABLE);
+        gc.drawImage (image, x, struct.top + MARGIN_HEIGHT);
+        if (this.image !is image) image.dispose ();
+        gc.dispose ();
+    }
+    if (parent.foreground !is -1) OS.SetTextColor (struct.hDC, parent.foreground);
+    return null;
+}
+
+LRESULT wmMeasureChild (int wParam, int lParam) {
+    MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT ();
+    OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof);
+    int width = 0, height = 0;
+    if (image !is null) {
+        Rectangle rect = image.getBounds ();
+        width = rect.width;
+        height = rect.height;
+    } else {
+        /*
+        * Bug in Windows.  If a menu contains items that have
+        * images and can be checked, Windows does not include
+        * the width of the image and the width of the check when
+        * computing the width of the menu.  When the longest item
+        * does not have an image, the label and the accelerator
+        * text can overlap.  The fix is to use SetMenuItemInfo()
+        * to indicate that all items have a bitmap and then include
+        * the width of the widest bitmap in WM_MEASURECHILD.
+        */
+        MENUINFO lpcmi = new MENUINFO ();
+        lpcmi.cbSize = MENUINFO.sizeof;
+        lpcmi.fMask = OS.MIM_STYLE;
+        int hMenu = parent.handle;
+        OS.GetMenuInfo (hMenu, lpcmi);
+        if ((lpcmi.dwStyle & OS.MNS_CHECKORBMP) is 0) {
+            MenuItem [] items = parent.getItems ();
+            for (int i=0; i<items.length; i++) {
+                MenuItem item = items [i];
+                if (item.image !is null) {
+                    Rectangle rect = item.image.getBounds ();
+                    width = Math.max (width, rect.width);
+                }
+            }
+        }
+    }
+    if (width !is 0 || height !is 0) {
+        struct.itemWidth = width + MARGIN_WIDTH * 2;
+        struct.itemHeight = height + MARGIN_HEIGHT * 2;
+        OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof);
+    }
+    return null;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/MessageBox.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.MessageBox;
+
+import dwt.widgets.Dialog;
+import dwt.widgets.Shell;
+
+class MessageBox : Dialog {
+    public this (Shell parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class are used to inform or warn the user.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>ICON_ERROR, ICON_INFORMATION, ICON_QUESTION, ICON_WARNING, ICON_WORKING</dd>
+ * <dd>OK, OK | CANCEL</dd>
+ * <dd>YES | NO, YES | NO | CANCEL</dd>
+ * <dd>RETRY | CANCEL</dd>
+ * <dd>ABORT | RETRY | IGNORE</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles ICON_ERROR, ICON_INFORMATION, ICON_QUESTION,
+ * ICON_WARNING and ICON_WORKING may be specified.
+ * </p><p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+public  class MessageBox extends Dialog {
+    String message = "";
+
+/**
+ * Constructs a new instance of this class given only its parent.
+ *
+ * @param parent a shell which will be the parent of the new instance
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public MessageBox (Shell parent) {
+    this (parent, DWT.OK | DWT.ICON_INFORMATION | DWT.APPLICATION_MODAL);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ *
+ * @param parent a shell which will be the parent of the new instance
+ * @param style the style of dialog to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public MessageBox (Shell parent, int style) {
+    super (parent, checkStyle (style));
+    checkSubclass ();
+}
+
+static int checkStyle (int style) {
+    if ((style & (DWT.PRIMARY_MODAL | DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) is 0) style |= DWT.APPLICATION_MODAL;
+    int mask = (DWT.YES | DWT.NO | DWT.OK | DWT.CANCEL | DWT.ABORT | DWT.RETRY | DWT.IGNORE);
+    int bits = style & mask;
+    if (bits is DWT.OK || bits is DWT.CANCEL || bits is (DWT.OK | DWT.CANCEL)) return style;
+    if (bits is DWT.YES || bits is DWT.NO || bits is (DWT.YES | DWT.NO) || bits is (DWT.YES | DWT.NO | DWT.CANCEL)) return style;
+    if (bits is (DWT.RETRY | DWT.CANCEL) || bits is (DWT.ABORT | DWT.RETRY | DWT.IGNORE)) return style;
+    style = (style & ~mask) | DWT.OK;
+    return style;
+}
+
+/**
+ * Returns the dialog's message, or an empty string if it does not have one.
+ * The message is a description of the purpose for which the dialog was opened.
+ * This message will be visible in the dialog while it is open.
+ *
+ * @return the message
+ */
+public String getMessage () {
+    return message;
+}
+
+/**
+ * Makes the dialog visible and brings it to the front
+ * of the display.
+ *
+ * @return the ID of the button that was selected to dismiss the
+ *         message box (e.g. DWT.OK, DWT.CANCEL, etc.)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li>
+ * </ul>
+ */
+public int open () {
+
+    /* Compute the MessageBox style */
+    int buttonBits = 0;
+    if ((style & DWT.OK) is DWT.OK) buttonBits = OS.MB_OK;
+    if ((style & (DWT.OK | DWT.CANCEL)) is (DWT.OK | DWT.CANCEL)) buttonBits = OS.MB_OKCANCEL;
+    if ((style & (DWT.YES | DWT.NO)) is (DWT.YES | DWT.NO)) buttonBits = OS.MB_YESNO;
+    if ((style & (DWT.YES | DWT.NO | DWT.CANCEL)) is (DWT.YES | DWT.NO | DWT.CANCEL)) buttonBits = OS.MB_YESNOCANCEL;
+    if ((style & (DWT.RETRY | DWT.CANCEL)) is (DWT.RETRY | DWT.CANCEL)) buttonBits = OS.MB_RETRYCANCEL;
+    if ((style & (DWT.ABORT | DWT.RETRY | DWT.IGNORE)) is (DWT.ABORT | DWT.RETRY | DWT.IGNORE)) buttonBits = OS.MB_ABORTRETRYIGNORE;
+    if (buttonBits is 0) buttonBits = OS.MB_OK;
+
+    int iconBits = 0;
+    if ((style & DWT.ICON_ERROR) !is 0) iconBits = OS.MB_ICONERROR;
+    if ((style & DWT.ICON_INFORMATION) !is 0) iconBits = OS.MB_ICONINFORMATION;
+    if ((style & DWT.ICON_QUESTION) !is 0) iconBits = OS.MB_ICONQUESTION;
+    if ((style & DWT.ICON_WARNING) !is 0) iconBits = OS.MB_ICONWARNING;
+    if ((style & DWT.ICON_WORKING) !is 0) iconBits = OS.MB_ICONINFORMATION;
+
+    /* Only MB_APPLMODAL is supported on WinCE */
+    int modalBits = 0;
+    if (OS.IsWinCE) {
+        if ((style & (DWT.PRIMARY_MODAL | DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) !is 0) {
+            modalBits = OS.MB_APPLMODAL;
+        }
+    } else {
+        if ((style & DWT.PRIMARY_MODAL) !is 0) modalBits = OS.MB_APPLMODAL;
+        if ((style & DWT.APPLICATION_MODAL) !is 0) modalBits = OS.MB_TASKMODAL;
+        if ((style & DWT.SYSTEM_MODAL) !is 0) modalBits = OS.MB_SYSTEMMODAL;
+    }
+
+    int bits = buttonBits | iconBits | modalBits;
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) bits |= OS.MB_RTLREADING;
+    if ((style & (DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT)) is 0) {
+        if (parent !is null && (parent.style & DWT.MIRRORED) !is 0) {
+            bits |= OS.MB_RTLREADING;
+        }
+    }
+
+    /*
+    * Feature in Windows.  System modal is not supported
+    * on Windows 95 and NT.  The fix is to convert system
+    * modal to task modal.
+    */
+    if ((bits & OS.MB_SYSTEMMODAL) !is 0) {
+        bits |= OS.MB_TASKMODAL;
+        bits &= ~OS.MB_SYSTEMMODAL;
+        /* Force a system modal message box to the front */
+        bits |= OS.MB_TOPMOST;
+    }
+
+    /*
+    * Feature in Windows.  In order for MB_TASKMODAL to work,
+    * the parent HWND of the MessageBox () call must be NULL.
+    * If the parent is not NULL, MB_TASKMODAL behaves the
+    * same as MB_APPLMODAL.  The fix to set the parent HWND
+    * anyway and not rely on MB_MODAL to work by making the
+    * parent be temporarily modal.
+    */
+    int hwndOwner = parent !is null ? parent.handle : 0;
+    Shell oldModal = null;
+    Display display = null;
+    if ((bits & OS.MB_TASKMODAL) !is 0) {
+        display = parent.getDisplay ();
+        oldModal = display.getModalDialogShell ();
+        display.setModalDialogShell (parent);
+    }
+
+    /* Open the message box */
+    /* Use the character encoding for the default locale */
+    TCHAR buffer1 = new TCHAR (0, message, true);
+    TCHAR buffer2 = new TCHAR (0, title, true);
+    int code = OS.MessageBox (hwndOwner, buffer1, buffer2, bits);
+
+    /* Clear the temporarily dialog modal parent */
+    if ((bits & OS.MB_TASKMODAL) !is 0) {
+        display.setModalDialogShell (oldModal);
+    }
+
+    /*
+    * This code is intentionally commented.  On some
+    * platforms, the owner window is repainted right
+    * away when a dialog window exits.  This behavior
+    * is currently unspecified.
+    */
+//  if (hwndOwner !is 0) OS.UpdateWindow (hwndOwner);
+
+    /* Compute and return the result */
+    if (code !is 0) {
+        int type = bits & 0x0F;
+        if (type is OS.MB_OK) return DWT.OK;
+        if (type is OS.MB_OKCANCEL) {
+            return (code is OS.IDOK) ? DWT.OK : DWT.CANCEL;
+        }
+        if (type is OS.MB_YESNO) {
+            return (code is OS.IDYES) ? DWT.YES : DWT.NO;
+        }
+        if (type is OS.MB_YESNOCANCEL) {
+            if (code is OS.IDYES) return DWT.YES;
+            if (code is OS.IDNO) return DWT.NO;
+            return DWT.CANCEL;
+        }
+        if (type is OS.MB_RETRYCANCEL) {
+            return (code is OS.IDRETRY) ? DWT.RETRY : DWT.CANCEL;
+        }
+        if (type is OS.MB_ABORTRETRYIGNORE) {
+            if (code is OS.IDRETRY) return DWT.RETRY;
+            if (code is OS.IDABORT) return DWT.ABORT;
+            return DWT.IGNORE;
+        }
+    }
+    return DWT.CANCEL;
+}
+
+/**
+ * Sets the dialog's message, which is a description of
+ * the purpose for which it was opened. This message will be
+ * visible on the dialog while it is open.
+ *
+ * @param string the message
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ */
+public void setMessage (String string) {
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    message = string;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Monitor.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Monitor;
+
+import dwt.graphics.Rectangle;
+
+/**
+ * Instances of this class are descriptions of monitors.
+ *
+ * @see Display
+ *
+ * @since 3.0
+ */
+public final class Monitor {
+    int handle;
+    int x, y, width, height;
+    int clientX, clientY, clientWidth, clientHeight;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+this () {
+}
+
+/**
+ * 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 ( auto mon = cast(dwt.widgets.Monitor.Monitor)object ){
+       return handle is mon.handle;
+    }
+    return false;
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its device.
+ *
+ * @return the receiver's bounding rectangle
+ */
+public Rectangle getBounds () {
+    return new Rectangle (x, y, width, height);
+}
+
+/**
+ * Returns a rectangle which describes the area of the
+ * receiver which is capable of displaying data.
+ *
+ * @return the client area
+ */
+public Rectangle getClientArea () {
+    return new Rectangle (clientX, clientY, clientWidth, clientHeight);
+}
+
+/**
+ * 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(Object)
+ */
+public override hash_t toHash() {
+    return cast(hash_t)handle;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/ProgressBar.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,375 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.ProgressBar;
+
+import dwt.widgets.Control;
+class ProgressBar : Control {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Point;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of the receiver represent an unselectable
+ * user interface object that is used to display progress,
+ * typically in the form of a bar.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SMOOTH, HORIZONTAL, VERTICAL, INDETERMINATE</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
+ * </p><p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+public class ProgressBar extends Control {
+    static final int DELAY = 100;
+    static final int TIMER_ID = 100;
+    static final int MINIMUM_WIDTH = 100;
+    static final int ProgressBarProc;
+    static final TCHAR ProgressBarClass = new TCHAR (0, OS.PROGRESS_CLASS, true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, ProgressBarClass, lpWndClass);
+        ProgressBarProc = lpWndClass.lpfnWndProc;
+        /*
+        * Feature in Windows.  The progress bar window class
+        * does not include CS_DBLCLKS.  This mean that these
+        * controls will not get double click messages such as
+        * WM_LBUTTONDBLCLK.  The fix is to register a new
+        * window class with CS_DBLCLKS.
+        *
+        * NOTE:  Screen readers look for the exact class name
+        * of the control in order to provide the correct kind
+        * of assistance.  Therefore, it is critical that the
+        * new window class have the same name.  It is possible
+        * to register a local window class with the same name
+        * as a global class.  Since bits that affect the class
+        * are being changed, it is possible that other native
+        * code, other than DWT, could create a control with
+        * this class name, and fail unexpectedly.
+        */
+        int hInstance = OS.GetModuleHandle (null);
+        int hHeap = OS.GetProcessHeap ();
+        lpWndClass.hInstance = hInstance;
+        lpWndClass.style &= ~OS.CS_GLOBALCLASS;
+        lpWndClass.style |= OS.CS_DBLCLKS;
+        int byteCount = ProgressBarClass.length () * TCHAR.sizeof;
+        int lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+        OS.MoveMemory (lpszClassName, ProgressBarClass, byteCount);
+        lpWndClass.lpszClassName = lpszClassName;
+        OS.RegisterClass (lpWndClass);
+        OS.HeapFree (hHeap, 0, lpszClassName);
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#SMOOTH
+ * @see DWT#HORIZONTAL
+ * @see DWT#VERTICAL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public ProgressBar (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.CallWindowProc (ProgressBarProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    style |= DWT.NO_FOCUS;
+    return checkBits (style, DWT.HORIZONTAL, DWT.VERTICAL, 0, 0, 0, 0);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int border = getBorderWidth ();
+    int width = border * 2, height = border * 2;
+    if ((style & DWT.HORIZONTAL) !is 0) {
+        width += OS.GetSystemMetrics (OS.SM_CXHSCROLL) * 10;
+        height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    } else {
+        width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+        height += OS.GetSystemMetrics (OS.SM_CYVSCROLL) * 10;
+    }
+    if (wHint !is DWT.DEFAULT) width = wHint + (border * 2);
+    if (hHint !is DWT.DEFAULT) height = hHint + (border * 2);
+    return new Point (width, height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    startTimer ();
+}
+
+int defaultForeground () {
+    return OS.GetSysColor (OS.COLOR_HIGHLIGHT);
+}
+
+/**
+ * Returns the maximum value which the receiver will allow.
+ *
+ * @return the maximum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMaximum () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.PBM_GETRANGE, 0, 0);
+}
+
+/**
+ * Returns the minimum value which the receiver will allow.
+ *
+ * @return the minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMinimum () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.PBM_GETRANGE, 1, 0);
+}
+
+/**
+ * Returns the single 'selection' that is the receiver's position.
+ *
+ * @return the selection
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelection () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.PBM_GETPOS, 0, 0);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    stopTimer ();
+}
+
+void startTimer () {
+    if ((style & DWT.INDETERMINATE) !is 0) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if (OS.COMCTL32_MAJOR < 6 || (bits & OS.PBS_MARQUEE) is 0) {
+            OS.SetTimer (handle, TIMER_ID, DELAY, 0);
+        } else {
+            OS.SendMessage (handle, OS.PBM_SETMARQUEE, 1, DELAY);
+        }
+    }
+}
+
+void stopTimer () {
+    if ((style & DWT.INDETERMINATE) !is 0) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if (OS.COMCTL32_MAJOR < 6 || (bits & OS.PBS_MARQUEE) is 0) {
+            OS.KillTimer (handle, TIMER_ID);
+        } else {
+            OS.SendMessage (handle, OS.PBM_SETMARQUEE, 0, 0);
+        }
+    }
+}
+
+void setBackgroundPixel (int pixel) {
+    if (pixel is -1) pixel = OS.CLR_DEFAULT;
+    OS.SendMessage (handle, OS.PBM_SETBKCOLOR, 0, pixel);
+}
+
+void setForegroundPixel (int pixel) {
+    if (pixel is -1) pixel = OS.CLR_DEFAULT;
+    OS.SendMessage (handle, OS.PBM_SETBARCOLOR, 0, pixel);
+}
+
+/**
+ * Sets the maximum value that the receiver will allow.  This new
+ * value will be ignored if it is not greater than the receiver's current
+ * minimum value.  If the new maximum is applied then the receiver's
+ * selection value will be adjusted if necessary to fall within its new range.
+ *
+ * @param value the new maximum, which must be greater than the current minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMaximum (int value) {
+    checkWidget ();
+    int minimum = OS.SendMessage (handle, OS.PBM_GETRANGE, 1, 0);
+    if (0 <= minimum && minimum < value) {
+        OS.SendMessage (handle, OS.PBM_SETRANGE32, minimum, value);
+    }
+}
+
+/**
+ * Sets the minimum value that the receiver will allow.  This new
+ * value will be ignored if it is negative or is not less than the receiver's
+ * current maximum value.  If the new minimum is applied then the receiver's
+ * selection value will be adjusted if necessary to fall within its new range.
+ *
+ * @param value the new minimum, which must be nonnegative and less than the current maximum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinimum (int value) {
+    checkWidget ();
+    int maximum = OS.SendMessage (handle, OS.PBM_GETRANGE, 0, 0);
+    if (0 <= value && value < maximum) {
+        OS.SendMessage (handle, OS.PBM_SETRANGE32, value, maximum);
+    }
+}
+
+/**
+ * Sets the single 'selection' that is the receiver's
+ * position to the argument which must be greater than or equal
+ * to zero.
+ *
+ * @param value the new selection (must be zero or greater)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (int value) {
+    checkWidget ();
+    OS.SendMessage (handle, OS.PBM_SETPOS, value, 0);
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle ();
+    if ((style & DWT.SMOOTH) !is 0) bits |= OS.PBS_SMOOTH;
+    if ((style & DWT.VERTICAL) !is 0) bits |= OS.PBS_VERTICAL;
+    if ((style & DWT.INDETERMINATE) !is 0) bits |= OS.PBS_MARQUEE;
+    return bits;
+}
+
+TCHAR windowClass () {
+    return ProgressBarClass;
+}
+
+int windowProc () {
+    return ProgressBarProc;
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  The progress bar does
+    * not implement WM_GETDLGCODE.  As a result,
+    * a progress bar takes focus and takes part
+    * in tab traversal.  This behavior, while
+    * unspecified, is unwanted.  The fix is to
+    * implement WM_GETDLGCODE to behave like a
+    * STATIC control.
+    */
+    return new LRESULT (OS.DLGC_STATIC);
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  When a progress bar with the style
+    * PBS_MARQUEE becomes too small, the animation (currently
+    * a small bar moving from right to left) does not have
+    * enough space to draw.  The result is that the progress
+    * bar does not appear to be moving.  The fix is to detect
+    * this case, clear the PBS_MARQUEE style and emulate the
+    * animation using PBM_STEPIT.
+    *
+    * NOTE:  This only happens on Window XP.
+    */
+    if ((style & DWT.INDETERMINATE) !is 0) {
+        if (OS.COMCTL32_MAJOR >= 6) {
+            forceResize ();
+            RECT rect = new RECT ();
+            OS.GetClientRect (handle, rect);
+            int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            int newBits = oldBits;
+            if (rect.right - rect.left < MINIMUM_WIDTH) {
+                newBits &= ~OS.PBS_MARQUEE;
+            } else {
+                newBits |= OS.PBS_MARQUEE;
+            }
+            if (newBits !is oldBits) {
+                stopTimer ();
+                OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+                startTimer ();
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_TIMER (int wParam, int lParam) {
+    LRESULT result = super.WM_TIMER (wParam, lParam);
+    if (result !is null) return result;
+    if ((style & DWT.INDETERMINATE) !is 0) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if (OS.COMCTL32_MAJOR < 6 || (bits & OS.PBS_MARQUEE) is 0) {
+            if (wParam is TIMER_ID) {
+                OS.SendMessage (handle, OS.PBM_STEPIT, 0, 0);
+            }
+        }
+    }
+    return result;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/RunnableLock.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.RunnableLock;
+
+import tango.core.Thread;
+import dwt.dwthelper.Runnable;
+import tango.core.Exception;
+
+/**
+ * Instances of this class are used to ensure that an
+ * application cannot interfere with the locking mechanism
+ * used to implement asynchronous and synchronous communication
+ * between widgets and background threads.
+ */
+
+class RunnableLock  {
+    Runnable runnable;
+    Thread thread;
+    TracedException throwable;
+
+this (Runnable runnable) {
+    this.runnable = runnable;
+}
+
+bool done () {
+    return runnable is null || throwable !is null;
+}
+
+void run () {
+    if (runnable !is null) runnable.run ();
+    runnable = null;
+}
+
+//PORTING_FIXME: How to emulate Java locking?
+void notifyAll(){
+}
+//PORTING_FIXME: How to emulate Java locking?
+void wait(){
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Sash.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,427 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Sash;
+
+import dwt.widgets.Control;
+class Sash : Control {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Point;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of the receiver represent a selectable user interface object
+ * that allows the user to drag a rubber banded outline of the sash within
+ * the parent control.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>HORIZONTAL, VERTICAL, SMOOTH</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
+ * </p><p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+public class Sash extends Control {
+    bool dragging;
+    int startX, startY, lastX, lastY;
+    final static int INCREMENT = 1;
+    final static int PAGE_INCREMENT = 9;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#HORIZONTAL
+ * @see DWT#VERTICAL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Sash (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the x, y, width, and height fields of the event object are valid.
+ * If the receiver is being dragged, the event object detail field contains the value <code>DWT.DRAG</code>.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the control is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state |= THEME_BACKGROUND;
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.HORIZONTAL, DWT.VERTICAL, 0, 0, 0, 0);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int border = getBorderWidth ();
+    int width = border * 2, height = border * 2;
+    if ((style & DWT.HORIZONTAL) !is 0) {
+        width += DEFAULT_WIDTH;  height += 3;
+    } else {
+        width += 3; height += DEFAULT_HEIGHT;
+    }
+    if (wHint !is DWT.DEFAULT) width = wHint + (border * 2);
+    if (hHint !is DWT.DEFAULT) height = hHint + (border * 2);
+    return new Point (width, height);
+}
+
+void drawBand (int x, int y, int width, int height) {
+    if ((style & DWT.SMOOTH) !is 0) return;
+    int hwndTrack = parent.handle;
+    byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0};
+    int stippleBitmap = OS.CreateBitmap (8, 8, 1, 1, bits);
+    int stippleBrush = OS.CreatePatternBrush (stippleBitmap);
+    int hDC = OS.GetDCEx (hwndTrack, 0, OS.DCX_CACHE);
+    int oldBrush = OS.SelectObject (hDC, stippleBrush);
+    OS.PatBlt (hDC, x, y, width, height, OS.PATINVERT);
+    OS.SelectObject (hDC, oldBrush);
+    OS.ReleaseDC (hwndTrack, hDC);
+    OS.DeleteObject (stippleBrush);
+    OS.DeleteObject (stippleBitmap);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+TCHAR windowClass () {
+    return display.windowClass;
+}
+
+int windowProc () {
+    return display.windowProc;
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    super.WM_ERASEBKGND (wParam, lParam);
+    drawBackground (wParam);
+    return LRESULT.ONE;
+}
+
+LRESULT WM_KEYDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_KEYDOWN (wParam, lParam);
+    if (result !is null) return result;
+    switch (wParam) {
+        case OS.VK_LEFT:
+        case OS.VK_RIGHT:
+        case OS.VK_UP:
+        case OS.VK_DOWN:
+
+            /* Calculate the new x or y position */
+            if (OS.GetKeyState (OS.VK_LBUTTON) < 0) return result;
+            int step = OS.GetKeyState (OS.VK_CONTROL) < 0 ? INCREMENT : PAGE_INCREMENT;
+            POINT pt = new POINT ();
+            if ((style & DWT.VERTICAL) !is 0) {
+                if (wParam is OS.VK_UP || wParam is OS.VK_DOWN) break;
+                pt.x = wParam is OS.VK_LEFT ? -step : step;
+            } else {
+                if (wParam is OS.VK_LEFT || wParam is OS.VK_RIGHT) break;
+                pt.y = wParam is OS.VK_UP ? -step : step;
+            }
+            int hwndTrack = parent.handle;
+            OS.MapWindowPoints (handle, hwndTrack, pt, 1);
+            RECT rect = new RECT (), clientRect = new RECT ();
+            OS.GetWindowRect (handle, rect);
+            int width = rect.right - rect.left;
+            int height = rect.bottom - rect.top;
+            OS.GetClientRect (hwndTrack, clientRect);
+            int clientWidth = clientRect.right - clientRect.left;
+            int clientHeight = clientRect.bottom - clientRect.top;
+            int newX = lastX, newY = lastY;
+            if ((style & DWT.VERTICAL) !is 0) {
+                newX = Math.min (Math.max (0, pt.x - startX), clientWidth - width);
+            } else {
+                newY = Math.min (Math.max (0, pt.y - startY), clientHeight - height);
+            }
+            if (newX is lastX && newY is lastY) return result;
+
+            /* Update the pointer position */
+            POINT cursorPt = new POINT ();
+            cursorPt.x = pt.x;  cursorPt.y = pt.y;
+            OS.ClientToScreen (hwndTrack, cursorPt);
+            if ((style & DWT.VERTICAL) !is 0) {
+                cursorPt.y += height / 2;
+            }
+            else {
+                cursorPt.x += width / 2;
+            }
+            OS.SetCursorPos (cursorPt.x, cursorPt.y);
+
+            Event event = new Event ();
+            event.x = newX;
+            event.y = newY;
+            event.width = width;
+            event.height = height;
+            sendEvent (DWT.Selection, event);
+            if (isDisposed ()) return LRESULT.ZERO;
+            if (event.doit) {
+                if ((style & DWT.SMOOTH) !is 0) {
+                    setBounds (event.x, event.y, width, height);
+                }
+            }
+            return result;
+    }
+    return result;
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    return new LRESULT (OS.DLGC_STATIC);
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+
+    /* Compute the banding rectangle */
+    int hwndTrack = parent.handle;
+    POINT pt = new POINT ();
+    pt.x = (short) (lParam & 0xFFFF);
+    pt.y = (short) (lParam >> 16);
+    RECT rect = new RECT ();
+    OS.GetWindowRect (handle, rect);
+    OS.MapWindowPoints (handle, 0, pt, 1);
+    startX = pt.x - rect.left;
+    startY = pt.y - rect.top;
+    OS.MapWindowPoints (0, hwndTrack, rect, 2);
+    lastX = rect.left;
+    lastY = rect.top;
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+
+    /* The event must be sent because doit flag is used */
+    Event event = new Event ();
+    event.x = lastX;
+    event.y = lastY;
+    event.width = width;
+    event.height = height;
+    if ((style & DWT.SMOOTH) is 0) {
+        event.detail = DWT.DRAG;
+    }
+    sendEvent (DWT.Selection, event);
+    if (isDisposed ()) return LRESULT.ZERO;
+
+    /* Draw the banding rectangle */
+    if (event.doit) {
+        dragging = true;
+        lastX = event.x;
+        lastY = event.y;
+        menuShell ().bringToTop ();
+        if (isDisposed ()) return LRESULT.ZERO;
+        if (OS.IsWinCE) {
+            OS.UpdateWindow (hwndTrack);
+        } else {
+            int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN;
+            OS.RedrawWindow (hwndTrack, null, 0, flags);
+        }
+        drawBand (event.x, event.y, width, height);
+        if ((style & DWT.SMOOTH) !is 0) {
+            setBounds (event.x, event.y, width, height);
+            // widget could be disposed at this point
+        }
+    }
+    return result;
+}
+
+LRESULT WM_LBUTTONUP (int wParam, int lParam) {
+    LRESULT result = super.WM_LBUTTONUP (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+
+    /* Compute the banding rectangle */
+    if (!dragging) return result;
+    dragging = false;
+    RECT rect = new RECT ();
+    OS.GetWindowRect (handle, rect);
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+
+    /* The event must be sent because doit flag is used */
+    Event event = new Event ();
+    event.x = lastX;
+    event.y = lastY;
+    event.width = width;
+    event.height = height;
+    drawBand (event.x, event.y, width, height);
+    sendEvent (DWT.Selection, event);
+    if (isDisposed ()) return result;
+    if (event.doit) {
+        if ((style & DWT.SMOOTH) !is 0) {
+            setBounds (event.x, event.y, width, height);
+            // widget could be disposed at this point
+        }
+    }
+    return result;
+}
+
+LRESULT WM_MOUSEMOVE (int wParam, int lParam) {
+    LRESULT result = super.WM_MOUSEMOVE (wParam, lParam);
+    if (result !is null) return result;
+    if (!dragging || (wParam & OS.MK_LBUTTON) is 0) return result;
+
+    /* Compute the banding rectangle */
+    POINT pt = new POINT ();
+    pt.x = (short) (lParam & 0xFFFF);
+    pt.y = (short) (lParam >> 16);
+    int hwndTrack = parent.handle;
+    OS.MapWindowPoints (handle, hwndTrack, pt, 1);
+    RECT rect = new RECT (), clientRect = new RECT ();
+    OS.GetWindowRect (handle, rect);
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+    OS.GetClientRect (hwndTrack, clientRect);
+    int newX = lastX, newY = lastY;
+    if ((style & DWT.VERTICAL) !is 0) {
+        int clientWidth = clientRect.right - clientRect.left;
+        newX = Math.min (Math.max (0, pt.x - startX), clientWidth - width);
+    } else {
+        int clientHeight = clientRect.bottom - clientRect.top;
+        newY = Math.min (Math.max (0, pt.y - startY), clientHeight - height);
+    }
+    if (newX is lastX && newY is lastY) return result;
+    drawBand (lastX, lastY, width, height);
+
+    /* The event must be sent because doit flag is used */
+    Event event = new Event ();
+    event.x = newX;
+    event.y = newY;
+    event.width = width;
+    event.height = height;
+    if ((style & DWT.SMOOTH) is 0) {
+        event.detail = DWT.DRAG;
+    }
+    sendEvent (DWT.Selection, event);
+    if (isDisposed ()) return LRESULT.ZERO;
+    if (event.doit) {
+        lastX = event.x;
+        lastY = event.y;
+    }
+    if (OS.IsWinCE) {
+        OS.UpdateWindow (hwndTrack);
+    } else {
+        int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN;
+        OS.RedrawWindow (hwndTrack, null, 0, flags);
+    }
+    drawBand (lastX, lastY, width, height);
+    if ((style & DWT.SMOOTH) !is 0) {
+        setBounds (lastX, lastY, width, height);
+        // widget could be disposed at this point
+    }
+    return result;
+}
+
+LRESULT WM_SETCURSOR (int wParam, int lParam) {
+    LRESULT result = super.WM_SETCURSOR (wParam, lParam);
+    if (result !is null) return result;
+    int hitTest = lParam & 0xFFFF;
+    if (hitTest is OS.HTCLIENT) {
+        int hCursor = 0;
+        if ((style & DWT.HORIZONTAL) !is 0) {
+            hCursor = OS.LoadCursor (0, OS.IDC_SIZENS);
+        } else {
+            hCursor = OS.LoadCursor (0, OS.IDC_SIZEWE);
+        }
+        OS.SetCursor (hCursor);
+        return LRESULT.ONE;
+    }
+    return result;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Scale.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,512 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Scale;
+
+import dwt.widgets.Control;
+import dwt.widgets.Composite;
+
+class Scale : Control {
+    this (Composite parent, int style) ;
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionListener;
+import dwt.graphics.Point;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of the receiver represent a selectable user
+ * interface object that present a range of continuous
+ * numeric values.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>HORIZONTAL, VERTICAL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
+ * </p><p>
+ * <p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+
+public class Scale extends Control {
+    bool ignoreResize;
+    static final int TrackBarProc;
+    static final TCHAR TrackBarClass = new TCHAR (0, OS.TRACKBAR_CLASS, true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, TrackBarClass, lpWndClass);
+        TrackBarProc = lpWndClass.lpfnWndProc;
+        /*
+        * Feature in Windows.  The track bar window class
+        * does not include CS_DBLCLKS.  This mean that these
+        * controls will not get double click messages such as
+        * WM_LBUTTONDBLCLK.  The fix is to register a new
+        * window class with CS_DBLCLKS.
+        *
+        * NOTE:  Screen readers look for the exact class name
+        * of the control in order to provide the correct kind
+        * of assistance.  Therefore, it is critical that the
+        * new window class have the same name.  It is possible
+        * to register a local window class with the same name
+        * as a global class.  Since bits that affect the class
+        * are being changed, it is possible that other native
+        * code, other than DWT, could create a control with
+        * this class name, and fail unexpectedly.
+        */
+        int hInstance = OS.GetModuleHandle (null);
+        int hHeap = OS.GetProcessHeap ();
+        lpWndClass.hInstance = hInstance;
+        lpWndClass.style &= ~OS.CS_GLOBALCLASS;
+        lpWndClass.style |= OS.CS_DBLCLKS;
+        int byteCount = TrackBarClass.length () * TCHAR.sizeof;
+        int lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+        OS.MoveMemory (lpszClassName, TrackBarClass, byteCount);
+        lpWndClass.lpszClassName = lpszClassName;
+        OS.RegisterClass (lpWndClass);
+        OS.HeapFree (hHeap, 0, lpszClassName);
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#HORIZONTAL
+ * @see DWT#VERTICAL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Scale (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's value, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the user changes the receiver's value.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ */
+public void addSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.CallWindowProc (TrackBarProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.HORIZONTAL, DWT.VERTICAL, 0, 0, 0, 0);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int border = getBorderWidth ();
+    int width = border * 2, height = border * 2;
+    RECT rect = new RECT ();
+    OS.SendMessage (handle, OS.TBM_GETTHUMBRECT, 0, rect);
+    if ((style & DWT.HORIZONTAL) !is 0) {
+        width += OS.GetSystemMetrics (OS.SM_CXHSCROLL) * 10;
+        int scrollY = OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+        height += (rect.top * 2) + scrollY + (scrollY / 3);
+    } else {
+        int scrollX = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+        width += (rect.left * 2) + scrollX + (scrollX / 3);
+        height += OS.GetSystemMetrics (OS.SM_CYVSCROLL) * 10;
+    }
+    if (wHint !is DWT.DEFAULT) width = wHint + (border * 2);
+    if (hHint !is DWT.DEFAULT) height = hHint + (border * 2);
+    return new Point (width, height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state |= THEME_BACKGROUND | DRAW_BACKGROUND;
+    OS.SendMessage (handle, OS.TBM_SETRANGEMAX, 0, 100);
+    OS.SendMessage (handle, OS.TBM_SETPAGESIZE, 0, 10);
+    OS.SendMessage (handle, OS.TBM_SETTICFREQ, 10, 0);
+}
+
+int defaultForeground () {
+    return OS.GetSysColor (OS.COLOR_BTNFACE);
+}
+
+/**
+ * Returns the amount that the receiver's value will be
+ * modified by when the up/down (or right/left) arrows
+ * are pressed.
+ *
+ * @return the increment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getIncrement () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.TBM_GETLINESIZE, 0, 0);
+}
+
+/**
+ * Returns the maximum value which the receiver will allow.
+ *
+ * @return the maximum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMaximum () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.TBM_GETRANGEMAX, 0, 0);
+}
+
+/**
+ * Returns the minimum value which the receiver will allow.
+ *
+ * @return the minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMinimum () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.TBM_GETRANGEMIN, 0, 0);
+}
+
+/**
+ * Returns the amount that the receiver's value will be
+ * modified by when the page increment/decrement areas
+ * are selected.
+ *
+ * @return the page increment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getPageIncrement () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.TBM_GETPAGESIZE, 0, 0);
+}
+
+/**
+ * Returns the 'selection', which is the receiver's position.
+ *
+ * @return the selection
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelection () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.TBM_GETPOS, 0, 0);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's value.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+void setBackgroundImage (int hImage) {
+    super.setBackgroundImage (hImage);
+    /*
+    * Bug in Windows.  Changing the background color of the Scale
+    * widget and calling InvalidateRect() still draws with the old
+    * color.  The fix is to send a fake WM_SIZE event to cause
+    * it to redraw with the new background color.
+    */
+    ignoreResize = true;
+    OS.SendMessage (handle, OS.WM_SIZE, 0, 0);
+    ignoreResize = false;
+}
+
+void setBackgroundPixel (int pixel) {
+    super.setBackgroundPixel (pixel);
+    /*
+    * Bug in Windows.  Changing the background color of the Scale
+    * widget and calling InvalidateRect() still draws with the old
+    * color.  The fix is to send a fake WM_SIZE event to cause
+    * it to redraw with the new background color.
+    */
+    ignoreResize = true;
+    OS.SendMessage (handle, OS.WM_SIZE, 0, 0);
+    ignoreResize = false;
+}
+
+/**
+ * Sets the amount that the receiver's value will be
+ * modified by when the up/down (or right/left) arrows
+ * are pressed to the argument, which must be at least
+ * one.
+ *
+ * @param increment the new increment (must be greater than zero)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setIncrement (int increment) {
+    checkWidget ();
+    if (increment < 1) return;
+    int minimum = OS.SendMessage (handle, OS.TBM_GETRANGEMIN, 0, 0);
+    int maximum = OS.SendMessage (handle, OS.TBM_GETRANGEMAX, 0, 0);
+    if (increment > maximum - minimum) return;
+    OS.SendMessage (handle, OS.TBM_SETLINESIZE, 0, increment);
+}
+
+/**
+ * Sets the maximum value that the receiver will allow.  This new
+ * value will be ignored if it is not greater than the receiver's current
+ * minimum value.  If the new maximum is applied then the receiver's
+ * selection value will be adjusted if necessary to fall within its new range.
+ *
+ * @param value the new maximum, which must be greater than the current minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMaximum (int value) {
+    checkWidget ();
+    int minimum = OS.SendMessage (handle, OS.TBM_GETRANGEMIN, 0, 0);
+    if (0 <= minimum && minimum < value) {
+        OS.SendMessage (handle, OS.TBM_SETRANGEMAX, 1, value);
+    }
+}
+
+/**
+ * Sets the minimum value that the receiver will allow.  This new
+ * value will be ignored if it is negative or is not less than the receiver's
+ * current maximum value.  If the new minimum is applied then the receiver's
+ * selection value will be adjusted if necessary to fall within its new range.
+ *
+ * @param value the new minimum, which must be nonnegative and less than the current maximum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinimum (int value) {
+    checkWidget ();
+    int maximum = OS.SendMessage (handle, OS.TBM_GETRANGEMAX, 0, 0);
+    if (0 <= value && value < maximum) {
+        OS.SendMessage (handle, OS.TBM_SETRANGEMIN, 1, value);
+    }
+}
+
+/**
+ * Sets the amount that the receiver's value will be
+ * modified by when the page increment/decrement areas
+ * are selected to the argument, which must be at least
+ * one.
+ *
+ * @param pageIncrement the page increment (must be greater than zero)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setPageIncrement (int pageIncrement) {
+    checkWidget ();
+    if (pageIncrement < 1) return;
+    int minimum = OS.SendMessage (handle, OS.TBM_GETRANGEMIN, 0, 0);
+    int maximum = OS.SendMessage (handle, OS.TBM_GETRANGEMAX, 0, 0);
+    if (pageIncrement > maximum - minimum) return;
+    OS.SendMessage (handle, OS.TBM_SETPAGESIZE, 0, pageIncrement);
+    OS.SendMessage (handle, OS.TBM_SETTICFREQ, pageIncrement, 0);
+}
+
+/**
+ * Sets the 'selection', which is the receiver's value,
+ * to the argument which must be greater than or equal to zero.
+ *
+ * @param value the new selection (must be zero or greater)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (int value) {
+    checkWidget ();
+    OS.SendMessage (handle, OS.TBM_SETPOS, 1, value);
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.WS_TABSTOP | OS.TBS_BOTH | OS.TBS_AUTOTICKS;
+    if ((style & DWT.HORIZONTAL) !is 0) return bits | OS.TBS_HORZ | OS.TBS_DOWNISLEFT;
+    return bits | OS.TBS_VERT;
+}
+
+TCHAR windowClass () {
+    return TrackBarClass;
+}
+
+int windowProc () {
+    return TrackBarProc;
+}
+
+LRESULT WM_PAINT (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  For some reason, when WM_CTLCOLORSTATIC
+    * is used to implement transparency and returns a NULL brush,
+    * Windows doesn't always draw the track bar.  It seems that
+    * it is drawn correctly the first time.  It is possible that
+    * Windows double buffers the control and the double buffer
+    * strategy fails when WM_CTLCOLORSTATIC returns unexpected
+    * results.  The fix is to send a fake WM_SIZE to force it
+    * to redraw every time there is a WM_PAINT.
+    */
+    bool fixPaint = findBackgroundControl () !is null;
+    if (!fixPaint) {
+        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+            Control control = findThemeControl ();
+            fixPaint = control !is null;
+        }
+    }
+    if (fixPaint) {
+        bool redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+        if (redraw) OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+        ignoreResize = true;
+        OS.SendMessage (handle, OS.WM_SIZE, 0, 0);
+        ignoreResize = false;
+        if (redraw) {
+            OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+            OS.InvalidateRect (handle, null, false);
+        }
+    }
+    return super.WM_PAINT (wParam, lParam);
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    if (ignoreResize) return null;
+    return super.WM_SIZE (wParam, lParam);
+}
+
+LRESULT wmScrollChild (int wParam, int lParam) {
+
+    /* Do nothing when scrolling is ending */
+    int code = wParam & 0xFFFF;
+    switch (code) {
+        case OS.TB_ENDTRACK:
+        case OS.TB_THUMBPOSITION:
+            return null;
+    }
+
+    Event event = new Event ();
+    /*
+    * This code is intentionally commented.  The event
+    * detail field is not currently supported on all
+    * platforms.
+    */
+//  switch (code) {
+//      case OS.TB_TOP:         event.detail = DWT.HOME;  break;
+//      case OS.TB_BOTTOM:      event.detail = DWT.END;  break;
+//      case OS.TB_LINEDOWN:    event.detail = DWT.ARROW_DOWN;  break;
+//      case OS.TB_LINEUP:      event.detail = DWT.ARROW_UP;  break;
+//      case OS.TB_PAGEDOWN:    event.detail = DWT.PAGE_DOWN;  break;
+//      case OS.TB_PAGEUP:      event.detail = DWT.PAGE_UP;  break;
+//  }
+
+    /*
+    * Send the event because WM_HSCROLL and WM_VSCROLL
+    * are sent from a modal message loop in windows that
+    * is active when the user is scrolling.
+    */
+    sendEvent (DWT.Selection, event);
+    // widget could be disposed at this point
+    return null;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/ScrollBar.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,915 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.ScrollBar;
+
+import dwt.widgets.Widget;
+class ScrollBar : Widget {
+    this( Widget, int );
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SCROLLINFO;
+
+/**
+ * Instances of this class are selectable user interface
+ * objects that represent a range of positive, numeric values.
+ * <p>
+ * At any given moment, a given scroll bar will have a
+ * single 'selection' that is considered to be its
+ * value, which is constrained to be within the range of
+ * values the scroll bar represents (that is, between its
+ * <em>minimum</em> and <em>maximum</em> values).
+ * </p><p>
+ * Typically, scroll bars will be made up of five areas:
+ * <ol>
+ * <li>an arrow button for decrementing the value</li>
+ * <li>a page decrement area for decrementing the value by a larger amount</li>
+ * <li>a <em>thumb</em> for modifying the value by mouse dragging</li>
+ * <li>a page increment area for incrementing the value by a larger amount</li>
+ * <li>an arrow button for incrementing the value</li>
+ * </ol>
+ * Based on their style, scroll bars are either <code>HORIZONTAL</code>
+ * (which have a left facing button for decrementing the value and a
+ * right facing button for incrementing it) or <code>VERTICAL</code>
+ * (which have an upward facing button for decrementing the value
+ * and a downward facing buttons for incrementing it).
+ * </p><p>
+ * On some platforms, the size of the scroll bar's thumb can be
+ * varied relative to the magnitude of the range of values it
+ * represents (that is, relative to the difference between its
+ * maximum and minimum values). Typically, this is used to
+ * indicate some proportional value such as the ratio of the
+ * visible area of a document to the total amount of space that
+ * it would take to display it. DWT supports setting the thumb
+ * size even if the underlying platform does not, but in this
+ * case the appearance of the scroll bar will not change.
+ * </p><p>
+ * Scroll bars are created by specifying either <code>H_SCROLL</code>,
+ * <code>V_SCROLL</code> or both when creating a <code>Scrollable</code>.
+ * They are accessed from the <code>Scrollable</code> using
+ * <code>getHorizontalBar</code> and <code>getVerticalBar</code>.
+ * </p><p>
+ * Note: Scroll bars are not Controls.  On some platforms, scroll bars
+ * that appear as part of some standard controls such as a text or list
+ * have no operating system resources and are not children of the control.
+ * For this reason, scroll bars are treated specially.  To create a control
+ * that looks like a scroll bar but has operating system resources, use
+ * <code>Slider</code>.
+ * </p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>HORIZONTAL, VERTICAL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see Slider
+ * @see Scrollable
+ * @see Scrollable#getHorizontalBar
+ * @see Scrollable#getVerticalBar
+ */
+
+public class ScrollBar extends Widget {
+    Scrollable parent;
+    int increment, pageIncrement;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#HORIZONTAL
+ * @see DWT#VERTICAL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+ScrollBar (Scrollable parent, int style) {
+    super (parent, checkStyle (style));
+    this.parent = parent;
+    createWidget ();
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's value, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the event object detail field contains one of the following values:
+ * <code>DWT.NONE</code> - for the end of a drag.
+ * <code>DWT.DRAG</code>.
+ * <code>DWT.HOME</code>.
+ * <code>DWT.END</code>.
+ * <code>DWT.ARROW_DOWN</code>.
+ * <code>DWT.ARROW_UP</code>.
+ * <code>DWT.PAGE_DOWN</code>.
+ * <code>DWT.PAGE_UP</code>.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the user changes the receiver's value
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener(listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.HORIZONTAL, DWT.VERTICAL, 0, 0, 0, 0);
+}
+
+void createWidget () {
+    increment = 1;
+    pageIncrement = 10;
+    /*
+    * Do not set the initial values of the maximum
+    * or the thumb.  These values normally default
+    * to 100 and 10 but may have been set already
+    * by the widget that owns the scroll bar.  For
+    * example, a scroll bar that is created for a
+    * list widget, setting these defaults would
+    * override the initial values provided by the
+    * list widget.
+    */
+}
+
+void destroyWidget () {
+    int hwnd = hwndScrollBar (), type = scrollBarType ();
+    if (OS.IsWinCE) {
+        SCROLLINFO info = new SCROLLINFO ();
+        info.cbSize = SCROLLINFO.sizeof;
+        info.fMask = OS.SIF_RANGE | OS.SIF_PAGE;
+        info.nPage = 101;
+        info.nMax = 100;
+        info.nMin = 0;
+        OS.SetScrollInfo (hwnd, type, info, true);
+    } else {
+        OS.ShowScrollBar (hwnd, type, false);
+    }
+    releaseHandle ();
+}
+
+Rectangle getBounds () {
+//  checkWidget ();
+    parent.forceResize ();
+    RECT rect = new RECT ();
+    OS.GetClientRect (parent.scrolledHandle (), rect);
+    int x = 0, y = 0, width, height;
+    if ((style & DWT.HORIZONTAL) !is 0) {
+        y = rect.bottom - rect.top;
+        width = rect.right - rect.left;
+        height = OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    } else {
+        x = rect.right - rect.left;
+        width = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+        height = rect.bottom - rect.top;
+    }
+    return new Rectangle (x, y, width, height);
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled, and
+ * <code>false</code> otherwise. A disabled control is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #isEnabled
+ */
+public bool getEnabled () {
+    checkWidget();
+    return (state & DISABLED) is 0;
+}
+
+/**
+ * Returns the amount that the receiver's value will be
+ * modified by when the up/down (or right/left) arrows
+ * are pressed.
+ *
+ * @return the increment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getIncrement () {
+    checkWidget();
+    return increment;
+}
+
+/**
+ * Returns the maximum value which the receiver will allow.
+ *
+ * @return the maximum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMaximum () {
+    checkWidget();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_RANGE;
+    int hwnd = hwndScrollBar ();
+    int type = scrollBarType ();
+    OS.GetScrollInfo (hwnd, type, info);
+    return info.nMax;
+}
+
+/**
+ * Returns the minimum value which the receiver will allow.
+ *
+ * @return the minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMinimum () {
+    checkWidget();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_RANGE;
+    int hwnd = hwndScrollBar ();
+    int type = scrollBarType ();
+    OS.GetScrollInfo (hwnd, type, info);
+    return info.nMin;
+}
+
+/**
+ * Returns the amount that the receiver's value will be
+ * modified by when the page increment/decrement areas
+ * are selected.
+ *
+ * @return the page increment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getPageIncrement () {
+    checkWidget();
+    return pageIncrement;
+}
+
+/**
+ * Returns the receiver's parent, which must be a Scrollable.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Scrollable getParent () {
+    checkWidget();
+    return parent;
+}
+
+/**
+ * Returns the single 'selection' that is the receiver's value.
+ *
+ * @return the selection
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelection () {
+    checkWidget();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_POS;
+    int hwnd = hwndScrollBar ();
+    int type = scrollBarType ();
+    OS.GetScrollInfo (hwnd, type, info);
+    return info.nPos;
+}
+
+/**
+ * Returns a point describing the receiver's size. The
+ * x coordinate of the result is the width of the receiver.
+ * The y coordinate of the result is the height of the
+ * receiver.
+ *
+ * @return the receiver's size
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getSize () {
+    checkWidget();
+    parent.forceResize ();
+    RECT rect = new RECT ();
+    OS.GetClientRect (parent.scrolledHandle (), rect);
+    int width, height;
+    if ((style & DWT.HORIZONTAL) !is 0) {
+        width = rect.right - rect.left;
+        height = OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    } else {
+        width = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+        height = rect.bottom - rect.top;
+    }
+    return new Point (width, height);
+}
+
+/**
+ * Returns the size of the receiver's thumb relative to the
+ * difference between its maximum and minimum values.
+ *
+ * @return the thumb value
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ScrollBar
+ */
+public int getThumb () {
+    checkWidget();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_PAGE;
+    int hwnd = hwndScrollBar ();
+    int type = scrollBarType ();
+    OS.GetScrollInfo (hwnd, type, info);
+    if (info.nPage !is 0) --info.nPage;
+    return info.nPage;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible, and
+ * <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getVisible () {
+    checkWidget();
+    return (state & HIDDEN) is 0;
+}
+
+int hwndScrollBar () {
+    return parent.scrolledHandle ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled and all
+ * of the receiver's ancestors are enabled, and <code>false</code>
+ * otherwise. A disabled control is typically not selectable from the
+ * user interface and draws with an inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getEnabled
+ */
+public bool isEnabled () {
+    checkWidget();
+    return getEnabled () && parent.isEnabled ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible and all
+ * of the receiver's ancestors are visible and <code>false</code>
+ * otherwise.
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getVisible
+ */
+public bool isVisible () {
+    checkWidget();
+    return getVisible () && parent.isVisible ();
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+}
+
+void releaseParent () {
+    super.releaseParent ();
+    if (parent.horizontalBar is this) parent.horizontalBar = null;
+    if (parent.verticalBar is this) parent.verticalBar = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's value.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+int scrollBarType () {
+    return (style & DWT.VERTICAL) !is 0 ? OS.SB_VERT : OS.SB_HORZ;
+}
+
+/**
+ * Enables the receiver if the argument is <code>true</code>,
+ * and disables it otherwise. A disabled control is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ *
+ * @param enabled the new enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setEnabled (bool enabled) {
+    checkWidget();
+    /*
+    * This line is intentionally commented.  Currently
+    * always show scrollbar as being enabled and visible.
+    */
+//  if (OS.IsWinCE) error (DWT.ERROR_NOT_IMPLEMENTED);
+    if (!OS.IsWinCE) {
+        int hwnd = hwndScrollBar (), type = scrollBarType ();
+        int flags = enabled ? OS.ESB_ENABLE_BOTH : OS.ESB_DISABLE_BOTH;
+        OS.EnableScrollBar (hwnd, type, flags);
+        if (enabled) {
+            state &= ~DISABLED;
+        } else {
+            state |= DISABLED;
+        }
+    }
+}
+
+/**
+ * Sets the amount that the receiver's value will be
+ * modified by when the up/down (or right/left) arrows
+ * are pressed to the argument, which must be at least
+ * one.
+ *
+ * @param value the new increment (must be greater than zero)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setIncrement (int value) {
+    checkWidget();
+    if (value < 1) return;
+    increment = value;
+}
+
+/**
+ * Sets the maximum. If this value is negative or less than or
+ * equal to the minimum, the value is ignored. If necessary, first
+ * the thumb and then the selection are adjusted to fit within the
+ * new range.
+ *
+ * @param value the new maximum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMaximum (int value) {
+    checkWidget();
+    if (value < 0) return;
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    int hwnd = hwndScrollBar (), type = scrollBarType ();
+    info.fMask = OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL;
+    OS.GetScrollInfo (hwnd, type, info);
+    if (value - info.nMin - info.nPage < 1) return;
+    info.nMax = value;
+    SetScrollInfo (hwnd, type, info, true);
+}
+
+/**
+ * Sets the minimum value. If this value is negative or greater
+ * than or equal to the maximum, the value is ignored. If necessary,
+ * first the thumb and then the selection are adjusted to fit within
+ * the new range.
+ *
+ * @param value the new minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinimum (int value) {
+    checkWidget();
+    if (value < 0) return;
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    int hwnd = hwndScrollBar (), type = scrollBarType ();
+    info.fMask = OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL;
+    OS.GetScrollInfo (hwnd, type, info);
+    if (info.nMax - value - info.nPage < 1) return;
+    info.nMin = value;
+    SetScrollInfo (hwnd, type, info, true);
+}
+
+/**
+ * Sets the amount that the receiver's value will be
+ * modified by when the page increment/decrement areas
+ * are selected to the argument, which must be at least
+ * one.
+ *
+ * @param value the page increment (must be greater than zero)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setPageIncrement (int value) {
+    checkWidget();
+    if (value < 1) return;
+    pageIncrement = value;
+}
+
+bool SetScrollInfo (int hwnd, int flags, SCROLLINFO info, bool fRedraw) {
+    /*
+    * Bug in Windows.  For some reason, when SetScrollInfo()
+    * is used with SIF_POS and the scroll bar is hidden,
+    * the opposite scroll bar is incorrectly made visible
+    * so that the next time the parent is resized (or another
+    * scroll bar operation is performed), the opposite scroll
+    * bar draws.  The fix is to hide both scroll bars.
+    */
+    if ((state & (DISABLED | HIDDEN)) !is 0) fRedraw = false;
+    bool result = OS.SetScrollInfo (hwnd, flags, info, fRedraw);
+
+    /*
+    * Bug in Windows.  For some reason, when the widget
+    * is a standard scroll bar, and SetScrollInfo() is
+    * called with SIF_RANGE or SIF_PAGE, the widget is
+    * incorrectly made visible so that the next time the
+    * parent is resized (or another scroll bar operation
+    * is performed), the scroll bar draws.  The fix is
+    * to hide the scroll bar (again) when already hidden.
+    */
+    if ((state & HIDDEN) !is 0) {
+        /*
+        * This line is intentionally commented.  Currently
+        * always show scrollbar as being enabled and visible.
+        */
+//      if (OS.IsWinCE) error (DWT.ERROR_NOT_IMPLEMENTED);
+        if (!OS.IsWinCE) {
+            ScrollBar bar = null;
+            switch (flags) {
+                case OS.SB_HORZ:
+                    bar = parent.getVerticalBar ();
+                    break;
+                case OS.SB_VERT:
+                    bar = parent.getHorizontalBar ();
+                    break;
+            }
+            bool both = bar !is null && !bar.getVisible ();
+            OS.ShowScrollBar (hwnd, both ? OS.SB_BOTH : flags, false);
+        }
+    }
+
+    /*
+    * Feature in Windows.  Using SIF_DISABLENOSCROLL,
+    * SetScrollInfo () can change enabled and disabled
+    * state of the scroll bar causing a scroll bar that
+    * was disabled by the application to become enabled.
+    * The fix is to disable the scroll bar (again) when
+    * the application has disabled the scroll bar.
+    */
+    if ((state & DISABLED) !is 0) {
+        /*
+        * This line is intentionally commented.  Currently
+        * always show scrollbar as being enabled and visible.
+        */
+//      if (OS.IsWinCE) error (DWT.ERROR_NOT_IMPLEMENTED);
+        if (!OS.IsWinCE) {
+            OS.EnableScrollBar (hwnd, flags, OS.ESB_DISABLE_BOTH);
+        }
+    }
+    return result;
+}
+
+/**
+ * Sets the single <em>selection</em> that is the receiver's
+ * value to the argument which must be greater than or equal
+ * to zero.
+ *
+ * @param selection the new selection (must be zero or greater)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (int selection) {
+    checkWidget();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    int hwnd = hwndScrollBar (), type = scrollBarType ();
+    info.fMask = OS.SIF_POS;
+    info.nPos = selection;
+    SetScrollInfo (hwnd, type, info, true);
+}
+
+/**
+ * Sets the size of the receiver's thumb relative to the
+ * difference between its maximum and minimum values.  This new
+ * value will be ignored if it is less than one, and will be
+ * clamped if it exceeds the receiver's current range.
+ *
+ * @param value the new thumb value, which must be at least one and not
+ * larger than the size of the current range
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setThumb (int value) {
+    checkWidget();
+    if (value < 1) return;
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    int hwnd = hwndScrollBar (), type = scrollBarType ();
+    info.fMask = OS.SIF_PAGE | OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL;
+    OS.GetScrollInfo (hwnd, type, info);
+    info.nPage = value;
+    if (info.nPage !is 0) info.nPage++;
+    SetScrollInfo (hwnd, type, info, true);
+}
+
+/**
+ * Sets the receiver's selection, minimum value, maximum
+ * value, thumb, increment and page increment all at once.
+ * <p>
+ * Note: This is similar to setting the values individually
+ * using the appropriate methods, but may be implemented in a
+ * more efficient fashion on some platforms.
+ * </p>
+ *
+ * @param selection the new selection value
+ * @param minimum the new minimum value
+ * @param maximum the new maximum value
+ * @param thumb the new thumb value
+ * @param increment the new increment value
+ * @param pageIncrement the new pageIncrement value
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setValues (int selection, int minimum, int maximum, int thumb, int increment, int pageIncrement) {
+    checkWidget();
+    if (minimum < 0) return;
+    if (maximum < 0) return;
+    if (thumb < 1) return;
+    if (increment < 1) return;
+    if (pageIncrement < 1) return;
+    this.increment = increment;
+    this.pageIncrement = pageIncrement;
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_POS | OS.SIF_PAGE | OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL;
+    info.nPos = selection;
+    info.nMin = minimum;
+    info.nMax = maximum;
+    info.nPage = thumb;
+    if (info.nPage !is 0) info.nPage++;
+    int hwnd = hwndScrollBar (), type = scrollBarType ();
+    SetScrollInfo (hwnd, type, info, true);
+}
+
+/**
+ * Marks the receiver as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param visible the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setVisible (bool visible) {
+    checkWidget();
+    bool isVisible = (state & HIDDEN) is 0;
+    if (isVisible is visible) return;
+
+    /*
+    * On Windows CE, use SIF_DISABLENOSCROLL to show and
+    * hide the scroll bar when the page size is equal to
+    * the range.
+    */
+    if (OS.IsWinCE) {
+        SCROLLINFO info = new SCROLLINFO ();
+        info.cbSize = SCROLLINFO.sizeof;
+        int hwnd = hwndScrollBar (), type = scrollBarType ();
+        info.fMask = OS.SIF_RANGE | OS.SIF_PAGE;
+        if (visible) info.fMask |= OS.SIF_DISABLENOSCROLL;
+        OS.GetScrollInfo (hwnd, type, info);
+        if (info.nPage is info.nMax - info.nMin + 1) {
+            /*
+            * Bug in Windows.  When the only changed flag to
+            * SetScrollInfo () is OS.SIF_DISABLENOSCROLL,
+            * Windows does not update the scroll bar state.
+            * The fix is to increase and then decrease the
+            * maximum, causing Windows to honour the flag.
+            */
+            int max = info.nMax;
+            info.nMax++;
+            OS.SetScrollInfo (hwnd, type, info, false);
+            info.nMax = max;
+            OS.SetScrollInfo (hwnd, type, info, true);
+        } else {
+            /*
+            * This line is intentionally commented.  Currently
+            * always show scrollbar as being enabled and visible.
+            */
+//          if (OS.IsWinCE) error (DWT.ERROR_NOT_IMPLEMENTED);
+        }
+        return;
+    }
+
+    /*
+    * Set the state bits before calling ShowScrollBar ()
+    * because hiding and showing the scroll bar can cause
+    * WM_SIZE messages when the client area is resized.
+    * Setting the state before the call means that code
+    * that runs during WM_SIZE that queries the visibility
+    * of the scroll bar will get the correct value.
+    */
+    state = visible ? state & ~HIDDEN : state | HIDDEN;
+    int hwnd = hwndScrollBar (), type = scrollBarType ();
+    if (OS.ShowScrollBar (hwnd, type, visible)) {
+        /*
+        * Bug in Windows.  For some reason, when the widget
+        * is a standard scroll bar, and SetScrollInfo () is
+        * called with SIF_RANGE or SIF_PAGE while the widget
+        * is not visible, the widget is incorrectly disabled
+        * even though the values for SIF_RANGE and SIF_PAGE,
+        * when set for a visible scroll bar would not disable
+        * the scroll bar.  The fix is to enable the scroll bar
+        * when not disabled by the application and the current
+        * scroll bar ranges would cause the scroll bar to be
+        * enabled had they been set when the scroll bar was
+        * visible.
+        */
+        if ((state & DISABLED) is 0) {
+            SCROLLINFO info = new SCROLLINFO ();
+            info.cbSize = SCROLLINFO.sizeof;
+            info.fMask = OS.SIF_RANGE | OS.SIF_PAGE;
+            OS.GetScrollInfo (hwnd, type, info);
+            if (info.nMax - info.nMin - info.nPage >= 0) {
+                OS.EnableScrollBar (hwnd, type, OS.ESB_ENABLE_BOTH);
+            }
+        }
+        sendEvent (visible ? DWT.Show : DWT.Hide);
+        // widget could be disposed at this point
+    }
+}
+
+LRESULT wmScrollChild (int wParam, int lParam) {
+
+    /* Do nothing when scrolling is ending */
+    int code = wParam & 0xFFFF;
+    if (code is OS.SB_ENDSCROLL) return null;
+
+    /*
+    * Send the event because WM_HSCROLL and
+    * WM_VSCROLL are sent from a modal message
+    * loop in Windows that is active when the
+    * user is scrolling.
+    */
+    Event event = new Event ();
+    switch (code) {
+        case OS.SB_THUMBPOSITION:   event.detail = DWT.NONE;  break;
+        case OS.SB_THUMBTRACK:      event.detail = DWT.DRAG;  break;
+        case OS.SB_TOP:             event.detail = DWT.HOME;  break;
+        case OS.SB_BOTTOM:          event.detail = DWT.END;  break;
+        case OS.SB_LINEDOWN:        event.detail = DWT.ARROW_DOWN;  break;
+        case OS.SB_LINEUP:          event.detail = DWT.ARROW_UP;  break;
+        case OS.SB_PAGEDOWN:        event.detail = DWT.PAGE_DOWN;  break;
+        case OS.SB_PAGEUP:          event.detail = DWT.PAGE_UP;  break;
+    }
+    sendEvent (DWT.Selection, event);
+    // the widget could be destroyed at this point
+    return null;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Scrollable.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,424 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Scrollable;
+
+import dwt.widgets.Control;
+import dwt.widgets.Composite;
+import dwt.graphics.Rectangle;
+
+class Scrollable : Control {
+    this();
+    this( Composite, int );
+public Rectangle computeTrim (int x, int y, int width, int height) ;
+public Rectangle getClientArea () ;
+}
+
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SCROLLINFO;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * This class is the abstract superclass of all classes which
+ * represent controls that have standard scroll bars.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>H_SCROLL, V_SCROLL</dd>
+ * <dt><b>Events:</b>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ */
+
+public abstract class Scrollable extends Control {
+    ScrollBar horizontalBar, verticalBar;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+Scrollable () {
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#H_SCROLL
+ * @see DWT#V_SCROLL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Scrollable (Composite parent, int style) {
+    super (parent, style);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+}
+
+/**
+ * Given a desired <em>client area</em> for the receiver
+ * (as described by the arguments), returns the bounding
+ * rectangle which would be required to produce that client
+ * area.
+ * <p>
+ * In other words, it returns a rectangle such that, if the
+ * receiver's bounds were set to that rectangle, the area
+ * of the receiver which is capable of displaying data
+ * (that is, not covered by the "trimmings") would be the
+ * rectangle described by the arguments (relative to the
+ * receiver's parent).
+ * </p>
+ *
+ * @param x the desired x coordinate of the client area
+ * @param y the desired y coordinate of the client area
+ * @param width the desired width of the client area
+ * @param height the desired height of the client area
+ * @return the required bounds to produce the given client area
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getClientArea
+ */
+public Rectangle computeTrim (int x, int y, int width, int height) {
+    checkWidget ();
+    int scrolledHandle = scrolledHandle ();
+    RECT rect = new RECT ();
+    OS.SetRect (rect, x, y, x + width, y + height);
+    int bits1 = OS.GetWindowLong (scrolledHandle, OS.GWL_STYLE);
+    int bits2 = OS.GetWindowLong (scrolledHandle, OS.GWL_EXSTYLE);
+    OS.AdjustWindowRectEx (rect, bits1, false, bits2);
+    if (horizontalBar !is null) rect.bottom += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    if (verticalBar !is null) rect.right += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+    int nWidth = rect.right - rect.left, nHeight = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, nWidth, nHeight);
+}
+
+ScrollBar createScrollBar (int type) {
+    ScrollBar bar = new ScrollBar (this, type);
+    if ((state & CANVAS) !is 0) {
+        bar.setMaximum (100);
+        bar.setThumb (10);
+    }
+    return bar;
+}
+
+void createWidget () {
+    super.createWidget ();
+    if ((style & DWT.H_SCROLL) !is 0) horizontalBar = createScrollBar (DWT.H_SCROLL);
+    if ((style & DWT.V_SCROLL) !is 0) verticalBar = createScrollBar (DWT.V_SCROLL);
+}
+
+/**
+ * Returns a rectangle which describes the area of the
+ * receiver which is capable of displaying data (that is,
+ * not covered by the "trimmings").
+ *
+ * @return the client area
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #computeTrim
+ */
+public Rectangle getClientArea () {
+    checkWidget ();
+    forceResize ();
+    RECT rect = new RECT ();
+    int scrolledHandle = scrolledHandle ();
+    OS.GetClientRect (scrolledHandle, rect);
+    int x = rect.left, y = rect.top;
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+    if (scrolledHandle !is handle) {
+        OS.GetClientRect (handle, rect);
+        OS.MapWindowPoints(handle, scrolledHandle, rect, 2);
+        x = -rect.left;
+        y = -rect.top;
+    }
+    return new Rectangle (x, y, width, height);
+}
+
+/**
+ * Returns the receiver's horizontal scroll bar if it has
+ * one, and null if it does not.
+ *
+ * @return the horizontal scroll bar (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public ScrollBar getHorizontalBar () {
+    checkWidget ();
+    return horizontalBar;
+}
+
+/**
+ * Returns the receiver's vertical scroll bar if it has
+ * one, and null if it does not.
+ *
+ * @return the vertical scroll bar (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public ScrollBar getVerticalBar () {
+    checkWidget ();
+    return verticalBar;
+}
+
+void releaseChildren (bool destroy) {
+    if (horizontalBar !is null) {
+        horizontalBar.release (false);
+        horizontalBar = null;
+    }
+    if (verticalBar !is null) {
+        verticalBar.release (false);
+        verticalBar = null;
+    }
+    super.releaseChildren (destroy);
+}
+
+int scrolledHandle () {
+    return handle;
+}
+
+int widgetExtStyle () {
+    return super.widgetExtStyle ();
+    /*
+    * This code is intentionally commented.  In future,
+    * we may wish to support different standard Windows
+    * edge styles.  The issue here is that not all of
+    * these styles are available on the other platforms
+    * this would need to be a hint.
+    */
+//  if ((style & DWT.BORDER) !is 0) return OS.WS_EX_CLIENTEDGE;
+//  if ((style & DWT.SHADOW_IN) !is 0) return OS.WS_EX_STATICEDGE;
+//  return super.widgetExtStyle ();
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.WS_TABSTOP;
+    if ((style & DWT.H_SCROLL) !is 0) bits |= OS.WS_HSCROLL;
+    if ((style & DWT.V_SCROLL) !is 0) bits |= OS.WS_VSCROLL;
+    return bits;
+}
+
+TCHAR windowClass () {
+    return display.windowClass;
+}
+
+int windowProc () {
+    return display.windowProc;
+}
+
+LRESULT WM_HSCROLL (int wParam, int lParam) {
+    LRESULT result = super.WM_HSCROLL (wParam, lParam);
+    if (result !is null) return result;
+
+    /*
+    * Bug on WinCE.  lParam should be NULL when the message is not sent
+    * by a scroll bar control, but it contains the handle to the window.
+    * When the message is sent by a scroll bar control, it correctly
+    * contains the handle to the scroll bar.  The fix is to check for
+    * both.
+    */
+    if (horizontalBar !is null && (lParam is 0 || lParam is handle)) {
+        return wmScroll (horizontalBar, (state & CANVAS) !is 0, handle, OS.WM_HSCROLL, wParam, lParam);
+    }
+    return result;
+}
+
+LRESULT WM_MOUSEWHEEL (int wParam, int lParam) {
+    LRESULT result = super.WM_MOUSEWHEEL (wParam, lParam);
+    if (result !is null) return result;
+
+    /*
+    * Translate WM_MOUSEWHEEL to WM_VSCROLL or WM_HSCROLL.
+    */
+    if ((state & CANVAS) !is 0) {
+        if ((wParam & (OS.MK_SHIFT | OS.MK_CONTROL)) !is 0) return result;
+        bool vertical = verticalBar !is null && verticalBar.getEnabled ();
+        bool horizontal = horizontalBar !is null && horizontalBar.getEnabled ();
+        int msg = (vertical) ? OS.WM_VSCROLL : (horizontal) ? OS.WM_HSCROLL : 0;
+        if (msg is 0) return result;
+        int [] value = new int [1];
+        OS.SystemParametersInfo (OS.SPI_GETWHEELSCROLLLINES, 0, value, 0);
+        int delta = (short) (wParam >> 16);
+        int code = 0, count = 0;
+        if (value [0] is OS.WHEEL_PAGESCROLL) {
+            code = delta < 0 ? OS.SB_PAGEDOWN : OS.SB_PAGEUP;
+            count = Math.abs (delta / OS.WHEEL_DELTA);
+        } else {
+            code = delta < 0 ? OS.SB_LINEDOWN : OS.SB_LINEUP;
+            delta = Math.abs (delta);
+            if (delta < OS.WHEEL_DELTA) return result;
+            if (msg is OS.WM_VSCROLL) {
+                count = value [0] * delta / OS.WHEEL_DELTA;
+            } else {
+                count = delta / OS.WHEEL_DELTA;
+            }
+        }
+        for (int i=0; i<count; i++) {
+            OS.SendMessage (handle, msg, code, 0);
+        }
+        return LRESULT.ZERO;
+    }
+
+    /*
+    * When the native widget scrolls inside WM_MOUSEWHEEL, it
+    * may or may not send a WM_VSCROLL or WM_HSCROLL to do the
+    * actual scrolling.  This depends on the implementation of
+    * each native widget.  In order to ensure that application
+    * code is notified when the scroll bar moves, compare the
+    * scroll bar position before and after the WM_MOUSEWHEEL.
+    * If the native control sends a WM_VSCROLL or WM_HSCROLL,
+    * then the application has already been notified.  If not
+    * explicitly send the event.
+    */
+    int vPosition = verticalBar is null ? 0 : verticalBar.getSelection ();
+    int hPosition = horizontalBar is null ? 0 : horizontalBar.getSelection ();
+    int code = callWindowProc (handle, OS.WM_MOUSEWHEEL, wParam, lParam);
+    if (verticalBar !is null) {
+        int position = verticalBar.getSelection ();
+        if (position !is vPosition) {
+            Event event = new Event ();
+            event.detail = position < vPosition ? DWT.PAGE_UP : DWT.PAGE_DOWN;
+            verticalBar.sendEvent (DWT.Selection, event);
+        }
+    }
+    if (horizontalBar !is null) {
+        int position = horizontalBar.getSelection ();
+        if (position !is hPosition) {
+            Event event = new Event ();
+            event.detail = position < hPosition ? DWT.PAGE_UP : DWT.PAGE_DOWN;
+            horizontalBar.sendEvent (DWT.Selection, event);
+        }
+    }
+    return new LRESULT (code);
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    int code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam);
+    super.WM_SIZE (wParam, lParam);
+    // widget may be disposed at this point
+    if (code is 0) return LRESULT.ZERO;
+    return new LRESULT (code);
+}
+
+LRESULT WM_VSCROLL (int wParam, int lParam) {
+    LRESULT result = super.WM_VSCROLL (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug on WinCE.  lParam should be NULL when the message is not sent
+    * by a scroll bar control, but it contains the handle to the window.
+    * When the message is sent by a scroll bar control, it correctly
+    * contains the handle to the scroll bar.  The fix is to check for
+    * both.
+    */
+    if (verticalBar !is null && (lParam is 0 || lParam is handle)) {
+        return wmScroll (verticalBar, (state & CANVAS) !is 0, handle, OS.WM_VSCROLL, wParam, lParam);
+    }
+    return result;
+}
+
+LRESULT wmScroll (ScrollBar bar, bool update, int hwnd, int msg, int wParam, int lParam) {
+    LRESULT result = null;
+    if (update) {
+        int type = msg is OS.WM_HSCROLL ? OS.SB_HORZ : OS.SB_VERT;
+        SCROLLINFO info = new SCROLLINFO ();
+        info.cbSize = SCROLLINFO.sizeof;
+        info.fMask = OS.SIF_TRACKPOS | OS.SIF_POS | OS.SIF_RANGE;
+        OS.GetScrollInfo (hwnd, type, info);
+        info.fMask = OS.SIF_POS;
+        int code = wParam & 0xFFFF;
+        switch (code) {
+            case OS.SB_ENDSCROLL:  return null;
+            case OS.SB_THUMBPOSITION:
+            case OS.SB_THUMBTRACK:
+                /*
+                * Note: On WinCE, the value in SB_THUMBPOSITION is relative to nMin.
+                * Same for SB_THUMBPOSITION 'except' for the very first thumb track
+                * message which has the actual value of nMin. This is a problem when
+                * nMin is not zero.
+                */
+                info.nPos = info.nTrackPos;
+                break;
+            case OS.SB_TOP:
+                info.nPos = info.nMin;
+                break;
+            case OS.SB_BOTTOM:
+                info.nPos = info.nMax;
+                break;
+            case OS.SB_LINEDOWN:
+                info.nPos += bar.getIncrement ();
+                break;
+            case OS.SB_LINEUP:
+                int increment = bar.getIncrement ();
+                info.nPos = Math.max (info.nMin, info.nPos - increment);
+                break;
+            case OS.SB_PAGEDOWN:
+                info.nPos += bar.getPageIncrement ();
+                break;
+            case OS.SB_PAGEUP:
+                int pageIncrement = bar.getPageIncrement ();
+                info.nPos = Math.max (info.nMin, info.nPos - pageIncrement);
+                break;
+        }
+        OS.SetScrollInfo (hwnd, type, info, true);
+    } else {
+        int code = callWindowProc (hwnd, msg, wParam, lParam);
+        result = code is 0 ? LRESULT.ZERO : new LRESULT (code);
+    }
+    bar.wmScrollChild (wParam, lParam);
+    return result;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Shell.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,2306 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Shell;
+
+import dwt.widgets.Decorations;
+class Shell : Decorations {
+    void checkWidget ();
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.ShellListener;
+import dwt.graphics.Cursor;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.graphics.Region;
+import dwt.internal.win32.CREATESTRUCT;
+import dwt.internal.win32.LOGBRUSH;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MINMAXINFO;
+import dwt.internal.win32.MSG;
+import dwt.internal.win32.NMTTDISPINFO;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SHACTIVATEINFO;
+import dwt.internal.win32.SIPINFO;
+import dwt.internal.win32.STARTUPINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TOOLINFO;
+import dwt.internal.win32.WINDOWPOS;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class represent the "windows"
+ * which the desktop or "window manager" is managing.
+ * Instances that do not have a parent (that is, they
+ * are built using the constructor, which takes a
+ * <code>Display</code> as the argument) are described
+ * as <em>top level</em> shells. Instances that do have
+ * a parent are described as <em>secondary</em> or
+ * <em>dialog</em> shells.
+ * <p>
+ * Instances are always displayed in one of the maximized,
+ * minimized or normal states:
+ * <ul>
+ * <li>
+ * When an instance is marked as <em>maximized</em>, the
+ * window manager will typically resize it to fill the
+ * entire visible area of the display, and the instance
+ * is usually put in a state where it can not be resized
+ * (even if it has style <code>RESIZE</code>) until it is
+ * no longer maximized.
+ * </li><li>
+ * When an instance is in the <em>normal</em> state (neither
+ * maximized or minimized), its appearance is controlled by
+ * the style constants which were specified when it was created
+ * and the restrictions of the window manager (see below).
+ * </li><li>
+ * When an instance has been marked as <em>minimized</em>,
+ * its contents (client area) will usually not be visible,
+ * and depending on the window manager, it may be
+ * "iconified" (that is, replaced on the desktop by a small
+ * simplified representation of itself), relocated to a
+ * distinguished area of the screen, or hidden. Combinations
+ * of these changes are also possible.
+ * </li>
+ * </ul>
+ * </p><p>
+ * The <em>modality</em> of an instance may be specified using
+ * style bits. The modality style bits are used to determine
+ * whether input is blocked for other shells on the display.
+ * The <code>PRIMARY_MODAL</code> style allows an instance to block
+ * input to its parent. The <code>APPLICATION_MODAL</code> style
+ * allows an instance to block input to every other shell in the
+ * display. The <code>SYSTEM_MODAL</code> style allows an instance
+ * to block input to all shells, including shells belonging to
+ * different applications.
+ * </p><p>
+ * Note: The styles supported by this class are treated
+ * as <em>HINT</em>s, since the window manager for the
+ * desktop on which the instance is visible has ultimate
+ * control over the appearance and behavior of decorations
+ * and modality. For example, some window managers only
+ * support resizable windows and will always assume the
+ * RESIZE style, even if it is not set. In addition, if a
+ * modality style is not supported, it is "upgraded" to a
+ * more restrictive modality style that is supported. For
+ * example, if <code>PRIMARY_MODAL</code> is not supported,
+ * it would be upgraded to <code>APPLICATION_MODAL</code>.
+ * A modality style may also be "downgraded" to a less
+ * restrictive style. For example, most operating systems
+ * no longer support <code>SYSTEM_MODAL</code> because
+ * it can freeze up the desktop, so this is typically
+ * downgraded to <code>APPLICATION_MODAL</code>.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>BORDER, CLOSE, MIN, MAX, NO_TRIM, RESIZE, TITLE, ON_TOP, TOOL</dd>
+ * <dd>APPLICATION_MODAL, MODELESS, PRIMARY_MODAL, SYSTEM_MODAL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Activate, Close, Deactivate, Deiconify, Iconify</dd>
+ * </dl>
+ * Class <code>DWT</code> provides two "convenience constants"
+ * for the most commonly required style combinations:
+ * <dl>
+ * <dt><code>SHELL_TRIM</code></dt>
+ * <dd>
+ * the result of combining the constants which are required
+ * to produce a typical application top level shell: (that
+ * is, <code>CLOSE | TITLE | MIN | MAX | RESIZE</code>)
+ * </dd>
+ * <dt><code>DIALOG_TRIM</code></dt>
+ * <dd>
+ * the result of combining the constants which are required
+ * to produce a typical application dialog shell: (that
+ * is, <code>TITLE | CLOSE | BORDER</code>)
+ * </dd>
+ * </dl>
+ * </p>
+ * <p>
+ * Note: Only one of the styles APPLICATION_MODAL, MODELESS,
+ * PRIMARY_MODAL and SYSTEM_MODAL may be specified.
+ * </p><p>
+ * IMPORTANT: This class is not intended to be subclassed.
+ * </p>
+ *
+ * @see Decorations
+ * @see DWT
+ */
+public class Shell extends Decorations {
+    Menu activeMenu;
+    ToolTip [] toolTips;
+    int hIMC, hwndMDIClient, lpstrTip, toolTipHandle, balloonTipHandle;
+    int minWidth = DWT.DEFAULT, minHeight = DWT.DEFAULT;
+    int [] brushes;
+    bool showWithParent;
+    String toolTitle, balloonTitle;
+    int toolIcon, balloonIcon;
+    int windowProc;
+    Control lastActive, lockToolTipControl;
+    SHACTIVATEINFO psai;
+    Region region;
+    static /*final*/ int ToolTipProc;
+    static final int DialogProc;
+    static final TCHAR DialogClass = new TCHAR (0, OS.IsWinCE ? "Dialog" : "#32770", true);
+    final static int [] SYSTEM_COLORS = {
+        OS.COLOR_BTNFACE,
+        OS.COLOR_WINDOW,
+        OS.COLOR_BTNTEXT,
+        OS.COLOR_WINDOWTEXT,
+        OS.COLOR_HIGHLIGHT,
+        OS.COLOR_SCROLLBAR,
+    };
+    final static int BRUSHES_SIZE = 32;
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, DialogClass, lpWndClass);
+        DialogProc = lpWndClass.lpfnWndProc;
+    }
+
+/**
+ * Constructs a new instance of this class. This is equivalent
+ * to calling <code>Shell((Display) null)</code>.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public Shell () {
+    this ((Display) null);
+}
+
+/**
+ * Constructs a new instance of this class given only the style
+ * value describing its behavior and appearance. This is equivalent
+ * to calling <code>Shell((Display) null, style)</code>.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param style the style of control to construct
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#BORDER
+ * @see DWT#CLOSE
+ * @see DWT#MIN
+ * @see DWT#MAX
+ * @see DWT#RESIZE
+ * @see DWT#TITLE
+ * @see DWT#NO_TRIM
+ * @see DWT#SHELL_TRIM
+ * @see DWT#DIALOG_TRIM
+ * @see DWT#MODELESS
+ * @see DWT#PRIMARY_MODAL
+ * @see DWT#APPLICATION_MODAL
+ * @see DWT#SYSTEM_MODAL
+ */
+public Shell (int style) {
+    this ((Display) null, style);
+}
+
+/**
+ * Constructs a new instance of this class given only the display
+ * to create it on. It is created with style <code>DWT.SHELL_TRIM</code>.
+ * <p>
+ * Note: Currently, null can be passed in for the display argument.
+ * This has the effect of creating the shell on the currently active
+ * display if there is one. If there is no current display, the
+ * shell is created on a "default" display. <b>Passing in null as
+ * the display argument is not considered to be good coding style,
+ * and may not be supported in a future release of DWT.</b>
+ * </p>
+ *
+ * @param display the display to create the shell on
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public Shell (Display display) {
+    this (display, OS.IsWinCE ? DWT.NONE : DWT.SHELL_TRIM);
+}
+
+/**
+ * Constructs a new instance of this class given the display
+ * to create it on and a style value describing its behavior
+ * and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p><p>
+ * Note: Currently, null can be passed in for the display argument.
+ * This has the effect of creating the shell on the currently active
+ * display if there is one. If there is no current display, the
+ * shell is created on a "default" display. <b>Passing in null as
+ * the display argument is not considered to be good coding style,
+ * and may not be supported in a future release of DWT.</b>
+ * </p>
+ *
+ * @param display the display to create the shell on
+ * @param style the style of control to construct
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#BORDER
+ * @see DWT#CLOSE
+ * @see DWT#MIN
+ * @see DWT#MAX
+ * @see DWT#RESIZE
+ * @see DWT#TITLE
+ * @see DWT#NO_TRIM
+ * @see DWT#SHELL_TRIM
+ * @see DWT#DIALOG_TRIM
+ * @see DWT#MODELESS
+ * @see DWT#PRIMARY_MODAL
+ * @see DWT#APPLICATION_MODAL
+ * @see DWT#SYSTEM_MODAL
+ */
+public Shell (Display display, int style) {
+    this (display, null, style, 0, false);
+}
+
+Shell (Display display, Shell parent, int style, int handle, bool embedded) {
+    super ();
+    checkSubclass ();
+    if (display is null) display = Display.getCurrent ();
+    if (display is null) display = Display.getDefault ();
+    if (!display.isValidThread ()) {
+        error (DWT.ERROR_THREAD_INVALID_ACCESS);
+    }
+    if (parent !is null && parent.isDisposed ()) {
+        error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    this.style = checkStyle (style);
+    this.parent = parent;
+    this.display = display;
+    this.handle = handle;
+    if (handle !is 0 && !embedded) {
+        state |= FOREIGN_HANDLE;
+    }
+    createWidget ();
+}
+
+/**
+ * Constructs a new instance of this class given only its
+ * parent. It is created with style <code>DWT.DIALOG_TRIM</code>.
+ * <p>
+ * Note: Currently, null can be passed in for the parent.
+ * This has the effect of creating the shell on the currently active
+ * display if there is one. If there is no current display, the
+ * shell is created on a "default" display. <b>Passing in null as
+ * the parent is not considered to be good coding style,
+ * and may not be supported in a future release of DWT.</b>
+ * </p>
+ *
+ * @param parent a shell which will be the parent of the new instance
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+public Shell (Shell parent) {
+    this (parent, OS.IsWinCE ? DWT.NONE : DWT.DIALOG_TRIM);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p><p>
+ * Note: Currently, null can be passed in for the parent.
+ * This has the effect of creating the shell on the currently active
+ * display if there is one. If there is no current display, the
+ * shell is created on a "default" display. <b>Passing in null as
+ * the parent is not considered to be good coding style,
+ * and may not be supported in a future release of DWT.</b>
+ * </p>
+ *
+ * @param parent a shell which will be the parent of the new instance
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#BORDER
+ * @see DWT#CLOSE
+ * @see DWT#MIN
+ * @see DWT#MAX
+ * @see DWT#RESIZE
+ * @see DWT#TITLE
+ * @see DWT#NO_TRIM
+ * @see DWT#SHELL_TRIM
+ * @see DWT#DIALOG_TRIM
+ * @see DWT#ON_TOP
+ * @see DWT#TOOL
+ * @see DWT#MODELESS
+ * @see DWT#PRIMARY_MODAL
+ * @see DWT#APPLICATION_MODAL
+ * @see DWT#SYSTEM_MODAL
+ */
+public Shell (Shell parent, int style) {
+    this (parent !is null ? parent.display : null, parent, style, 0, false);
+}
+
+/**
+ * Invokes platform specific functionality to allocate a new shell.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Shell</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 display the display for the shell
+ * @param handle the handle for the shell
+ * @return a new shell object containing the specified display and handle
+ */
+public static Shell win32_new (Display display, int handle) {
+    return new Shell (display, null, DWT.NO_TRIM, handle, true);
+}
+
+public static Shell internal_new (Display display, int handle) {
+    return new Shell (display, null, DWT.NO_TRIM, handle, false);
+}
+
+static int checkStyle (int style) {
+    style = Decorations.checkStyle (style);
+    int mask = DWT.SYSTEM_MODAL | DWT.APPLICATION_MODAL | DWT.PRIMARY_MODAL;
+    int bits = style & ~mask;
+    if ((style & DWT.SYSTEM_MODAL) !is 0) return bits | DWT.SYSTEM_MODAL;
+    if ((style & DWT.APPLICATION_MODAL) !is 0) return bits | DWT.APPLICATION_MODAL;
+    if ((style & DWT.PRIMARY_MODAL) !is 0) return bits | DWT.PRIMARY_MODAL;
+    return bits;
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when operations are performed on the receiver,
+ * by sending the listener one of the messages defined in the
+ * <code>ShellListener</code> interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ShellListener
+ * @see #removeShellListener
+ */
+public void addShellListener (ShellListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Close,typedListener);
+    addListener (DWT.Iconify,typedListener);
+    addListener (DWT.Deiconify,typedListener);
+    addListener (DWT.Activate, typedListener);
+    addListener (DWT.Deactivate, typedListener);
+}
+
+int balloonTipHandle () {
+    if (balloonTipHandle is 0) createBalloonTipHandle ();
+    return balloonTipHandle;
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    if (hwnd is toolTipHandle || hwnd is balloonTipHandle) {
+        return OS.CallWindowProc (ToolTipProc, hwnd, msg, wParam, lParam);
+    }
+    if (hwndMDIClient !is 0) {
+        return OS.DefFrameProc (hwnd, hwndMDIClient, msg, wParam, lParam);
+    }
+    if (windowProc !is 0) {
+        return OS.CallWindowProc (windowProc, hwnd, msg, wParam, lParam);
+    }
+    if ((style & DWT.TOOL) !is 0) {
+        int trim = DWT.TITLE | DWT.CLOSE | DWT.MIN | DWT.MAX | DWT.BORDER | DWT.RESIZE;
+        if ((style & trim) is 0) return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+    }
+    if (parent !is null) {
+        switch (msg) {
+            case OS.WM_KILLFOCUS:
+            case OS.WM_SETFOCUS:
+                return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+        }
+        return OS.CallWindowProc (DialogProc, hwnd, msg, wParam, lParam);
+    }
+    return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+}
+
+/**
+ * Requests that the window manager close the receiver in
+ * the same way it would be closed when the user clicks on
+ * the "close box" or performs some other platform specific
+ * key or mouse combination that indicates the window
+ * should be removed.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT#Close
+ * @see #dispose
+ */
+public void close () {
+    checkWidget ();
+    closeWidget ();
+}
+
+void createBalloonTipHandle () {
+    balloonTipHandle = OS.CreateWindowEx (
+        0,
+        new TCHAR (0, OS.TOOLTIPS_CLASS, true),
+        null,
+        OS.TTS_ALWAYSTIP | OS.TTS_BALLOON,
+        OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
+        handle,
+        0,
+        OS.GetModuleHandle (null),
+        null);
+    if (balloonTipHandle is 0) error (DWT.ERROR_NO_HANDLES);
+    if (ToolTipProc is 0) {
+        ToolTipProc = OS.GetWindowLong (balloonTipHandle, OS.GWL_WNDPROC);
+    }
+    /*
+    * Feature in Windows.  Despite the fact that the
+    * tool tip text contains \r\n, the tooltip will
+    * not honour the new line unless TTM_SETMAXTIPWIDTH
+    * is set.  The fix is to set TTM_SETMAXTIPWIDTH to
+    * a large value.
+    */
+    OS.SendMessage (balloonTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
+    display.addControl (balloonTipHandle, this);
+    OS.SetWindowLong (balloonTipHandle, OS.GWL_WNDPROC, display.windowProc);
+}
+
+void createHandle () {
+    bool embedded = handle !is 0 && (state & FOREIGN_HANDLE) is 0;
+
+    /*
+    * On Windows 98 and NT, setting a window to be the
+    * top most window using HWND_TOPMOST can result in a
+    * parent dialog shell being moved behind its parent
+    * if the dialog has a sibling that is currently on top
+    * This only occurs using SetWindowPos (), not when the
+    * handle is created.
+    */
+    /*
+    * The following code is intentionally commented.
+    */
+//  if ((style & DWT.ON_TOP) !is 0) display.lockActiveWindow = true;
+    if (handle is 0 || embedded) {
+        super.createHandle ();
+    } else {
+        state |= CANVAS;
+        if ((style & (DWT.H_SCROLL | DWT.V_SCROLL)) is 0) {
+            state |= THEME_BACKGROUND;
+        }
+        windowProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC);
+    }
+
+    /*
+    * The following code is intentionally commented.
+    */
+//  if ((style & DWT.ON_TOP) !is 0)  display.lockActiveWindow = false;
+
+    if (!embedded) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        bits &= ~(OS.WS_OVERLAPPED | OS.WS_CAPTION);
+        if (!OS.IsWinCE) bits |= OS.WS_POPUP;
+        if ((style & DWT.TITLE) !is 0) bits |= OS.WS_CAPTION;
+        if ((style & DWT.NO_TRIM) is 0) {
+            if ((style & (DWT.BORDER | DWT.RESIZE)) is 0) bits |= OS.WS_BORDER;
+        }
+        /*
+        * Bug in Windows.  When the WS_CAPTION bits are cleared using
+        * SetWindowLong(), Windows does not resize the client area of
+        * the window to get rid of the caption until the first resize.
+        * The fix is to use SetWindowPos() with SWP_DRAWFRAME to force
+        * the frame to be redrawn and resized.
+        */
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+        int flags = OS.SWP_DRAWFRAME | OS.SWP_NOMOVE | OS.SWP_NOSIZE | OS.SWP_NOZORDER | OS.SWP_NOACTIVATE;
+        SetWindowPos (handle, 0, 0, 0, 0, 0, flags);
+        if (OS.IsWinCE) _setMaximized (true);
+        if (OS.IsPPC) {
+            psai = new SHACTIVATEINFO ();
+            psai.cbSize = SHACTIVATEINFO.sizeof;
+        }
+    }
+    if (OS.IsDBLocale) {
+        hIMC = OS.ImmCreateContext ();
+        if (hIMC !is 0) OS.ImmAssociateContext (handle, hIMC);
+    }
+}
+
+void createToolTip (ToolTip toolTip) {
+    int id = 0;
+    if (toolTips is null) toolTips = new ToolTip [4];
+    while (id < toolTips.length && toolTips [id] !is null) id++;
+    if (id is toolTips.length) {
+        ToolTip [] newToolTips = new ToolTip [toolTips.length + 4];
+        System.arraycopy (toolTips, 0, newToolTips, 0, toolTips.length);
+        toolTips = newToolTips;
+    }
+    toolTips [id] = toolTip;
+    toolTip.id = id + Display.ID_START;
+    if (OS.IsWinCE) return;
+    TOOLINFO lpti = new TOOLINFO ();
+    lpti.cbSize = TOOLINFO.sizeof;
+    lpti.hwnd = handle;
+    lpti.uId = toolTip.id;
+    lpti.uFlags = OS.TTF_TRACK;
+    lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
+    OS.SendMessage (toolTip.hwndToolTip (), OS.TTM_ADDTOOL, 0, lpti);
+}
+
+void createToolTipHandle () {
+    toolTipHandle = OS.CreateWindowEx (
+        0,
+        new TCHAR (0, OS.TOOLTIPS_CLASS, true),
+        null,
+        OS.TTS_ALWAYSTIP,
+        OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
+        handle,
+        0,
+        OS.GetModuleHandle (null),
+        null);
+    if (toolTipHandle is 0) error (DWT.ERROR_NO_HANDLES);
+    if (ToolTipProc is 0) {
+        ToolTipProc = OS.GetWindowLong (toolTipHandle, OS.GWL_WNDPROC);
+    }
+    /*
+    * Feature in Windows.  Despite the fact that the
+    * tool tip text contains \r\n, the tooltip will
+    * not honour the new line unless TTM_SETMAXTIPWIDTH
+    * is set.  The fix is to set TTM_SETMAXTIPWIDTH to
+    * a large value.
+    */
+    OS.SendMessage (toolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
+    display.addControl (toolTipHandle, this);
+    OS.SetWindowLong (toolTipHandle, OS.GWL_WNDPROC, display.windowProc);
+}
+
+void deregister () {
+    super.deregister ();
+    if (toolTipHandle !is 0) display.removeControl (toolTipHandle);
+    if (balloonTipHandle !is 0) display.removeControl (balloonTipHandle);
+}
+
+void destroyToolTip (ToolTip toolTip) {
+    if (toolTips is null) return;
+    toolTips [toolTip.id - Display.ID_START] = null;
+    if (OS.IsWinCE) return;
+    if (balloonTipHandle !is 0) {
+        TOOLINFO lpti = new TOOLINFO ();
+        lpti.cbSize = TOOLINFO.sizeof;
+        lpti.uId = toolTip.id;
+        lpti.hwnd = handle;
+        OS.SendMessage (balloonTipHandle, OS.TTM_DELTOOL, 0, lpti);
+    }
+    toolTip.id = -1;
+}
+
+void destroyWidget () {
+    fixActiveShell ();
+    super.destroyWidget ();
+}
+
+public void dispose () {
+    /*
+    * This code is intentionally commented.  On some
+    * platforms, the owner window is repainted right
+    * away when a dialog window exits.  This behavior
+    * is currently unspecified.
+    */
+//  /*
+//  * Note:  It is valid to attempt to dispose a widget
+//  * more than once.  If this happens, fail silently.
+//  */
+//  if (!isValidWidget ()) return;
+//  if (!isValidThread ()) error (DWT.ERROR_THREAD_INVALID_ACCESS);
+//  Display oldDisplay = display;
+    super.dispose ();
+    // widget is disposed at this point
+//  if (oldDisplay !is null) oldDisplay.update ();
+}
+
+void enableWidget (bool enabled) {
+    if (enabled) {
+        state &= ~DISABLED;
+    } else {
+        state |= DISABLED;
+    }
+    if (Display.TrimEnabled) {
+        if (isActive ()) setItemEnabled (OS.SC_CLOSE, enabled);
+    } else {
+        OS.EnableWindow (handle, enabled);
+    }
+}
+
+int findBrush (int value, int lbStyle) {
+    if (lbStyle is OS.BS_SOLID) {
+        for (int i=0; i<SYSTEM_COLORS.length; i++) {
+            if (value is OS.GetSysColor (SYSTEM_COLORS [i])) {
+                return OS.GetSysColorBrush (SYSTEM_COLORS [i]);
+            }
+        }
+    }
+    if (brushes is null) brushes = new int [BRUSHES_SIZE];
+    LOGBRUSH logBrush = new LOGBRUSH ();
+    for (int i=0; i<brushes.length; i++) {
+        int hBrush = brushes [i];
+        if (hBrush is 0) break;
+        OS.GetObject (hBrush, LOGBRUSH.sizeof, logBrush);
+        switch (logBrush.lbStyle) {
+            case OS.BS_SOLID:
+                if (lbStyle is OS.BS_SOLID) {
+                    if (logBrush.lbColor is value) return hBrush;
+                }
+                break;
+            case OS.BS_PATTERN:
+                if (lbStyle is OS.BS_PATTERN) {
+                    if (logBrush.lbHatch is value) return hBrush;
+                }
+                break;
+        }
+    }
+    int length = brushes.length;
+    int hBrush = brushes [--length];
+    if (hBrush !is 0) OS.DeleteObject (hBrush);
+    System.arraycopy (brushes, 0, brushes, 1, length);
+    switch (lbStyle) {
+        case OS.BS_SOLID:
+            hBrush = OS.CreateSolidBrush (value);
+            break;
+        case OS.BS_PATTERN:
+            hBrush = OS.CreatePatternBrush (value);
+            break;
+    }
+    return brushes [0] = hBrush;
+}
+
+Control findBackgroundControl () {
+    return background !is -1 || backgroundImage !is null ? this : null;
+}
+
+Cursor findCursor () {
+    return cursor;
+}
+
+Control findThemeControl () {
+    return null;
+}
+
+ToolTip findToolTip (int id) {
+    if (toolTips is null) return null;
+    id = id - Display.ID_START;
+    return 0 <= id && id < toolTips.length ? toolTips [id] : null;
+}
+
+void fixActiveShell () {
+    /*
+    * Feature in Windows.  When the active shell is disposed
+    * or hidden, Windows normally makes the parent shell active
+    * and assigns focus.  This does not happen when the parent
+    * shell is disabled.  Instead, Windows assigns focus to the
+    * next shell on the desktop (possibly a shell in another
+    * application).  The fix is to activate the disabled parent
+    * shell before disposing or hiding the active shell.
+    */
+    int hwndParent = OS.GetParent (handle);
+    if (hwndParent !is 0 && handle is OS.GetActiveWindow ()) {
+        if (!OS.IsWindowEnabled (hwndParent) && OS.IsWindowVisible (hwndParent)) {
+            OS.SetActiveWindow (hwndParent);
+        }
+    }
+}
+
+void fixShell (Shell newShell, Control control) {
+    if (this is newShell) return;
+    if (control is lastActive) setActiveControl (null);
+    String toolTipText = control.toolTipText;
+    if (toolTipText !is null) {
+        control.setToolTipText (this, null);
+        control.setToolTipText (newShell, toolTipText);
+    }
+}
+
+void fixToolTip () {
+    /*
+    * Bug in Windows.  On XP, when a tooltip is
+    * hidden due to a time out or mouse press,
+    * the tooltip remains active although no
+    * longer visible and won't show again until
+    * another tooltip becomes active.  If there
+    * is only one tooltip in the window,  it will
+    * never show again.  The fix is to remove the
+    * current tooltip and add it again every time
+    * the mouse leaves the control.
+    */
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if (toolTipHandle is 0) return;
+        TOOLINFO lpti = new TOOLINFO ();
+        lpti.cbSize = TOOLINFO.sizeof;
+        if (OS.SendMessage (toolTipHandle, OS.TTM_GETCURRENTTOOL, 0, lpti) !is 0) {
+            if ((lpti.uFlags & OS.TTF_IDISHWND) !is 0) {
+                OS.SendMessage (toolTipHandle, OS.TTM_DELTOOL, 0, lpti);
+                OS.SendMessage (toolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
+            }
+        }
+    }
+}
+
+/**
+ * If the receiver is visible, moves it to the top of the
+ * drawing order for the display on which it was created
+ * (so that all other shells on that display, which are not
+ * the receiver's children will be drawn behind it) and forces
+ * the window manager to make the shell active.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ * @see Control#moveAbove
+ * @see Control#setFocus
+ * @see Control#setVisible
+ * @see Display#getActiveShell
+ * @see Decorations#setDefaultButton(Button)
+ * @see Shell#open
+ * @see Shell#setActive
+ */
+public void forceActive () {
+    checkWidget ();
+    if(!isVisible()) return;
+    OS.SetForegroundWindow (handle);
+}
+
+void forceResize () {
+    /* Do nothing */
+}
+
+public Rectangle getBounds () {
+    checkWidget ();
+    if (!OS.IsWinCE) {
+        if (OS.IsIconic (handle)) return super.getBounds ();
+    }
+    RECT rect = new RECT ();
+    OS.GetWindowRect (handle, rect);
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+ToolTip getCurrentToolTip () {
+    if (toolTipHandle !is 0) {
+        ToolTip tip = getCurrentToolTip (toolTipHandle);
+        if (tip !is null) return tip;
+    }
+    if (balloonTipHandle !is 0) {
+        ToolTip tip = getCurrentToolTip (balloonTipHandle);
+        if (tip !is null) return tip;
+    }
+    return null;
+}
+
+ToolTip getCurrentToolTip (int hwndToolTip) {
+    if (hwndToolTip is 0) return null;
+    if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, 0) !is 0) {
+        TOOLINFO lpti = new TOOLINFO ();
+        lpti.cbSize = TOOLINFO.sizeof;
+        if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) !is 0) {
+            if ((lpti.uFlags & OS.TTF_IDISHWND) is 0) return findToolTip (lpti.uId);
+        }
+    }
+    return null;
+}
+
+public bool getEnabled () {
+    checkWidget ();
+    return (state & DISABLED) is 0;
+}
+
+/**
+ * Returns the receiver's input method editor mode. This
+ * will be the result of bitwise OR'ing together one or
+ * more of the following constants defined in class
+ * <code>DWT</code>:
+ * <code>NONE</code>, <code>ROMAN</code>, <code>DBCS</code>,
+ * <code>PHONETIC</code>, <code>NATIVE</code>, <code>ALPHA</code>.
+ *
+ * @return the IME mode
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT
+ */
+public int getImeInputMode () {
+    checkWidget ();
+    if (!OS.IsDBLocale) return 0;
+    int hIMC = OS.ImmGetContext (handle);
+    int [] lpfdwConversion = new int [1], lpfdwSentence = new int [1];
+    bool open = OS.ImmGetOpenStatus (hIMC);
+    if (open) open = OS.ImmGetConversionStatus (hIMC, lpfdwConversion, lpfdwSentence);
+    OS.ImmReleaseContext (handle, hIMC);
+    if (!open) return DWT.NONE;
+    int result = 0;
+    if ((lpfdwConversion [0] & OS.IME_CMODE_ROMAN) !is 0) result |= DWT.ROMAN;
+    if ((lpfdwConversion [0] & OS.IME_CMODE_FULLSHAPE) !is 0) result |= DWT.DBCS;
+    if ((lpfdwConversion [0] & OS.IME_CMODE_KATAKANA) !is 0) return result | DWT.PHONETIC;
+    if ((lpfdwConversion [0] & OS.IME_CMODE_NATIVE) !is 0) return result | DWT.NATIVE;
+    return result | DWT.ALPHA;
+}
+
+public Point getLocation () {
+    checkWidget ();
+    if (!OS.IsWinCE) {
+        if (OS.IsIconic (handle)) {
+            return super.getLocation ();
+        }
+    }
+    RECT rect = new RECT ();
+    OS.GetWindowRect (handle, rect);
+    return new Point (rect.left, rect.top);
+}
+
+/**
+ * Returns a point describing the minimum receiver's size. The
+ * x coordinate of the result is the minimum width of the receiver.
+ * The y coordinate of the result is the minimum height of the
+ * receiver.
+ *
+ * @return the receiver's size
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Point getMinimumSize () {
+    checkWidget ();
+    int width = Math.max (0, minWidth);
+    int trim = DWT.TITLE | DWT.CLOSE | DWT.MIN | DWT.MAX;
+    if ((style & DWT.NO_TRIM) is 0 && (style & trim) !is 0) {
+        width = Math.max (width, OS.GetSystemMetrics (OS.SM_CXMINTRACK));
+    }
+    int height = Math.max (0, minHeight);
+    if ((style & DWT.NO_TRIM) is 0 && (style & trim) !is 0) {
+        if ((style & DWT.RESIZE) !is 0) {
+            height = Math.max (height, OS.GetSystemMetrics (OS.SM_CYMINTRACK));
+        } else {
+            RECT rect = new RECT ();
+            int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+            OS.AdjustWindowRectEx (rect, bits1, false, bits2);
+            height = Math.max (height, rect.bottom - rect.top);
+        }
+    }
+    return new Point (width,  height);
+}
+
+/**
+ * Returns the region that defines the shape of the shell,
+ * or null if the shell has the default shape.
+ *
+ * @return the region that defines the shape of the shell (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ *
+ */
+public Region getRegion () {
+    checkWidget ();
+    return region;
+}
+
+public Shell getShell () {
+    checkWidget ();
+    return this;
+}
+
+public Point getSize () {
+    checkWidget ();
+    if (!OS.IsWinCE) {
+        if (OS.IsIconic (handle)) return super.getSize ();
+    }
+    RECT rect = new RECT ();
+    OS.GetWindowRect (handle, rect);
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+    return new Point (width, height);
+}
+
+/**
+ * Returns an array containing all shells which are
+ * descendants of the receiver.
+ * <p>
+ * @return the dialog shells
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Shell [] getShells () {
+    checkWidget ();
+    int count = 0;
+    Shell [] shells = display.getShells ();
+    for (int i=0; i<shells.length; i++) {
+        Control shell = shells [i];
+        do {
+            shell = shell.parent;
+        } while (shell !is null && shell !is this);
+        if (shell is this) count++;
+    }
+    int index = 0;
+    Shell [] result = new Shell [count];
+    for (int i=0; i<shells.length; i++) {
+        Control shell = shells [i];
+        do {
+            shell = shell.parent;
+        } while (shell !is null && shell !is this);
+        if (shell is this) {
+            result [index++] = shells [i];
+        }
+    }
+    return result;
+}
+
+Composite findDeferredControl () {
+    return layoutCount > 0 ? this : null;
+}
+
+public bool isEnabled () {
+    checkWidget ();
+    return getEnabled ();
+}
+
+public bool isVisible () {
+    checkWidget ();
+    return getVisible ();
+}
+
+int hwndMDIClient () {
+    if (hwndMDIClient is 0) {
+        int widgetStyle = OS.MDIS_ALLCHILDSTYLES | OS.WS_CHILD | OS.WS_CLIPCHILDREN | OS.WS_CLIPSIBLINGS;
+        hwndMDIClient = OS.CreateWindowEx (
+            0,
+            new TCHAR (0, "MDICLIENT", true),
+            null,
+            widgetStyle,
+            0, 0, 0, 0,
+            handle,
+            0,
+            OS.GetModuleHandle (null),
+            new CREATESTRUCT ());
+//      OS.ShowWindow (hwndMDIClient, OS.SW_SHOW);
+    }
+    return hwndMDIClient;
+}
+
+/**
+ * Moves the receiver to the top of the drawing order for
+ * the display on which it was created (so that all other
+ * shells on that display, which are not the receiver's
+ * children will be drawn behind it), marks it visible,
+ * sets the focus and asks the window manager to make the
+ * shell active.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Control#moveAbove
+ * @see Control#setFocus
+ * @see Control#setVisible
+ * @see Display#getActiveShell
+ * @see Decorations#setDefaultButton(Button)
+ * @see Shell#setActive
+ * @see Shell#forceActive
+ */
+public void open () {
+    checkWidget ();
+    STARTUPINFO lpStartUpInfo = Display.lpStartupInfo;
+    if (lpStartUpInfo is null || (lpStartUpInfo.dwFlags & OS.STARTF_USESHOWWINDOW) is 0) {
+        bringToTop ();
+        if (isDisposed ()) return;
+    }
+    /*
+    * Feature on WinCE PPC.  A new application becomes
+    * the foreground application only if it has at least
+    * one visible window before the event loop is started.
+    * The workaround is to explicitly force the shell to
+    * be the foreground window.
+    */
+    if (OS.IsWinCE) OS.SetForegroundWindow (handle);
+    OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
+    setVisible (true);
+    if (isDisposed ()) return;
+    /*
+    * Bug in Windows XP.  Despite the fact that an icon has been
+    * set for a window, the task bar displays the wrong icon the
+    * first time the window is made visible with ShowWindow() after
+    * a call to BringToTop(), when a long time elapses between the
+    * ShowWindow() and the time the event queue is read.  The icon
+    * in the window trimming is correct but the one in the task
+    * bar does not get updated.  The fix is to call PeekMessage()
+    * with the flag PM_NOREMOVE and PM_QS_SENDMESSAGE to respond
+    * to a cross thread WM_GETICON.
+    *
+    * NOTE: This allows other cross thread messages to be delivered,
+    * most notably WM_ACTIVATE.
+    */
+    MSG msg = new MSG ();
+    int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD | OS.PM_QS_SENDMESSAGE;
+    OS.PeekMessage (msg, 0, 0, 0, flags);
+    if (!restoreFocus () && !traverseGroup (true)) setFocus ();
+}
+
+void register () {
+    super.register ();
+    if (toolTipHandle !is 0) display.addControl (toolTipHandle, this);
+    if (balloonTipHandle !is 0) display.addControl (balloonTipHandle, this);
+}
+
+void releaseBrushes () {
+    if (brushes !is null) {
+        for (int i=0; i<brushes.length; i++) {
+            if (brushes [i] !is 0) OS.DeleteObject (brushes [i]);
+        }
+    }
+    brushes = null;
+}
+
+void releaseChildren (bool destroy) {
+    Shell [] shells = getShells ();
+    for (int i=0; i<shells.length; i++) {
+        Shell shell = shells [i];
+        if (shell !is null && !shell.isDisposed ()) {
+            shell.release (false);
+        }
+    }
+    if (toolTips !is null) {
+        for (int i=0; i<toolTips.length; i++) {
+            ToolTip toolTip = toolTips [i];
+            if (toolTip !is null && !toolTip.isDisposed ()) {
+                toolTip.release (false);
+            }
+        }
+    }
+    toolTips = null;
+    super.releaseChildren (destroy);
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    hwndMDIClient = 0;
+}
+
+void releaseParent () {
+    /* Do nothing */
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    releaseBrushes ();
+    activeMenu = null;
+    display.clearModal (this);
+    if (lpstrTip !is 0) {
+        int hHeap = OS.GetProcessHeap ();
+        OS.HeapFree (hHeap, 0, lpstrTip);
+    }
+    lpstrTip = 0;
+    toolTipHandle = balloonTipHandle = 0;
+    if (OS.IsDBLocale) {
+        if (hIMC !is 0) OS.ImmDestroyContext (hIMC);
+    }
+    lastActive = null;
+    region = null;
+    toolTitle = balloonTitle = null;
+    lockToolTipControl = null;
+}
+
+void removeMenu (Menu menu) {
+    super.removeMenu (menu);
+    if (menu is activeMenu) activeMenu = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when operations are performed on the receiver.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ShellListener
+ * @see #addShellListener
+ */
+public void removeShellListener (ShellListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Close, listener);
+    eventTable.unhook (DWT.Iconify,listener);
+    eventTable.unhook (DWT.Deiconify,listener);
+    eventTable.unhook (DWT.Activate, listener);
+    eventTable.unhook (DWT.Deactivate, listener);
+}
+
+LRESULT selectPalette (int hPalette) {
+    int hDC = OS.GetDC (handle);
+    int hOld = OS.SelectPalette (hDC, hPalette, false);
+    int result = OS.RealizePalette (hDC);
+    if (result > 0) {
+        OS.InvalidateRect (handle, null, true);
+    } else {
+        OS.SelectPalette (hDC, hOld, true);
+        OS.RealizePalette (hDC);
+    }
+    OS.ReleaseDC (handle, hDC);
+    return (result > 0) ? LRESULT.ONE : LRESULT.ZERO;
+}
+
+/**
+ * If the receiver is visible, moves it to the top of the
+ * drawing order for the display on which it was created
+ * (so that all other shells on that display, which are not
+ * the receiver's children will be drawn behind it) and asks
+ * the window manager to make the shell active
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ * @see Control#moveAbove
+ * @see Control#setFocus
+ * @see Control#setVisible
+ * @see Display#getActiveShell
+ * @see Decorations#setDefaultButton(Button)
+ * @see Shell#open
+ * @see Shell#setActive
+ */
+public void setActive () {
+    checkWidget ();
+    if (!isVisible ()) return;
+    bringToTop ();
+    // widget could be disposed at this point
+}
+
+void setActiveControl (Control control) {
+    if (control !is null && control.isDisposed ()) control = null;
+    if (lastActive !is null && lastActive.isDisposed ()) lastActive = null;
+    if (lastActive is control) return;
+
+    /*
+    * Compute the list of controls to be activated and
+    * deactivated by finding the first common parent
+    * control.
+    */
+    Control [] activate = (control is null) ? new Control [0] : control.getPath ();
+    Control [] deactivate = (lastActive is null) ? new Control [0] : lastActive.getPath ();
+    lastActive = control;
+    int index = 0, length = Math.min (activate.length, deactivate.length);
+    while (index < length) {
+        if (activate [index] !is deactivate [index]) break;
+        index++;
+    }
+
+    /*
+    * It is possible (but unlikely), that application
+    * code could have destroyed some of the widgets. If
+    * this happens, keep processing those widgets that
+    * are not disposed.
+    */
+    for (int i=deactivate.length-1; i>=index; --i) {
+        if (!deactivate [i].isDisposed ()) {
+            deactivate [i].sendEvent (DWT.Deactivate);
+        }
+    }
+    for (int i=activate.length-1; i>=index; --i) {
+        if (!activate [i].isDisposed ()) {
+            activate [i].sendEvent (DWT.Activate);
+        }
+    }
+}
+
+void setBounds (int x, int y, int width, int height, int flags, bool defer) {
+    super.setBounds (x, y, width, height, flags, false);
+}
+
+public void setEnabled (bool enabled) {
+    checkWidget ();
+    if (((state & DISABLED) is 0) is enabled) return;
+    super.setEnabled (enabled);
+    if (enabled && handle is OS.GetActiveWindow ()) {
+        if (!restoreFocus ()) traverseGroup (true);
+    }
+}
+
+/**
+ * Sets the input method editor mode to the argument which
+ * should be the result of bitwise OR'ing together one or more
+ * of the following constants defined in class <code>DWT</code>:
+ * <code>NONE</code>, <code>ROMAN</code>, <code>DBCS</code>,
+ * <code>PHONETIC</code>, <code>NATIVE</code>, <code>ALPHA</code>.
+ *
+ * @param mode the new IME mode
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT
+ */
+public void setImeInputMode (int mode) {
+    checkWidget ();
+    if (!OS.IsDBLocale) return;
+    bool imeOn = mode !is DWT.NONE && mode !is DWT.ROMAN;
+    int hIMC = OS.ImmGetContext (handle);
+    OS.ImmSetOpenStatus (hIMC, imeOn);
+    if (imeOn) {
+        int [] lpfdwConversion = new int [1], lpfdwSentence = new int [1];
+        if (OS.ImmGetConversionStatus (hIMC, lpfdwConversion, lpfdwSentence)) {
+            int newBits = 0;
+            int oldBits = OS.IME_CMODE_NATIVE | OS.IME_CMODE_KATAKANA;
+            if ((mode & DWT.PHONETIC) !is 0) {
+                newBits = OS.IME_CMODE_KATAKANA | OS.IME_CMODE_NATIVE;
+                oldBits = 0;
+            } else {
+                if ((mode & DWT.NATIVE) !is 0) {
+                    newBits = OS.IME_CMODE_NATIVE;
+                    oldBits = OS.IME_CMODE_KATAKANA;
+                }
+            }
+            if ((mode & DWT.DBCS) !is 0) {
+                newBits |= OS.IME_CMODE_FULLSHAPE;
+            } else {
+                oldBits |= OS.IME_CMODE_FULLSHAPE;
+            }
+            if ((mode & DWT.ROMAN) !is 0) {
+                newBits |= OS.IME_CMODE_ROMAN;
+            } else {
+                oldBits |= OS.IME_CMODE_ROMAN;
+            }
+            lpfdwConversion [0] |= newBits;  lpfdwConversion [0] &= ~oldBits;
+            OS.ImmSetConversionStatus (hIMC, lpfdwConversion [0], lpfdwSentence [0]);
+        }
+    }
+    OS.ImmReleaseContext (handle, hIMC);
+}
+
+/**
+ * Sets the receiver's minimum size to the size specified by the arguments.
+ * If the new minimum size is larger than the current size of the receiver,
+ * the receiver is resized to the new minimum size.
+ *
+ * @param width the new minimum width for the receiver
+ * @param height the new minimum height for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setMinimumSize (int width, int height) {
+    checkWidget ();
+    int widthLimit = 0, heightLimit = 0;
+    int trim = DWT.TITLE | DWT.CLOSE | DWT.MIN | DWT.MAX;
+    if ((style & DWT.NO_TRIM) is 0 && (style & trim) !is 0) {
+        widthLimit = OS.GetSystemMetrics (OS.SM_CXMINTRACK);
+        if ((style & DWT.RESIZE) !is 0) {
+            heightLimit = OS.GetSystemMetrics (OS.SM_CYMINTRACK);
+        } else {
+            RECT rect = new RECT ();
+            int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+            OS.AdjustWindowRectEx (rect, bits1, false, bits2);
+            heightLimit = rect.bottom - rect.top;
+        }
+    }
+    minWidth = Math.max (widthLimit, width);
+    minHeight = Math.max (heightLimit, height);
+    Point size = getSize ();
+    int newWidth = Math.max (size.x, minWidth);
+    int newHeight = Math.max (size.y, minHeight);
+    if (minWidth <= widthLimit) minWidth = DWT.DEFAULT;
+    if (minHeight <= heightLimit) minHeight = DWT.DEFAULT;
+    if (newWidth !is size.x || newHeight !is size.y) setSize (newWidth, newHeight);
+}
+
+/**
+ * Sets the receiver's minimum size to the size specified by the argument.
+ * If the new minimum size is larger than the current size of the receiver,
+ * the receiver is resized to the new minimum size.
+ *
+ * @param size the new minimum size for the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setMinimumSize (Point size) {
+    checkWidget ();
+    if (size is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setMinimumSize (size.x, size.y);
+}
+
+void setItemEnabled (int cmd, bool enabled) {
+    int hMenu = OS.GetSystemMenu (handle, false);
+    if (hMenu is 0) return;
+    int flags = OS.MF_ENABLED;
+    if (!enabled) flags = OS.MF_DISABLED | OS.MF_GRAYED;
+    OS.EnableMenuItem (hMenu, cmd, OS.MF_BYCOMMAND | flags);
+}
+
+void setParent () {
+    /* Do nothing.  Not necessary for Shells */
+}
+
+/**
+ * Sets the shape of the shell to the region specified
+ * by the argument.  When the argument is null, the
+ * default shape of the shell is restored.  The shell
+ * must be created with the style DWT.NO_TRIM in order
+ * to specify a region.
+ *
+ * @param region the region that defines the shape of the shell (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the region has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ *
+ */
+public void setRegion (Region region) {
+    checkWidget ();
+    if ((style & DWT.NO_TRIM) is 0) return;
+    if (region !is null && region.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT);
+    int hRegion = 0;
+    if (region !is null) {
+        hRegion = OS.CreateRectRgn (0, 0, 0, 0);
+        OS.CombineRgn (hRegion, region.handle, hRegion, OS.RGN_OR);
+    }
+    OS.SetWindowRgn (handle, hRegion, true);
+    this.region = region;
+}
+
+void setToolTipText (int hwnd, String text) {
+    if (OS.IsWinCE) return;
+    TOOLINFO lpti = new TOOLINFO ();
+    lpti.cbSize = TOOLINFO.sizeof;
+    lpti.hwnd = handle;
+    lpti.uId = hwnd;
+    int hwndToolTip = toolTipHandle ();
+    if (text is null) {
+        OS.SendMessage (hwndToolTip, OS.TTM_DELTOOL, 0, lpti);
+    } else {
+        if (OS.SendMessage (hwndToolTip, OS.TTM_GETTOOLINFO, 0, lpti) !is 0) {
+            OS.SendMessage (hwndToolTip, OS.TTM_UPDATE, 0, 0);
+        } else {
+            lpti.uFlags = OS.TTF_IDISHWND | OS.TTF_SUBCLASS;
+            lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
+            OS.SendMessage (hwndToolTip, OS.TTM_ADDTOOL, 0, lpti);
+        }
+    }
+}
+
+void setToolTipText (NMTTDISPINFO lpnmtdi, byte [] buffer) {
+    /*
+    * Ensure that the current position of the mouse
+    * is inside the client area of the shell.  This
+    * prevents tool tips from popping up over the
+    * shell trimmings.
+    */
+    if (!hasCursor ()) return;
+    int hHeap = OS.GetProcessHeap ();
+    if (lpstrTip !is 0) OS.HeapFree (hHeap, 0, lpstrTip);
+    int byteCount = buffer.length;
+    lpstrTip = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+    OS.MoveMemory (lpstrTip, buffer, byteCount);
+    lpnmtdi.lpszText = lpstrTip;
+}
+
+void setToolTipText (NMTTDISPINFO lpnmtdi, char [] buffer) {
+    /*
+    * Ensure that the current position of the mouse
+    * is inside the client area of the shell.  This
+    * prevents tool tips from popping up over the
+    * shell trimmings.
+    */
+    if (!hasCursor ()) return;
+    int hHeap = OS.GetProcessHeap ();
+    if (lpstrTip !is 0) OS.HeapFree (hHeap, 0, lpstrTip);
+    int byteCount = buffer.length * 2;
+    lpstrTip = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+    OS.MoveMemory (lpstrTip, buffer, byteCount);
+    lpnmtdi.lpszText = lpstrTip;
+}
+
+void setToolTipTitle (int hwndToolTip, String text, int icon) {
+    /*
+    * Bug in Windows.  For some reason, when TTM_SETTITLE
+    * is used to set the title of a tool tip, Windows leaks
+    * GDI objects.  This happens even when TTM_SETTITLE is
+    * called with TTI_NONE and NULL.  The documentation
+    * states that Windows copies the icon and that the
+    * programmer must free the copy but does not provide
+    * API to get the icon.  For example, when TTM_SETTITLE
+    * is called with ICON_ERROR, when TTM_GETTITLE is used
+    * to query the title and the icon, the uTitleBitmap
+    * field in the TTGETTITLE struct is zero.  The fix
+    * is to remember these values, only set them when then
+    * change and leak less.
+    *
+    * NOTE:  This only happens on Vista.
+    */
+    if (hwndToolTip !is toolTipHandle && hwndToolTip !is balloonTipHandle) {
+        return;
+    }
+    if (hwndToolTip is toolTipHandle) {
+        if (text is toolTitle || (toolTitle !is null && toolTitle.equals (text))) {
+            if (icon is toolIcon) return;
+        }
+        toolTitle = text;
+        toolIcon = icon;
+    } else {
+        if (hwndToolTip is balloonTipHandle) {
+            if (text is balloonTitle || (balloonTitle !is null && balloonTitle.equals (text))) {
+                if (icon is toolIcon) return;
+            }
+            balloonTitle = text;
+            balloonIcon = icon;
+        }
+    }
+    if (text !is null) {
+        TCHAR pszTitle = new TCHAR (getCodePage (), text, true);
+        OS.SendMessage (hwndToolTip, OS.TTM_SETTITLE, icon, pszTitle);
+    } else {
+        OS.SendMessage (hwndToolTip, OS.TTM_SETTITLE, 0, 0);
+    }
+}
+
+public void setVisible (bool visible) {
+    checkWidget ();
+    if (drawCount !is 0) {
+        if (((state & HIDDEN) is 0) is visible) return;
+    } else {
+        if (visible is OS.IsWindowVisible (handle)) return;
+    }
+
+    /*
+    * Feature in Windows.  When ShowWindow() is called used to hide
+    * a window, Windows attempts to give focus to the parent. If the
+    * parent is disabled by EnableWindow(), focus is assigned to
+    * another windows on the desktop.  This means that if you hide
+    * a modal window before the parent is enabled, the parent will
+    * not come to the front.  The fix is to change the modal state
+    * before hiding or showing a window so that this does not occur.
+    */
+    int mask = DWT.PRIMARY_MODAL | DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL;
+    if ((style & mask) !is 0) {
+        if (visible) {
+            display.setModalShell (this);
+            Control control = display._getFocusControl ();
+            if (control !is null && !control.isActive ()) {
+                bringToTop ();
+                if (isDisposed ()) return;
+            }
+            int hwndShell = OS.GetActiveWindow ();
+            if (hwndShell is 0) {
+                if (parent !is null) hwndShell = parent.handle;
+            }
+            if (hwndShell !is 0) {
+                OS.SendMessage (hwndShell, OS.WM_CANCELMODE, 0, 0);
+            }
+            OS.ReleaseCapture ();
+        } else {
+            display.clearModal (this);
+        }
+    } else {
+        updateModal ();
+    }
+
+    /*
+    * Bug in Windows.  Calling ShowOwnedPopups() to hide the
+    * child windows of a hidden window causes the application
+    * to be deactivated.  The fix is to call ShowOwnedPopups()
+    * to hide children before hiding the parent.
+    */
+    if (showWithParent && !visible) {
+        if (!OS.IsWinCE) OS.ShowOwnedPopups (handle, false);
+    }
+    if (!visible) fixActiveShell ();
+    super.setVisible (visible);
+    if (isDisposed ()) return;
+    if (showWithParent is visible) return;
+    showWithParent = visible;
+    if (visible) {
+        if (!OS.IsWinCE) OS.ShowOwnedPopups (handle, true);
+    }
+}
+
+void subclass () {
+    super.subclass ();
+    if (ToolTipProc !is 0) {
+        int newProc = display.windowProc;
+        if (toolTipHandle !is 0) {
+            OS.SetWindowLong (toolTipHandle, OS.GWL_WNDPROC, newProc);
+        }
+        if (balloonTipHandle !is 0) {
+            OS.SetWindowLong (balloonTipHandle, OS.GWL_WNDPROC, newProc);
+        }
+    }
+}
+
+int toolTipHandle () {
+    if (toolTipHandle is 0) createToolTipHandle ();
+    return toolTipHandle;
+}
+
+bool translateAccelerator (MSG msg) {
+    if (!isEnabled () || !isActive ()) return false;
+    if (menuBar !is null && !menuBar.isEnabled ()) return false;
+    return translateMDIAccelerator (msg) || translateMenuAccelerator (msg);
+}
+
+bool traverseEscape () {
+    if (parent is null) return false;
+    if (!isVisible () || !isEnabled ()) return false;
+    close ();
+    return true;
+}
+
+void unsubclass () {
+    super.unsubclass ();
+    if (ToolTipProc !is 0) {
+        if (toolTipHandle !is 0) {
+            OS.SetWindowLong (toolTipHandle, OS.GWL_WNDPROC, ToolTipProc);
+        }
+        if (toolTipHandle !is 0) {
+            OS.SetWindowLong (toolTipHandle, OS.GWL_WNDPROC, ToolTipProc);
+        }
+    }
+}
+
+void updateModal () {
+    if (Display.TrimEnabled) {
+        setItemEnabled (OS.SC_CLOSE, isActive ());
+    } else {
+        OS.EnableWindow (handle, isActive ());
+    }
+}
+
+CREATESTRUCT widgetCreateStruct () {
+    return null;
+}
+
+int widgetParent () {
+    if (handle !is 0) return handle;
+    return parent !is null ? parent.handle : 0;
+}
+
+int widgetExtStyle () {
+    int bits = super.widgetExtStyle () & ~OS.WS_EX_MDICHILD;
+    if ((style & DWT.TOOL) !is 0) bits |= OS.WS_EX_TOOLWINDOW;
+
+    /*
+    * Feature in Windows.  When a window that does not have a parent
+    * is created, it is automatically added to the Windows Task Bar,
+    * even when it has no title.  The fix is to use WS_EX_TOOLWINDOW
+    * which does not cause the window to appear in the Task Bar.
+    */
+    if (!OS.IsWinCE) {
+        if (parent is null) {
+            if ((style & DWT.ON_TOP) !is 0) {
+                int trim = DWT.TITLE | DWT.CLOSE | DWT.MIN | DWT.MAX;
+                if ((style & DWT.NO_TRIM) !is 0 || (style & trim) is 0) {
+                    bits |= OS.WS_EX_TOOLWINDOW;
+                }
+            }
+        }
+    }
+
+    /*
+    * Bug in Windows 98 and NT.  Creating a window with the
+    * WS_EX_TOPMOST extended style can result in a dialog shell
+    * being moved behind its parent.  The exact case where this
+    * happens is a shell with two dialog shell children where
+    * each dialog child has another hidden dialog child with
+    * the WS_EX_TOPMOST extended style.  Clicking on either of
+    * the visible dialogs causes them to become active but move
+    * to the back, behind the parent shell.  The fix is to
+    * disallow the WS_EX_TOPMOST extended style on Windows 98
+    * and NT.
+    */
+    if (parent !is null) {
+        if (OS.IsWin95) return bits;
+        if (OS.WIN32_VERSION < OS.VERSION (4, 10)) {
+            return bits;
+        }
+    }
+    if ((style & DWT.ON_TOP) !is 0) bits |= OS.WS_EX_TOPMOST;
+    return bits;
+}
+
+TCHAR windowClass () {
+    if (OS.IsSP) return DialogClass;
+    if ((style & DWT.TOOL) !is 0) {
+        int trim = DWT.TITLE | DWT.CLOSE | DWT.MIN | DWT.MAX | DWT.BORDER | DWT.RESIZE;
+        if ((style & trim) is 0) return display.windowShadowClass;
+    }
+    return parent !is null ? DialogClass : super.windowClass ();
+}
+
+int windowProc () {
+    if (windowProc !is 0) return windowProc;
+    if (OS.IsSP) return DialogProc;
+    if ((style & DWT.TOOL) !is 0) {
+        int trim = DWT.TITLE | DWT.CLOSE | DWT.MIN | DWT.MAX | DWT.BORDER | DWT.RESIZE;
+        if ((style & trim) is 0) super.windowProc ();
+    }
+    return parent !is null ? DialogProc : super.windowProc ();
+}
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    if (hwnd is toolTipHandle || hwnd is balloonTipHandle) {
+        switch (msg) {
+            case OS.WM_TIMER: {
+                if (wParam !is ToolTip.TIMER_ID) break;
+                ToolTip tip = getCurrentToolTip (hwnd);
+                if (tip !is null && tip.autoHide) {
+                    tip.setVisible (false);
+                }
+                break;
+            }
+            case OS.WM_LBUTTONDOWN: {
+                ToolTip tip = getCurrentToolTip (hwnd);
+                if (tip !is null) {
+                    tip.setVisible (false);
+                    tip.postEvent (DWT.Selection);
+                }
+                break;
+            }
+        }
+        return callWindowProc (hwnd, msg, wParam, lParam);
+    }
+    return super.windowProc (hwnd, msg, wParam, lParam);
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle ();
+    if (handle !is 0) return bits | OS.WS_CHILD;
+    bits &= ~OS.WS_CHILD;
+    /*
+    * Feature in WinCE.  Calling CreateWindowEx () with WS_OVERLAPPED
+    * and a parent window causes the new window to become a WS_CHILD of
+    * the parent instead of a dialog child.  The fix is to use WS_POPUP
+    * for a window with a parent.
+    *
+    * Feature in WinCE PPC.  A window without a parent with WS_POPUP
+    * always shows on top of the Pocket PC 'Today Screen'. The fix
+    * is to not set WS_POPUP for a window without a parent on WinCE
+    * devices.
+    *
+    * NOTE: WS_POPUP causes CreateWindowEx () to ignore CW_USEDEFAULT
+    * and causes the default window location and size to be zero.
+    */
+    if (OS.IsWinCE) {
+        if (OS.IsSP) return bits | OS.WS_POPUP;
+        return parent is null ? bits : bits | OS.WS_POPUP;
+    }
+
+    /*
+    * Use WS_OVERLAPPED for all windows, either dialog or top level
+    * so that CreateWindowEx () will respect CW_USEDEFAULT and set
+    * the default window location and size.
+    *
+    * NOTE:  When a WS_OVERLAPPED window is created, Windows gives
+    * the new window WS_CAPTION style bits.  These two constants are
+    * as follows:
+    *
+    *   WS_OVERLAPPED = 0
+    *   WS_CAPTION = WS_BORDER | WS_DLGFRAME
+    *
+    */
+    return bits | OS.WS_OVERLAPPED | OS.WS_CAPTION;
+}
+
+LRESULT WM_ACTIVATE (int wParam, int lParam) {
+    if (OS.IsPPC) {
+        /*
+        * Note: this does not work when we get WM_ACTIVATE prior
+        * to adding a listener.
+        */
+        if (hooks (DWT.HardKeyDown) || hooks (DWT.HardKeyUp)) {
+            int fActive = wParam & 0xFFFF;
+            int hwnd = fActive !is 0 ? handle : 0;
+            for (int bVk=OS.VK_APP1; bVk<=OS.VK_APP6; bVk++) {
+                OS.SHSetAppKeyWndAssoc ((byte) bVk, hwnd);
+            }
+        }
+        /* Restore SIP state when window is activated */
+        if ((wParam & 0xFFFF) !is 0) {
+            OS.SHSipPreference (handle, psai.fSipUp is 0 ? OS.SIP_DOWN : OS.SIP_UP);
+        }
+    }
+
+    /*
+    * Bug in Windows XP.  When a Shell is deactivated, the
+    * IME composition window does not go away. This causes
+    * repaint issues.  The fix is to close the IME to cause
+    * the composition string to be committed.
+    *
+    * Note. The IME needs to be reopened in order to preserve
+    * the input method status.
+    */
+    if (OS.WIN32_VERSION >= OS.VERSION (5, 1)) {
+        if ((wParam & 0xFFFF) is 0 && OS.IsDBLocale && hIMC !is 0) {
+            if (OS.ImmGetOpenStatus(hIMC)) {
+                OS.ImmSetOpenStatus (hIMC, false);
+                OS.ImmSetOpenStatus (hIMC, true);
+            }
+        }
+    }
+
+    /* Process WM_ACTIVATE */
+    LRESULT result = super.WM_ACTIVATE (wParam, lParam);
+    if ((wParam & 0xFFFF) is 0) {
+        if (lParam is 0 || (lParam !is toolTipHandle && lParam !is balloonTipHandle)) {
+            ToolTip tip = getCurrentToolTip ();
+            if (tip !is null) tip.setVisible (false);
+        }
+    }
+    return parent !is null ? LRESULT.ZERO : result;
+}
+
+LRESULT WM_COMMAND (int wParam, int lParam) {
+    if (OS.IsPPC) {
+        /*
+        * Note in WinCE PPC:  Close the Shell when the "Done Button" has
+        * been pressed. lParam is either 0 (PocketPC 2002) or the handle
+        * to the Shell (PocketPC).
+        */
+        int loWord = wParam & 0xFFFF;
+        if (loWord is OS.IDOK && (lParam is 0 || lParam is handle)) {
+            OS.PostMessage (handle, OS.WM_CLOSE, 0, 0);
+            return LRESULT.ZERO;
+        }
+    }
+    /*
+    * Feature in Windows.  On PPC, the menu is not actually an HMENU.
+    * By observation, it is a tool bar that is configured to look like
+    * a menu.  Therefore, when the PPC menu sends WM_COMMAND messages,
+    * lParam is not zero because the WM_COMMAND was not sent from a menu.
+    * Sub menu item events originate from the menu bar.  Top menu items
+    * events originate from a tool bar.  The fix is to detect the source
+    * of the WM_COMMAND and set lParam to zero to pretend that the message
+    * came from a real Windows menu, not a tool bar.
+    */
+    if (OS.IsPPC || OS.IsSP) {
+        if (menuBar !is null) {
+            int hwndCB = menuBar.hwndCB;
+            if (lParam !is 0 && hwndCB !is 0) {
+                if (lParam is hwndCB) {
+                    return super.WM_COMMAND (wParam, 0);
+                } else {
+                    int hwndChild = OS.GetWindow (hwndCB, OS.GW_CHILD);
+                    if (lParam is hwndChild) return super.WM_COMMAND (wParam, 0);
+                }
+            }
+        }
+    }
+    return super.WM_COMMAND (wParam, lParam);
+}
+
+LRESULT WM_DESTROY (int wParam, int lParam) {
+    LRESULT result = super.WM_DESTROY (wParam, lParam);
+    /*
+    * When the shell is a WS_CHILD window of a non-DWT
+    * window, the destroy code does not get called because
+    * the non-DWT window does not call dispose ().  Instead,
+    * the destroy code is called here in WM_DESTROY.
+    */
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.WS_CHILD) !is 0) {
+        releaseParent ();
+        release (false);
+    }
+    return result;
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  When a shell is resized by dragging
+    * the resize handles, Windows temporarily fills in black
+    * rectangles where the new contents of the shell should
+    * draw.  The fix is to always draw the background of shells.
+    *
+    * NOTE: This only happens on Vista.
+    */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        drawBackground (wParam);
+        return LRESULT.ONE;
+    }
+    return result;
+}
+
+LRESULT WM_ENTERIDLE (int wParam, int lParam) {
+    LRESULT result = super.WM_ENTERIDLE (wParam, lParam);
+    if (result !is null) return result;
+    if (OS.IsWinCE && display.runMessages) {
+        if (display.runAsyncMessages (true)) display.wakeThread ();
+    }
+    return result;
+}
+
+LRESULT WM_GETMINMAXINFO (int wParam, int lParam) {
+    LRESULT result = super.WM_GETMINMAXINFO (wParam, lParam);
+    if (result !is null) return result;
+    if (minWidth !is DWT.DEFAULT || minHeight !is DWT.DEFAULT) {
+        MINMAXINFO info = new MINMAXINFO ();
+        OS.MoveMemory (info, lParam, MINMAXINFO.sizeof);
+        if (minWidth !is DWT.DEFAULT) info.ptMinTrackSize_x = minWidth;
+        if (minHeight !is DWT.DEFAULT) info.ptMinTrackSize_y = minHeight;
+        OS.MoveMemory (lParam, info, MINMAXINFO.sizeof);
+        return LRESULT.ZERO;
+    }
+    return result;
+}
+
+LRESULT WM_MOUSEACTIVATE (int wParam, int lParam) {
+    LRESULT result = super.WM_MOUSEACTIVATE (wParam, lParam);
+    if (result !is null) return result;
+
+    /*
+    * Check for WM_MOUSEACTIVATE when an MDI shell is active
+    * and stop the normal shell activation but allow the mouse
+    * down to be delivered.
+    */
+    int hittest = (short) (lParam & 0xFFFF);
+    switch (hittest) {
+        case OS.HTERROR:
+        case OS.HTTRANSPARENT:
+        case OS.HTNOWHERE:
+            break;
+        default: {
+            Control control = display._getFocusControl ();
+            if (control !is null) {
+                Decorations decorations = control.menuShell ();
+                if (decorations.getShell () is this && decorations !is this) {
+                    display.ignoreRestoreFocus = true;
+                    display.lastHittest = hittest;
+                    display.lastHittestControl = null;
+                    if (hittest is OS.HTMENU || hittest is OS.HTSYSMENU) {
+                        display.lastHittestControl = control;
+                        return null;
+                    }
+                    if (OS.IsWin95 && hittest is OS.HTCAPTION) {
+                        display.lastHittestControl = control;
+                    }
+                    return new LRESULT (OS.MA_NOACTIVATE);
+                }
+            }
+        }
+    }
+    if (hittest is OS.HTMENU) return null;
+
+    /*
+    * Get the current location of the cursor,
+    * not the location of the cursor when the
+    * WM_MOUSEACTIVATE was generated.  This is
+    * strictly incorrect but is necessary in
+    * order to support Activate and Deactivate
+    * events for embedded widgets that have
+    * their own event loop.  In that case, the
+    * cursor location reported by GetMessagePos()
+    * is the one for our event loop, not the
+    * embedded widget's event loop.
+    */
+    POINT pt = new POINT ();
+    if (!OS.GetCursorPos (pt)) {
+        int pos = OS.GetMessagePos ();
+        pt.x = (short) (pos & 0xFFFF);
+        pt.y = (short) (pos >> 16);
+    }
+    int hwnd = OS.WindowFromPoint (pt);
+    if (hwnd is 0) return null;
+    Control control = display.findControl (hwnd);
+
+    /*
+    * When a shell is created with DWT.ON_TOP and DWT.NO_FOCUS,
+    * do not activate the shell when the user clicks on the
+    * the client area or on the border or a control within the
+    * shell that does not take focus.
+    */
+    if (control !is null && (control.state & CANVAS) !is 0) {
+        if ((control.style & DWT.NO_FOCUS) !is 0) {
+            int bits = DWT.ON_TOP | DWT.NO_FOCUS;
+            if ((style & bits) is bits) {
+                if (hittest is OS.HTBORDER || hittest is OS.HTCLIENT) {
+                    return new LRESULT (OS.MA_NOACTIVATE);
+                }
+            }
+        }
+    }
+
+    setActiveControl (control);
+    return null;
+}
+
+LRESULT WM_MOVE (int wParam, int lParam) {
+    LRESULT result = super.WM_MOVE (wParam, lParam);
+    if (result !is null) return result;
+    ToolTip tip = getCurrentToolTip ();
+    if (tip !is null) tip.setVisible (false);
+    return result;
+}
+
+LRESULT WM_NCACTIVATE (int wParam, int lParam) {
+    Display display = this.display;
+    LRESULT result = super.WM_NCACTIVATE (wParam, lParam);
+    if (display.isXMouseActive ()) {
+        if (lockToolTipControl !is null) {
+            if (OS.GetAsyncKeyState (OS.VK_LBUTTON) < 0) return result;
+            if (OS.GetAsyncKeyState (OS.VK_MBUTTON) < 0) return result;
+            if (OS.GetAsyncKeyState (OS.VK_RBUTTON) < 0) return result;
+            if (OS.GetAsyncKeyState (OS.VK_XBUTTON1) < 0) return result;
+            if (OS.GetAsyncKeyState (OS.VK_XBUTTON2) < 0) return result;
+            return LRESULT.ZERO;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_NCHITTEST (int wParam, int lParam) {
+    if (!OS.IsWindowEnabled (handle)) return null;
+    if (!isEnabled () || !isActive ()) {
+        if (!Display.TrimEnabled) return new LRESULT (OS.HTNOWHERE);
+        int hittest = callWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam);
+        if (hittest is OS.HTCLIENT || hittest is OS.HTMENU) hittest = OS.HTBORDER;
+        return new LRESULT (hittest);
+    }
+    if (menuBar !is null && !menuBar.getEnabled ()) {
+        int hittest = callWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam);
+        if (hittest is OS.HTMENU) hittest = OS.HTBORDER;
+        return new LRESULT (hittest);
+    }
+    return null;
+}
+
+LRESULT WM_NCLBUTTONDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_NCLBUTTONDOWN (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * When the normal activation was interrupted in WM_MOUSEACTIVATE
+    * because the active shell was an MDI shell, set the active window
+    * to the top level shell but lock the active window and stop focus
+    * changes.  This allows the user to interact the top level shell
+    * in the normal manner.
+    */
+    if (!display.ignoreRestoreFocus) return result;
+    Display display = this.display;
+    int hwndActive = 0;
+    bool fixActive = OS.IsWin95 && display.lastHittest is OS.HTCAPTION;
+    if (fixActive) hwndActive = OS.SetActiveWindow (handle);
+    display.lockActiveWindow = true;
+    int code = callWindowProc (handle, OS.WM_NCLBUTTONDOWN, wParam, lParam);
+    display.lockActiveWindow = false;
+    if (fixActive) OS.SetActiveWindow (hwndActive);
+    Control focusControl = display.lastHittestControl;
+    if (focusControl !is null && !focusControl.isDisposed ()) {
+        focusControl.setFocus ();
+    }
+    display.lastHittestControl = null;
+    display.ignoreRestoreFocus = false;
+    return new LRESULT (code);
+}
+
+LRESULT WM_PALETTECHANGED (int wParam, int lParam) {
+    if (wParam !is handle) {
+        int hPalette = display.hPalette;
+        if (hPalette !is 0) return selectPalette (hPalette);
+    }
+    return super.WM_PALETTECHANGED (wParam, lParam);
+}
+
+LRESULT WM_QUERYNEWPALETTE (int wParam, int lParam) {
+    int hPalette = display.hPalette;
+    if (hPalette !is 0) return selectPalette (hPalette);
+    return super.WM_QUERYNEWPALETTE (wParam, lParam);
+}
+
+LRESULT WM_SETCURSOR (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When the shell is disabled
+    * by a Windows standard dialog (like a MessageBox
+    * or FileDialog), clicking in the shell does not
+    * bring the shell or the dialog to the front. The
+    * fix is to detect this case and bring the shell
+    * forward.
+    */
+    int msg = (short) (lParam >> 16);
+    if (msg is OS.WM_LBUTTONDOWN) {
+        if (!Display.TrimEnabled) {
+            Shell modalShell = display.getModalShell ();
+            if (modalShell !is null && !isActive ()) {
+                int hwndModal = modalShell.handle;
+                if (OS.IsWindowEnabled (hwndModal)) {
+                    OS.SetActiveWindow (hwndModal);
+                }
+            }
+        }
+        if (!OS.IsWindowEnabled (handle)) {
+            if (!OS.IsWinCE) {
+                int hwndPopup = OS.GetLastActivePopup (handle);
+                if (hwndPopup !is 0 && hwndPopup !is handle) {
+                    if (display.getControl (hwndPopup) is null) {
+                        if (OS.IsWindowEnabled (hwndPopup)) {
+                            OS.SetActiveWindow (hwndPopup);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    /*
+    * When the shell that contains a cursor is disabled,
+    * WM_SETCURSOR is called with HTERROR.  Normally,
+    * when a control is disabled, the parent will get
+    * mouse and cursor events.  In the case of a disabled
+    * shell, there is no enabled parent.  In order to
+    * show the cursor when a shell is disabled, it is
+    * necessary to override WM_SETCURSOR when called
+    * with HTERROR to set the cursor but only when the
+    * mouse is in the client area of the shell.
+    */
+    int hitTest = (short) (lParam & 0xFFFF);
+    if (hitTest is OS.HTERROR) {
+        if (!getEnabled ()) {
+            Control control = display.getControl (wParam);
+            if (control is this && cursor !is null) {
+                POINT pt = new POINT ();
+                int pos = OS.GetMessagePos ();
+                pt.x = (short) (pos & 0xFFFF);
+                pt.y = (short) (pos >> 16);
+                OS.ScreenToClient (handle, pt);
+                RECT rect = new RECT ();
+                OS.GetClientRect (handle, rect);
+                if (OS.PtInRect (rect, pt)) {
+                    OS.SetCursor (cursor.handle);
+                    switch (msg) {
+                        case OS.WM_LBUTTONDOWN:
+                        case OS.WM_RBUTTONDOWN:
+                        case OS.WM_MBUTTONDOWN:
+                        case OS.WM_XBUTTONDOWN:
+                            OS.MessageBeep (OS.MB_OK);
+                    }
+                    return LRESULT.ONE;
+                }
+            }
+        }
+    }
+    return super.WM_SETCURSOR (wParam, lParam);
+}
+
+LRESULT WM_SETTINGCHANGE (int wParam, int lParam) {
+    LRESULT result = super.WM_SETTINGCHANGE (wParam, lParam);
+    if (result !is null) return result;
+    if (OS.IsPPC) {
+        if (wParam is OS.SPI_SETSIPINFO) {
+            /*
+            * The SIP is in a new state.  Cache its new value.
+            * Resize the Shell if it has the style DWT.RESIZE.
+            * Note that SHHandleWMSettingChange resizes the
+            * Shell and also updates the cached state.
+            */
+            if ((style & DWT.RESIZE) !is 0) {
+                OS.SHHandleWMSettingChange (handle, wParam, lParam, psai);
+                return LRESULT.ZERO;
+            } else {
+                SIPINFO pSipInfo = new SIPINFO ();
+                pSipInfo.cbSize = SIPINFO.sizeof;
+                OS.SipGetInfo (pSipInfo);
+                psai.fSipUp = pSipInfo.fdwFlags & OS.SIPF_ON;
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_SHOWWINDOW (int wParam, int lParam) {
+    LRESULT result = super.WM_SHOWWINDOW (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug in Windows.  If the shell is hidden while the parent
+    * is iconic,  Windows shows the shell when the parent is
+    * deiconified.  This does not happen if the shell is hidden
+    * while the parent is not an icon.  The fix is to track
+    * visible state for the shell and refuse to show the shell
+    * when the parent is shown.
+    */
+    if (lParam is OS.SW_PARENTOPENING) {
+        Control control = this;
+        while (control !is null) {
+            Shell shell = control.getShell ();
+            if (!shell.showWithParent) return LRESULT.ZERO;
+            control = control.parent;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_SYSCOMMAND (int wParam, int lParam) {
+    LRESULT result = super.WM_SYSCOMMAND (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  When the last visible window in
+    * a process minimized, Windows swaps out the memory for
+    * the process.  The assumption is that the user can no
+    * longer interact with the window, so the memory can be
+    * released to other applications.  However, for programs
+    * that use a lot of memory, swapping the memory back in
+    * can take a long time, sometimes minutes.  The fix is
+    * to intercept WM_SYSCOMMAND looking for SC_MINIMIZE
+    * and use ShowWindow() with SW_SHOWMINIMIZED to minimize
+    * the window, rather than running the default window proc.
+    *
+    * NOTE:  The default window proc activates the next
+    * top-level window in the Z-order while ShowWindow()
+    * with SW_SHOWMINIMIZED does not.  There is no fix for
+    * this at this time.
+    */
+    if (OS.IsWinNT) {
+        int cmd = wParam & 0xFFF0;
+        switch (cmd) {
+            case OS.SC_MINIMIZE:
+                long memory = Runtime.getRuntime ().totalMemory ();
+                if (memory >= 32 * 1024 * 1024) {
+                    OS.ShowWindow (handle, OS.SW_SHOWMINIMIZED);
+                    return LRESULT.ZERO;
+                }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_WINDOWPOSCHANGING (int wParam, int lParam) {
+    LRESULT result = super.WM_WINDOWPOSCHANGING (wParam,lParam);
+    if (result !is null) return result;
+    WINDOWPOS lpwp = new WINDOWPOS ();
+    OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
+    if ((lpwp.flags & OS.SWP_NOSIZE) is 0) {
+        lpwp.cx = Math.max (lpwp.cx, minWidth);
+        int trim = DWT.TITLE | DWT.CLOSE | DWT.MIN | DWT.MAX;
+        if ((style & DWT.NO_TRIM) is 0 && (style & trim) !is 0) {
+            lpwp.cx = Math.max (lpwp.cx, OS.GetSystemMetrics (OS.SM_CXMINTRACK));
+        }
+        lpwp.cy = Math.max (lpwp.cy, minHeight);
+        if ((style & DWT.NO_TRIM) is 0 && (style & trim) !is 0) {
+            if ((style & DWT.RESIZE) !is 0) {
+                lpwp.cy = Math.max (lpwp.cy, OS.GetSystemMetrics (OS.SM_CYMINTRACK));
+            } else {
+                RECT rect = new RECT ();
+                int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE);
+                int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+                OS.AdjustWindowRectEx (rect, bits1, false, bits2);
+                lpwp.cy = Math.max (lpwp.cy, rect.bottom - rect.top);
+            }
+        }
+        OS.MoveMemory (lParam, lpwp, WINDOWPOS.sizeof);
+    }
+    return result;
+}
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Slider.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,796 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Slider;
+
+import dwt.widgets.Control;
+import dwt.widgets.Composite;
+class Slider : Control {
+    this (Composite parent, int style);
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Point;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.SCROLLINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class are selectable user interface
+ * objects that represent a range of positive, numeric values.
+ * <p>
+ * At any given moment, a given slider will have a
+ * single 'selection' that is considered to be its
+ * value, which is constrained to be within the range of
+ * values the slider represents (that is, between its
+ * <em>minimum</em> and <em>maximum</em> values).
+ * </p><p>
+ * Typically, sliders will be made up of five areas:
+ * <ol>
+ * <li>an arrow button for decrementing the value</li>
+ * <li>a page decrement area for decrementing the value by a larger amount</li>
+ * <li>a <em>thumb</em> for modifying the value by mouse dragging</li>
+ * <li>a page increment area for incrementing the value by a larger amount</li>
+ * <li>an arrow button for incrementing the value</li>
+ * </ol>
+ * Based on their style, sliders are either <code>HORIZONTAL</code>
+ * (which have a left facing button for decrementing the value and a
+ * right facing button for incrementing it) or <code>VERTICAL</code>
+ * (which have an upward facing button for decrementing the value
+ * and a downward facing buttons for incrementing it).
+ * </p><p>
+ * On some platforms, the size of the slider's thumb can be
+ * varied relative to the magnitude of the range of values it
+ * represents (that is, relative to the difference between its
+ * maximum and minimum values). Typically, this is used to
+ * indicate some proportional value such as the ratio of the
+ * visible area of a document to the total amount of space that
+ * it would take to display it. DWT supports setting the thumb
+ * size even if the underlying platform does not, but in this
+ * case the appearance of the slider will not change.
+ * </p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>HORIZONTAL, VERTICAL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see ScrollBar
+ */
+public class Slider extends Control {
+    int increment, pageIncrement;
+    bool ignoreFocus;
+    static final int ScrollBarProc;
+    static final TCHAR ScrollBarClass = new TCHAR (0, "SCROLLBAR", true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, ScrollBarClass, lpWndClass);
+        ScrollBarProc = lpWndClass.lpfnWndProc;
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#HORIZONTAL
+ * @see DWT#VERTICAL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Slider (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's value, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the event object detail field contains one of the following values:
+ * <code>DWT.NONE</code> - for the end of a drag.
+ * <code>DWT.DRAG</code>.
+ * <code>DWT.HOME</code>.
+ * <code>DWT.END</code>.
+ * <code>DWT.ARROW_DOWN</code>.
+ * <code>DWT.ARROW_UP</code>.
+ * <code>DWT.PAGE_DOWN</code>.
+ * <code>DWT.PAGE_UP</code>.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the user changes the receiver's value
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener(listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    /*
+    * Feature in Windows.  Windows runs a modal message
+    * loop when the user drags a scroll bar.  This means
+    * that mouse down events won't get delivered until
+    * after the loop finishes.  The fix is to run any
+    * deferred messages, including mouse down messages
+    * before calling the scroll bar window proc.
+    */
+    switch (msg) {
+        case OS.WM_LBUTTONDOWN:
+        case OS.WM_LBUTTONDBLCLK:
+            display.runDeferredEvents ();
+    }
+    return OS.CallWindowProc (ScrollBarProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.HORIZONTAL, DWT.VERTICAL, 0, 0, 0, 0);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int border = getBorderWidth ();
+    int width = border * 2, height = border * 2;
+    if ((style & DWT.HORIZONTAL) !is 0) {
+        width += OS.GetSystemMetrics (OS.SM_CXHSCROLL) * 10;
+        height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    } else {
+        width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+        height += OS.GetSystemMetrics (OS.SM_CYVSCROLL) * 10;
+    }
+    if (wHint !is DWT.DEFAULT) width = wHint + (border * 2);
+    if (hHint !is DWT.DEFAULT) height = hHint + (border * 2);
+    return new Point (width, height);
+}
+
+void createWidget () {
+    super.createWidget ();
+    increment = 1;
+    pageIncrement = 10;
+    /*
+    * Set the initial values of the maximum
+    * to 100 and the thumb to 10.  Note that
+    * info.nPage needs to be 11 in order to
+    * get a thumb that is 10.
+    */
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_ALL;
+    info.nMax = 100;
+    info.nPage = 11;
+    OS.SetScrollInfo (handle, OS.SB_CTL, info, true);
+}
+
+int defaultBackground () {
+    return OS.GetSysColor (OS.COLOR_SCROLLBAR);
+}
+
+int defaultForeground () {
+    return OS.GetSysColor (OS.COLOR_BTNFACE);
+}
+
+void enableWidget (bool enabled) {
+    super.enableWidget (enabled);
+    if (!OS.IsWinCE) {
+        int flags = enabled ? OS.ESB_ENABLE_BOTH : OS.ESB_DISABLE_BOTH;
+        OS.EnableScrollBar (handle, OS.SB_CTL, flags);
+    }
+    if (enabled) {
+        state &= ~DISABLED;
+    } else {
+        state |= DISABLED;
+    }
+}
+
+public bool getEnabled () {
+    checkWidget ();
+    return (state & DISABLED) is 0;
+}
+
+/**
+ * Returns the amount that the receiver's value will be
+ * modified by when the up/down (or right/left) arrows
+ * are pressed.
+ *
+ * @return the increment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getIncrement () {
+    checkWidget ();
+    return increment;
+}
+
+/**
+ * Returns the maximum value which the receiver will allow.
+ *
+ * @return the maximum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMaximum () {
+    checkWidget ();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_RANGE;
+    OS.GetScrollInfo (handle, OS.SB_CTL, info);
+    return info.nMax;
+}
+
+/**
+ * Returns the minimum value which the receiver will allow.
+ *
+ * @return the minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMinimum () {
+    checkWidget ();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_RANGE;
+    OS.GetScrollInfo (handle, OS.SB_CTL, info);
+    return info.nMin;
+}
+
+/**
+ * Returns the amount that the receiver's value will be
+ * modified by when the page increment/decrement areas
+ * are selected.
+ *
+ * @return the page increment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getPageIncrement () {
+    checkWidget ();
+    return pageIncrement;
+}
+
+/**
+ * Returns the 'selection', which is the receiver's value.
+ *
+ * @return the selection
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelection () {
+    checkWidget ();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_POS;
+    OS.GetScrollInfo (handle, OS.SB_CTL, info);
+    return info.nPos;
+}
+
+/**
+ * Returns the size of the receiver's thumb relative to the
+ * difference between its maximum and minimum values.
+ *
+ * @return the thumb value
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getThumb () {
+    checkWidget ();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_PAGE;
+    OS.GetScrollInfo (handle, OS.SB_CTL, info);
+    if (info.nPage !is 0) --info.nPage;
+    return info.nPage;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's value.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+void setBounds (int x, int y, int width, int height, int flags) {
+    super.setBounds (x, y, width, height, flags);
+    /*
+    * Bug in Windows.  If the scroll bar is resized when it has focus,
+    * the flashing cursor that is used to show that the scroll bar has
+    * focus is not moved.  The fix is to send a fake WM_SETFOCUS to
+    * get the scroll bar to recompute the size of the flashing cursor.
+    */
+    if (OS.GetFocus () is handle) {
+        ignoreFocus = true;
+        OS.SendMessage (handle, OS.WM_SETFOCUS, 0, 0);
+        ignoreFocus = false;
+    }
+}
+
+/**
+ * Sets the amount that the receiver's value will be
+ * modified by when the up/down (or right/left) arrows
+ * are pressed to the argument, which must be at least
+ * one.
+ *
+ * @param value the new increment (must be greater than zero)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setIncrement (int value) {
+    checkWidget ();
+    if (value < 1) return;
+    increment = value;
+}
+
+/**
+ * Sets the maximum. If this value is negative or less than or
+ * equal to the minimum, the value is ignored. If necessary, first
+ * the thumb and then the selection are adjusted to fit within the
+ * new range.
+ *
+ * @param value the new maximum, which must be greater than the current minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMaximum (int value) {
+    checkWidget ();
+    if (value < 0) return;
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL;
+    OS.GetScrollInfo (handle, OS.SB_CTL, info);
+    if (value - info.nMin - info.nPage < 1) return;
+    info.nMax = value;
+    SetScrollInfo (handle, OS.SB_CTL, info, true);
+}
+
+/**
+ * Sets the minimum value. If this value is negative or greater
+ * than or equal to the maximum, the value is ignored. If necessary,
+ * first the thumb and then the selection are adjusted to fit within
+ * the new range.
+ *
+ * @param value the new minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinimum (int value) {
+    checkWidget ();
+    if (value < 0) return;
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL;
+    OS.GetScrollInfo (handle, OS.SB_CTL, info);
+    if (info.nMax - value - info.nPage < 1) return;
+    info.nMin = value;
+    SetScrollInfo (handle, OS.SB_CTL, info, true);
+}
+
+/**
+ * Sets the amount that the receiver's value will be
+ * modified by when the page increment/decrement areas
+ * are selected to the argument, which must be at least
+ * one.
+ *
+ * @param value the page increment (must be greater than zero)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setPageIncrement (int value) {
+    checkWidget ();
+    if (value < 1) return;
+    pageIncrement = value;
+}
+
+bool SetScrollInfo (int hwnd, int flags, SCROLLINFO info, bool fRedraw) {
+    /*
+    * Feature in Windows.  Using SIF_DISABLENOSCROLL,
+    * SetScrollInfo () can change enabled and disabled
+    * state of the scroll bar causing a scroll bar that
+    * was disabled by the application to become enabled.
+    * The fix is to disable the scroll bar (again) when
+    * the application has disabled the scroll bar.
+    */
+    if ((state & DISABLED) !is 0) fRedraw = false;
+    bool result = OS.SetScrollInfo (hwnd, flags, info, fRedraw);
+    if ((state & DISABLED) !is 0) {
+        OS.EnableWindow (handle, false);
+        if (!OS.IsWinCE) {
+            OS.EnableScrollBar (handle, OS.SB_CTL, OS.ESB_DISABLE_BOTH);
+        }
+    }
+
+    /*
+    * Bug in Windows.  If the thumb is resized when it has focus,
+    * the flashing cursor that is used to show that the scroll bar
+    * has focus is not moved.  The fix is to send a fake WM_SETFOCUS
+    * to get the scroll bar to recompute the size of the flashing
+    * cursor.
+    */
+    if (OS.GetFocus () is handle) {
+        ignoreFocus = true;
+        OS.SendMessage (handle, OS.WM_SETFOCUS, 0, 0);
+        ignoreFocus = false;
+    }
+    return result;
+}
+
+/**
+ * Sets the 'selection', which is the receiver's
+ * value, to the argument which must be greater than or equal
+ * to zero.
+ *
+ * @param value the new selection (must be zero or greater)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (int value) {
+    checkWidget ();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_POS;
+    info.nPos = value;
+    SetScrollInfo (handle, OS.SB_CTL, info, true);
+}
+
+/**
+ * Sets the size of the receiver's thumb relative to the
+ * difference between its maximum and minimum values.  This new
+ * value will be ignored if it is less than one, and will be
+ * clamped if it exceeds the receiver's current range.
+ *
+ * @param value the new thumb value, which must be at least one and not
+ * larger than the size of the current range
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setThumb (int value) {
+    checkWidget ();
+    if (value < 1) return;
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_PAGE | OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL;
+    OS.GetScrollInfo (handle, OS.SB_CTL, info);
+    info.nPage = value;
+    if (info.nPage !is 0) info.nPage++;
+    SetScrollInfo (handle, OS.SB_CTL, info, true);
+}
+
+/**
+ * Sets the receiver's selection, minimum value, maximum
+ * value, thumb, increment and page increment all at once.
+ * <p>
+ * Note: This is similar to setting the values individually
+ * using the appropriate methods, but may be implemented in a
+ * more efficient fashion on some platforms.
+ * </p>
+ *
+ * @param selection the new selection value
+ * @param minimum the new minimum value
+ * @param maximum the new maximum value
+ * @param thumb the new thumb value
+ * @param increment the new increment value
+ * @param pageIncrement the new pageIncrement value
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setValues (int selection, int minimum, int maximum, int thumb, int increment, int pageIncrement) {
+    checkWidget ();
+    if (minimum < 0) return;
+    if (maximum < 0) return;
+    if (thumb < 1) return;
+    if (increment < 1) return;
+    if (pageIncrement < 1) return;
+    this.increment = increment;
+    this.pageIncrement = pageIncrement;
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_POS | OS.SIF_PAGE | OS.SIF_RANGE | OS.SIF_DISABLENOSCROLL;
+    info.nPos = selection;
+    info.nMin = minimum;
+    info.nMax = maximum;
+    info.nPage = thumb;
+    if (info.nPage !is 0) info.nPage++;
+    SetScrollInfo (handle, OS.SB_CTL, info, true);
+}
+
+int widgetExtStyle () {
+    /*
+    * Bug in Windows.  If a scroll bar control is given a border,
+    * dragging the scroll bar thumb eats away parts of the border
+    * while the thumb is dragged.  The fix is to clear border for
+    * all scroll bars.
+    */
+    int bits = super.widgetExtStyle ();
+    if ((style & DWT.BORDER) !is 0) bits &= ~OS.WS_EX_CLIENTEDGE;
+    return bits;
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.WS_TABSTOP;
+    /*
+    * Bug in Windows.  If a scroll bar control is given a border,
+    * dragging the scroll bar thumb eats away parts of the border
+    * while the thumb is dragged.  The fix is to clear WS_BORDER.
+    */
+    if ((style & DWT.BORDER) !is 0) bits &= ~OS.WS_BORDER;
+    if ((style & DWT.HORIZONTAL) !is 0) return bits | OS.SBS_HORZ;
+    return bits | OS.SBS_VERT;
+}
+
+TCHAR windowClass () {
+    return ScrollBarClass;
+}
+
+int windowProc () {
+    return ScrollBarProc;
+}
+
+LRESULT WM_KEYDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_KEYDOWN (wParam, lParam);
+    if (result !is null) return result;
+    if ((style & DWT.VERTICAL) !is 0) return result;
+    /*
+    * Bug in Windows.  When a horizontal scroll bar is mirrored,
+    * the native control does not correctly swap the arrow keys.
+    * The fix is to swap them before calling the scroll bar window
+    * proc.
+    *
+    * NOTE: This fix is not ideal.  It breaks when the bug is fixed
+    * in the operating system.
+    */
+    if ((style & DWT.MIRRORED) !is 0) {
+        switch (wParam) {
+            case OS.VK_LEFT:
+            case OS.VK_RIGHT: {
+                int key = wParam is OS.VK_LEFT ? OS.VK_RIGHT : OS.VK_LEFT;
+                int code = callWindowProc (handle, OS.WM_KEYDOWN, key, lParam);
+                return new LRESULT (code);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_LBUTTONDBLCLK (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  Windows uses the WS_TABSTOP
+    * style for the scroll bar to decide that focus
+    * should be set during WM_LBUTTONDBLCLK.  This is
+    * not the desired behavior.  The fix is to clear
+    * and restore WS_TABSTOP so that Windows will not
+    * assign focus.
+    */
+    int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    int newBits = oldBits & ~OS.WS_TABSTOP;
+    OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+    LRESULT result = super.WM_LBUTTONDBLCLK (wParam, lParam);
+    if (isDisposed ()) return LRESULT.ZERO;
+    OS.SetWindowLong (handle, OS.GWL_STYLE, oldBits);
+    if (result is LRESULT.ZERO) return result;
+
+    /*
+    * Feature in Windows.  Windows runs a modal message loop
+    * when the user drags a scroll bar that terminates when
+    * it sees an WM_LBUTTONUP.  Unfortunately the WM_LBUTTONUP
+    * is consumed.  The fix is to send a fake mouse up and
+    * release the automatic capture.
+    */
+    if (!OS.IsWinCE) {
+        if (OS.GetCapture () is handle) OS.ReleaseCapture ();
+        if (!sendMouseEvent (DWT.MouseUp, 1, handle, OS.WM_LBUTTONUP, wParam, lParam)) {
+            return LRESULT.ZERO;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  Windows uses the WS_TABSTOP
+    * style for the scroll bar to decide that focus
+    * should be set during WM_LBUTTONDOWN.  This is
+    * not the desired behavior.  The fix is to clear
+    * and restore WS_TABSTOP so that Windows will not
+    * assign focus.
+    */
+    int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    int newBits = oldBits & ~OS.WS_TABSTOP;
+    OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+    LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
+    if (isDisposed ()) return LRESULT.ZERO;
+    OS.SetWindowLong (handle, OS.GWL_STYLE, oldBits);
+    if (result is LRESULT.ZERO) return result;
+
+    /*
+    * Feature in Windows.  Windows runs a modal message loop
+    * when the user drags a scroll bar that terminates when
+    * it sees an WM_LBUTTONUP.  Unfortunately the WM_LBUTTONUP
+    * is consumed.  The fix is to send a fake mouse up and
+    * release the automatic capture.
+    */
+    if (!OS.IsWinCE) {
+        if (OS.GetCapture () is handle) OS.ReleaseCapture ();
+        if (!sendMouseEvent (DWT.MouseUp, 1, handle, OS.WM_LBUTTONUP, wParam, lParam)) {
+            return LRESULT.ONE;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    if (ignoreFocus) return null;
+    return super.WM_SETFOCUS (wParam, lParam);
+}
+
+LRESULT wmScrollChild (int wParam, int lParam) {
+
+    /* Do nothing when scrolling is ending */
+    int code = wParam & 0xFFFF;
+    if (code is OS.SB_ENDSCROLL) return null;
+
+    /* Move the thumb */
+    Event event = new Event ();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_TRACKPOS | OS.SIF_POS | OS.SIF_RANGE;
+    OS.GetScrollInfo (handle, OS.SB_CTL, info);
+    info.fMask = OS.SIF_POS;
+    switch (code) {
+        case OS.SB_THUMBPOSITION:
+            event.detail = DWT.NONE;
+            info.nPos = info.nTrackPos;
+            break;
+        case OS.SB_THUMBTRACK:
+            event.detail = DWT.DRAG;
+            info.nPos = info.nTrackPos;
+            break;
+        case OS.SB_TOP:
+            event.detail = DWT.HOME;
+            info.nPos = info.nMin;
+            break;
+        case OS.SB_BOTTOM:
+            event.detail = DWT.END;
+            info.nPos = info.nMax;
+            break;
+        case OS.SB_LINEDOWN:
+            event.detail = DWT.ARROW_DOWN;
+            info.nPos += increment;
+            break;
+        case OS.SB_LINEUP:
+            event.detail = DWT.ARROW_UP;
+            info.nPos = Math.max (info.nMin, info.nPos - increment);
+            break;
+        case OS.SB_PAGEDOWN:
+            event.detail = DWT.PAGE_DOWN;
+            info.nPos += pageIncrement;
+            break;
+        case OS.SB_PAGEUP:
+            event.detail = DWT.PAGE_UP;
+            info.nPos = Math.max (info.nMin, info.nPos - pageIncrement);
+            break;
+    }
+    OS.SetScrollInfo (handle, OS.SB_CTL, info, true);
+
+    /*
+    * Feature in Windows.  Windows runs a modal message
+    * loop when the user drags a scroll bar.  This means
+    * that selection event must be sent because WM_HSCROLL
+    * and WM_VSCROLL are sent from the modal message loop
+    * so that they are delivered during inside the loop.
+    */
+    sendEvent (DWT.Selection, event);
+    // the widget could be destroyed at this point
+    return null;
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Spinner.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1366 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Spinner;
+
+import dwt.widgets.Composite;
+class Spinner : Composite {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.ModifyListener;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.events.VerifyListener;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.NMUPDOWN;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+import dwt.internal.win32.UDACCEL;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class are selectable user interface
+ * objects that allow the user to enter and modify numeric
+ * values.
+ * <p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to add children to it, or set a layout on it.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>READ_ONLY, WRAP</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection, Modify, Verify</dd>
+ * </dl>
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class Spinner extends Composite {
+    int hwndText, hwndUpDown;
+    bool ignoreModify;
+    int pageIncrement, digits;
+    static final int EditProc;
+    static final TCHAR EditClass = new TCHAR (0, "EDIT", true);
+    static final int UpDownProc;
+    static final TCHAR UpDownClass = new TCHAR (0, OS.UPDOWN_CLASS, true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, EditClass, lpWndClass);
+        EditProc = lpWndClass.lpfnWndProc;
+        OS.GetClassInfo (0, UpDownClass, lpWndClass);
+        UpDownProc = lpWndClass.lpfnWndProc;
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#READ_ONLY
+ * @see DWT#WRAP
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Spinner (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    if (hwnd is hwndText) {
+        return OS.CallWindowProc (EditProc, hwnd, msg, wParam, lParam);
+    }
+    if (hwnd is hwndUpDown) {
+        return OS.CallWindowProc (UpDownProc, hwnd, msg, wParam, lParam);
+    }
+    return OS.DefWindowProc (handle, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    /*
+    * Even though it is legal to create this widget
+    * with scroll bars, they serve no useful purpose
+    * because they do not automatically scroll the
+    * widget's client area.  The fix is to clear
+    * the DWT style.
+    */
+    return style & ~(DWT.H_SCROLL | DWT.V_SCROLL);
+}
+
+bool checkHandle (int hwnd) {
+    return hwnd is handle || hwnd is hwndText || hwnd is hwndUpDown;
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state &= ~(CANVAS | THEME_BACKGROUND);
+    int hInstance = OS.GetModuleHandle (null);
+    int textExStyle = (style & DWT.BORDER) !is 0 ? OS.WS_EX_CLIENTEDGE : 0;
+    int textStyle = OS.WS_CHILD | OS.WS_VISIBLE | OS.ES_AUTOHSCROLL | OS.WS_CLIPSIBLINGS;
+    if ((style & DWT.READ_ONLY) !is 0) textStyle |= OS.ES_READONLY;
+    if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+        if ((style & DWT.RIGHT_TO_LEFT) !is 0) textExStyle |= OS.WS_EX_LAYOUTRTL;
+    }
+    hwndText = OS.CreateWindowEx (
+        textExStyle,
+        EditClass,
+        null,
+        textStyle,
+        0, 0, 0, 0,
+        handle,
+        0,
+        hInstance,
+        null);
+    if (hwndText is 0) error (DWT.ERROR_NO_HANDLES);
+    OS.SetWindowLong (hwndText, OS.GWL_ID, hwndText);
+    int upDownStyle = OS.WS_CHILD | OS.WS_VISIBLE | OS.UDS_AUTOBUDDY;
+    if ((style & DWT.WRAP) !is 0) upDownStyle |= OS.UDS_WRAP;
+    if ((style & DWT.BORDER) !is 0) {
+        if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+            upDownStyle |= OS.UDS_ALIGNLEFT;
+        } else {
+            upDownStyle |= OS.UDS_ALIGNRIGHT;
+        }
+    }
+    hwndUpDown = OS.CreateWindowEx (
+        0,
+        UpDownClass,
+        null,
+        upDownStyle,
+        0, 0, 0, 0,
+        handle,
+        0,
+        hInstance,
+        null);
+    if (hwndUpDown is 0) error (DWT.ERROR_NO_HANDLES);
+    int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
+    SetWindowPos (hwndText, hwndUpDown, 0, 0, 0, 0, flags);
+    OS.SetWindowLong (hwndUpDown, OS.GWL_ID, hwndUpDown);
+    if (OS.IsDBLocale) {
+        int hIMC = OS.ImmGetContext (handle);
+        OS.ImmAssociateContext (hwndText, hIMC);
+        OS.ImmAssociateContext (hwndUpDown, hIMC);
+        OS.ImmReleaseContext (handle, hIMC);
+    }
+    OS.SendMessage (hwndUpDown, OS.UDM_SETRANGE32, 0, 100);
+    OS.SendMessage (hwndUpDown, OS.IsWinCE ? OS.UDM_SETPOS : OS.UDM_SETPOS32, 0, 0);
+    pageIncrement = 10;
+    digits = 0;
+    TCHAR buffer = new TCHAR (getCodePage (), "0", true);
+    OS.SetWindowText (hwndText, buffer);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver's text is modified, by sending
+ * it one of the messages defined in the <code>ModifyListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ModifyListener
+ * @see #removeModifyListener
+ */
+public void addModifyListener (ModifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Modify, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is not called for texts.
+ * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed in a single-line text.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the control is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver's text is verified, by sending
+ * it one of the messages defined in the <code>VerifyListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see VerifyListener
+ * @see #removeVerifyListener
+ */
+void addVerifyListener (VerifyListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Verify, typedListener);
+}
+
+int borderHandle () {
+    return hwndText;
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = 0, height = 0;
+    if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) {
+        int newFont, oldFont = 0;
+        int hDC = OS.GetDC (hwndText);
+        newFont = OS.SendMessage (hwndText, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+        TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+        OS.GetTextMetrics (hDC, tm);
+        height = tm.tmHeight;
+        RECT rect = new RECT ();
+        int [] max = new int [1];
+        OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, null, max);
+        String string = String.valueOf (max [0]);
+        if (digits > 0) {
+            StringBuffer buffer = new StringBuffer ();
+            buffer.append (string);
+            buffer.append (getDecimalSeparator ());
+            int count = digits - string.length ();
+            while (count >= 0) {
+                buffer.append ("0");
+                count--;
+            }
+            string = buffer.toString ();
+        }
+        TCHAR buffer = new TCHAR (getCodePage (), string, false);
+        int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_NOPREFIX;
+        OS.DrawText (hDC, buffer, buffer.length (), rect, flags);
+        width = rect.right - rect.left;
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (hwndText, hDC);
+    }
+    if (width is 0) width = DEFAULT_WIDTH;
+    if (height is 0) height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    Rectangle trim = computeTrim (0, 0, width, height);
+    if (hHint is DWT.DEFAULT) {
+        int upDownHeight = OS.GetSystemMetrics (OS.SM_CYVSCROLL) + 2 * getBorderWidth ();
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+            upDownHeight += (style & DWT.BORDER) !is 0 ? 1 : 3;
+        }
+        trim.height = Math.max (trim.height, upDownHeight);
+    }
+    return new Point (trim.width, trim.height);
+}
+
+public Rectangle computeTrim (int x, int y, int width, int height) {
+    checkWidget ();
+
+    /* Get the trim of the text control */
+    RECT rect = new RECT ();
+    OS.SetRect (rect, x, y, x + width, y + height);
+    int bits0 = OS.GetWindowLong (hwndText, OS.GWL_STYLE);
+    int bits1 = OS.GetWindowLong (hwndText, OS.GWL_EXSTYLE);
+    OS.AdjustWindowRectEx (rect, bits0, false, bits1);
+    width = rect.right - rect.left;
+    height = rect.bottom - rect.top;
+
+    /*
+    * The preferred height of a single-line text widget
+    * has been hand-crafted to be the same height as
+    * the single-line text widget in an editable combo
+    * box.
+    */
+    int margins = OS.SendMessage (hwndText, OS.EM_GETMARGINS, 0, 0);
+    x -= margins & 0xFFFF;
+    width += (margins & 0xFFFF) + ((margins >> 16) & 0xFFFF);
+    if ((style & DWT.BORDER) !is 0) {
+        x -= 1;
+        y -= 1;
+        width += 2;
+        height += 2;
+    }
+    width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+    return new Rectangle (x, y, width, height);
+}
+
+/**
+ * Copies the selected text.
+ * <p>
+ * The current selection is copied to the clipboard.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void copy () {
+    checkWidget ();
+    OS.SendMessage (hwndText, OS.WM_COPY, 0, 0);
+}
+
+/**
+ * Cuts the selected text.
+ * <p>
+ * The current selection is first copied to the
+ * clipboard and then deleted from the widget.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void cut () {
+    checkWidget ();
+    if ((style & DWT.READ_ONLY) !is 0) return;
+    OS.SendMessage (hwndText, OS.WM_CUT, 0, 0);
+}
+
+int defaultBackground () {
+    return OS.GetSysColor (OS.COLOR_WINDOW);
+}
+
+void enableWidget (bool enabled) {
+    super.enableWidget (enabled);
+    OS.EnableWindow (hwndText, enabled);
+    OS.EnableWindow (hwndUpDown, enabled);
+}
+
+void deregister () {
+    super.deregister ();
+    display.removeControl (hwndText);
+    display.removeControl (hwndUpDown);
+}
+
+bool hasFocus () {
+    int hwndFocus = OS.GetFocus ();
+    if (hwndFocus is handle) return true;
+    if (hwndFocus is hwndText) return true;
+    if (hwndFocus is hwndUpDown) return true;
+    return false;
+}
+
+/**
+ * Returns the number of decimal places used by the receiver.
+ *
+ * @return the digits
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getDigits () {
+    checkWidget ();
+    return digits;
+}
+
+String getDecimalSeparator () {
+    TCHAR tchar = new TCHAR (getCodePage (), 4);
+    int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_SDECIMAL, tchar, 4);
+    return size !is 0 ? tchar.toString (0, size - 1) : ".";
+}
+
+/**
+ * Returns the amount that the receiver's value will be
+ * modified by when the up/down arrows are pressed.
+ *
+ * @return the increment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getIncrement () {
+    checkWidget ();
+    UDACCEL udaccel = new UDACCEL ();
+    OS.SendMessage (hwndUpDown, OS.UDM_GETACCEL, 1, udaccel);
+    return udaccel.nInc;
+}
+
+/**
+ * Returns the maximum value which the receiver will allow.
+ *
+ * @return the maximum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMaximum () {
+    checkWidget ();
+    int [] max = new int [1];
+    OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, null, max);
+    return max [0];
+}
+
+/**
+ * Returns the minimum value which the receiver will allow.
+ *
+ * @return the minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getMinimum () {
+    checkWidget ();
+    int [] min = new int [1];
+    OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, null);
+    return min [0];
+}
+
+/**
+ * Returns the amount that the receiver's position will be
+ * modified by when the page up/down keys are pressed.
+ *
+ * @return the page increment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getPageIncrement () {
+    checkWidget ();
+    return pageIncrement;
+}
+
+/**
+ * Returns the <em>selection</em>, which is the receiver's position.
+ *
+ * @return the selection
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelection () {
+    checkWidget ();
+    if (OS.IsWinCE) {
+        return OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0) & 0xFFFF;
+    } else {
+        return OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0);
+    }
+}
+
+int getSelectionText () {
+    int length = OS.GetWindowTextLength (hwndText);
+    TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+    OS.GetWindowText (hwndText, buffer, length + 1);
+    String string = buffer.toString (0, length);
+    try {
+        int value;
+        if (digits > 0) {
+            String decimalSeparator = getDecimalSeparator ();
+            int index = string.indexOf (decimalSeparator);
+            if (index !is -1)  {
+                String wholePart = string.substring (0, index);
+                String decimalPart = string.substring (index + 1);
+                if (decimalPart.length () > digits) {
+                    decimalPart = decimalPart.substring (0, digits);
+                } else {
+                    int i = digits - decimalPart.length ();
+                    for (int j = 0; j < i; j++) {
+                        decimalPart = decimalPart + "0";
+                    }
+                }
+                int wholeValue = Integer.parseInt (wholePart);
+                int decimalValue = Integer.parseInt (decimalPart);
+                for (int i = 0; i < digits; i++) wholeValue *= 10;
+                value = wholeValue + decimalValue;
+            } else {
+                value = Integer.parseInt (string);
+            }
+        } else {
+            value = Integer.parseInt (string);
+        }
+        int [] max = new int [1], min = new int [1];
+        OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, max);
+        if (min [0] <= value && value <= max [0]) return value;
+    } catch (NumberFormatException e) {
+    }
+    return -1;
+}
+
+int mbcsToWcsPos (int mbcsPos) {
+    if (mbcsPos <= 0) return 0;
+    if (OS.IsUnicode) return mbcsPos;
+    int mbcsSize = OS.GetWindowTextLengthA (hwndText);
+    if (mbcsSize is 0) return 0;
+    if (mbcsPos >= mbcsSize) return mbcsSize;
+    byte [] buffer = new byte [mbcsSize + 1];
+    OS.GetWindowTextA (hwndText, buffer, mbcsSize + 1);
+    return OS.MultiByteToWideChar (getCodePage (), OS.MB_PRECOMPOSED, buffer, mbcsPos, null, 0);
+}
+
+/**
+ * Pastes text from clipboard.
+ * <p>
+ * The selected text is deleted from the widget
+ * and new text inserted from the clipboard.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void paste () {
+    checkWidget ();
+    if ((style & DWT.READ_ONLY) !is 0) return;
+    OS.SendMessage (hwndText, OS.WM_PASTE, 0, 0);
+}
+
+void register () {
+    super.register ();
+    display.addControl (hwndText, this);
+    display.addControl (hwndUpDown, this);
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    hwndText = hwndUpDown = 0;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the receiver's text is modified.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ModifyListener
+ * @see #addModifyListener
+ */
+public void removeModifyListener (ModifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Modify, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is verified.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see VerifyListener
+ * @see #addVerifyListener
+ */
+void removeVerifyListener (VerifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Verify, listener);
+}
+
+bool sendKeyEvent (int type, int msg, int wParam, int lParam, Event event) {
+    if (!super.sendKeyEvent (type, msg, wParam, lParam, event)) {
+        return false;
+    }
+    if ((style & DWT.READ_ONLY) !is 0) return true;
+    if (type !is DWT.KeyDown) return true;
+    if (msg !is OS.WM_CHAR && msg !is OS.WM_KEYDOWN && msg !is OS.WM_IME_CHAR) {
+        return true;
+    }
+    if (event.character is 0) return true;
+//  if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return true;
+    char key = event.character;
+    int stateMask = event.stateMask;
+
+    /*
+    * Disable all magic keys that could modify the text
+    * and don't send events when Alt, Shift or Ctrl is
+    * pressed.
+    */
+    switch (msg) {
+        case OS.WM_CHAR:
+            if (key !is 0x08 && key !is 0x7F && key !is '\r' && key !is '\t' && key !is '\n') break;
+            // FALL THROUGH
+        case OS.WM_KEYDOWN:
+            if ((stateMask & (DWT.ALT | DWT.SHIFT | DWT.CONTROL)) !is 0) return false;
+            break;
+    }
+
+    /*
+    * If the left button is down, the text widget refuses the character.
+    */
+    if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
+        return true;
+    }
+
+    /* Verify the character */
+    String oldText = "";
+    int [] start = new int [1], end = new int [1];
+    OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
+    switch (key) {
+        case 0x08:  /* Bs */
+            if (start [0] is end [0]) {
+                if (start [0] is 0) return true;
+                start [0] = start [0] - 1;
+                if (!OS.IsUnicode && OS.IsDBLocale) {
+                    int [] newStart = new int [1], newEnd = new int [1];
+                    OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
+                    OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd);
+                    if (start [0] !is newStart [0]) start [0] = start [0] - 1;
+                }
+                start [0] = Math.max (start [0], 0);
+            }
+            break;
+        case 0x7F:  /* Del */
+            if (start [0] is end [0]) {
+                int length = OS.GetWindowTextLength (hwndText);
+                if (start [0] is length) return true;
+                end [0] = end [0] + 1;
+                if (!OS.IsUnicode && OS.IsDBLocale) {
+                    int [] newStart = new int [1], newEnd = new int [1];
+                    OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
+                    OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd);
+                    if (end [0] !is newEnd [0]) end [0] = end [0] + 1;
+                }
+                end [0] = Math.min (end [0], length);
+            }
+            break;
+        case '\r':  /* Return */
+            return true;
+        default:    /* Tab and other characters */
+            if (key !is '\t' && key < 0x20) return true;
+            oldText = new String (new char [] {key});
+            break;
+    }
+    String newText = verifyText (oldText, start [0], end [0], event);
+    if (newText is null) return false;
+    if (newText is oldText) return true;
+    TCHAR buffer = new TCHAR (getCodePage (), newText, true);
+    OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
+    OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer);
+    return false;
+}
+
+void setBackgroundImage (int hBitmap) {
+    super.setBackgroundImage (hBitmap);
+    OS.InvalidateRect (hwndText, null, true);
+}
+
+void setBackgroundPixel (int pixel) {
+    super.setBackgroundPixel (pixel);
+    OS.InvalidateRect (hwndText, null, true);
+}
+
+/**
+ * Sets the number of decimal places used by the receiver.
+ * <p>
+ * The digit setting is used to allow for floating point values in the receiver.
+ * For example, to set the selection to a floating point value of 1.37 call setDigits() with
+ * a value of 2 and setSelection() with a value of 137. Similarly, if getDigits() has a value
+ * of 2 and getSelection() returns 137 this should be interpreted as 1.37. This applies to all
+ * numeric APIs.
+ * </p>
+ *
+ * @param value the new digits (must be greater than or equal to zero)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the value is less than zero</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setDigits (int value) {
+    checkWidget ();
+    if (value < 0) error (DWT.ERROR_INVALID_ARGUMENT);
+    if (value is this.digits) return;
+    this.digits = value;
+    int pos;
+    if (OS.IsWinCE) {
+        pos = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0) & 0xFFFF;
+    } else {
+        pos = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0);
+    }
+    setSelection (pos, false, true, false);
+}
+
+void setForegroundPixel (int pixel) {
+    super.setForegroundPixel (pixel);
+    OS.InvalidateRect (hwndText, null, true);
+}
+
+/**
+ * Sets the amount that the receiver's value will be
+ * modified by when the up/down arrows are pressed to
+ * the argument, which must be at least one.
+ *
+ * @param value the new increment (must be greater than zero)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setIncrement (int value) {
+    checkWidget ();
+    if (value < 1) return;
+    int hHeap = OS.GetProcessHeap ();
+    int count = OS.SendMessage (hwndUpDown, OS.UDM_GETACCEL, 0, (UDACCEL)null);
+    int udaccels = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, UDACCEL.sizeof * count);
+    OS.SendMessage (hwndUpDown, OS.UDM_GETACCEL, count, udaccels);
+    int first = -1;
+    UDACCEL udaccel = new UDACCEL ();
+    for (int i = 0; i < count; i++) {
+        int offset = udaccels + (i * UDACCEL.sizeof);
+        OS.MoveMemory (udaccel, offset, UDACCEL.sizeof);
+        if (first is -1) first = udaccel.nInc;
+        udaccel.nInc  =  udaccel.nInc * value / first;
+        OS.MoveMemory (offset, udaccel, UDACCEL.sizeof);
+    }
+    OS.SendMessage (hwndUpDown, OS.UDM_SETACCEL, count, udaccels);
+    OS.HeapFree (hHeap, 0, udaccels);
+}
+
+/**
+ * Sets the maximum value that the receiver will allow.  This new
+ * value will be ignored if it is not greater than the receiver's current
+ * minimum value.  If the new maximum is applied then the receiver's
+ * selection value will be adjusted if necessary to fall within its new range.
+ *
+ * @param value the new maximum, which must be greater than the current minimum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMaximum (int value) {
+    checkWidget ();
+    if (value < 0) return;
+    int [] min = new int [1];
+    OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, null);
+    if (value <= min [0]) return;
+    int pos;
+    if (OS.IsWinCE) {
+        pos = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0) & 0xFFFF;
+    } else {
+        pos = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0);
+    }
+    OS.SendMessage (hwndUpDown , OS.UDM_SETRANGE32, min [0], value);
+    if (pos > value) setSelection (value, true, true, false);
+}
+
+/**
+ * Sets the minimum value that the receiver will allow.  This new
+ * value will be ignored if it is negative or is not less than the receiver's
+ * current maximum value.  If the new minimum is applied then the receiver's
+ * selection value will be adjusted if necessary to fall within its new range.
+ *
+ * @param value the new minimum, which must be nonnegative and less than the current maximum
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMinimum (int value) {
+    checkWidget ();
+    if (value < 0) return;
+    int [] max = new int [1];
+    OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, null, max);
+    if (value >= max [0]) return;
+    int pos;
+    if (OS.IsWinCE) {
+        pos = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0) & 0xFFFF;
+    } else {
+        pos = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0);
+    }
+    OS.SendMessage (hwndUpDown , OS.UDM_SETRANGE32, value, max [0]);
+    if (pos < value) setSelection (value, true, true, false);
+}
+
+/**
+ * Sets the amount that the receiver's position will be
+ * modified by when the page up/down keys are pressed
+ * to the argument, which must be at least one.
+ *
+ * @param value the page increment (must be greater than zero)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setPageIncrement (int value) {
+    checkWidget ();
+    if (value < 1) return;
+    pageIncrement = value;
+}
+
+/**
+ * Sets the <em>selection</em>, which is the receiver's
+ * position, to the argument. If the argument is not within
+ * the range specified by minimum and maximum, it will be
+ * adjusted to fall within this range.
+ *
+ * @param value the new selection (must be zero or greater)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (int value) {
+    checkWidget ();
+    int [] max = new int [1], min = new int [1];
+    OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, max);
+    value = Math.min (Math.max (min [0], value), max [0]);
+    setSelection (value, true, true, false);
+}
+
+void setSelection (int value, bool setPos, bool setText, bool notify) {
+    if (setPos) {
+        OS.SendMessage (hwndUpDown , OS.IsWinCE ? OS.UDM_SETPOS : OS.UDM_SETPOS32, 0, value);
+    }
+    if (setText) {
+        String string = String.valueOf (value);
+        if (digits > 0) {
+            String decimalSeparator = getDecimalSeparator ();
+            int index = string.length () - digits;
+            StringBuffer buffer = new StringBuffer ();
+            if (index > 0) {
+                buffer.append (string.substring (0, index));
+                buffer.append (decimalSeparator);
+                buffer.append (string.substring (index));
+            } else {
+                buffer.append ("0");
+                buffer.append (decimalSeparator);
+                while (index++ < 0) buffer.append ("0");
+                buffer.append (string);
+            }
+            string = buffer.toString ();
+        }
+        if (hooks (DWT.Verify) || filters (DWT.Verify)) {
+            int length = OS.GetWindowTextLength (hwndText);
+            string = verifyText (string, 0, length, null);
+            if (string is null) return;
+        }
+        TCHAR buffer = new TCHAR (getCodePage (), string, true);
+        OS.SetWindowText (hwndText, buffer);
+    }
+    if (notify) postEvent (DWT.Selection);
+}
+
+void setToolTipText (Shell shell, String string) {
+    shell.setToolTipText (hwndText, string);
+    shell.setToolTipText (hwndUpDown, string);
+}
+
+/**
+ * Sets the receiver's selection, minimum value, maximum
+ * value, digits, increment and page increment all at once.
+ * <p>
+ * Note: This is similar to setting the values individually
+ * using the appropriate methods, but may be implemented in a
+ * more efficient fashion on some platforms.
+ * </p>
+ *
+ * @param selection the new selection value
+ * @param minimum the new minimum value
+ * @param maximum the new maximum value
+ * @param digits the new digits value
+ * @param increment the new increment value
+ * @param pageIncrement the new pageIncrement value
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setValues (int selection, int minimum, int maximum, int digits, int increment, int pageIncrement) {
+    checkWidget ();
+    if (minimum < 0) return;
+    if (maximum <= minimum) return;
+    if (digits < 0) return;
+    if (increment < 1) return;
+    if (pageIncrement < 1) return;
+    selection = Math.min (Math.max (minimum, selection), maximum);
+    setIncrement (increment);
+    this.pageIncrement = pageIncrement;
+    this.digits = digits;
+    OS.SendMessage (hwndUpDown , OS.UDM_SETRANGE32, minimum, maximum);
+    setSelection (selection, true, true, false);
+}
+
+void subclass () {
+    super.subclass ();
+    int newProc = display.windowProc;
+    OS.SetWindowLong (hwndText, OS.GWL_WNDPROC, newProc);
+    OS.SetWindowLong (hwndUpDown, OS.GWL_WNDPROC, newProc);
+}
+
+void unsubclass () {
+    super.unsubclass ();
+    OS.SetWindowLong (hwndText, OS.GWL_WNDPROC, EditProc);
+    OS.SetWindowLong (hwndUpDown, OS.GWL_WNDPROC, UpDownProc);
+}
+
+String verifyText (String string, int start, int end, Event keyEvent) {
+    Event event = new Event ();
+    event.text = string;
+    event.start = start;
+    event.end = end;
+    if (keyEvent !is null) {
+        event.character = keyEvent.character;
+        event.keyCode = keyEvent.keyCode;
+        event.stateMask = keyEvent.stateMask;
+    }
+    int index = 0;
+    if (digits > 0) {
+        String decimalSeparator = getDecimalSeparator ();
+        index = string.indexOf (decimalSeparator);
+        if (index !is -1) {
+            string = string.substring (0, index) + string.substring (index + 1);
+        }
+        index = 0;
+    }
+    while (index < string.length ()) {
+        if (!Character.isDigit (string.charAt (index))) break;
+        index++;
+    }
+    event.doit = index is string.length ();
+    if (!OS.IsUnicode && OS.IsDBLocale) {
+        event.start = mbcsToWcsPos (start);
+        event.end = mbcsToWcsPos (end);
+    }
+    sendEvent (DWT.Verify, event);
+    if (!event.doit || isDisposed ()) return null;
+    return event.text;
+}
+
+int widgetExtStyle () {
+    return super.widgetExtStyle () & ~OS.WS_EX_CLIENTEDGE;
+}
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (hwnd is hwndText || hwnd is hwndUpDown) {
+        LRESULT result = null;
+        switch (msg) {
+            /* Keyboard messages */
+            case OS.WM_CHAR:        result = wmChar (hwnd, wParam, lParam); break;
+            case OS.WM_IME_CHAR:    result = wmIMEChar (hwnd, wParam, lParam); break;
+            case OS.WM_KEYDOWN:     result = wmKeyDown (hwnd, wParam, lParam); break;
+            case OS.WM_KEYUP:       result = wmKeyUp (hwnd, wParam, lParam); break;
+            case OS.WM_SYSCHAR:     result = wmSysChar (hwnd, wParam, lParam); break;
+            case OS.WM_SYSKEYDOWN:  result = wmSysKeyDown (hwnd, wParam, lParam); break;
+            case OS.WM_SYSKEYUP:    result = wmSysKeyUp (hwnd, wParam, lParam); break;
+
+            /* Mouse Messages */
+            case OS.WM_CAPTURECHANGED:  result = wmCaptureChanged (hwnd, wParam, lParam); break;
+            case OS.WM_LBUTTONDBLCLK:   result = wmLButtonDblClk (hwnd, wParam, lParam); break;
+            case OS.WM_LBUTTONDOWN:     result = wmLButtonDown (hwnd, wParam, lParam); break;
+            case OS.WM_LBUTTONUP:       result = wmLButtonUp (hwnd, wParam, lParam); break;
+            case OS.WM_MBUTTONDBLCLK:   result = wmMButtonDblClk (hwnd, wParam, lParam); break;
+            case OS.WM_MBUTTONDOWN:     result = wmMButtonDown (hwnd, wParam, lParam); break;
+            case OS.WM_MBUTTONUP:       result = wmMButtonUp (hwnd, wParam, lParam); break;
+            case OS.WM_MOUSEHOVER:      result = wmMouseHover (hwnd, wParam, lParam); break;
+            case OS.WM_MOUSELEAVE:      result = wmMouseLeave (hwnd, wParam, lParam); break;
+            case OS.WM_MOUSEMOVE:       result = wmMouseMove (hwnd, wParam, lParam); break;
+//          case OS.WM_MOUSEWHEEL:      result = wmMouseWheel (hwnd, wParam, lParam); break;
+            case OS.WM_RBUTTONDBLCLK:   result = wmRButtonDblClk (hwnd, wParam, lParam); break;
+            case OS.WM_RBUTTONDOWN:     result = wmRButtonDown (hwnd, wParam, lParam); break;
+            case OS.WM_RBUTTONUP:       result = wmRButtonUp (hwnd, wParam, lParam); break;
+            case OS.WM_XBUTTONDBLCLK:   result = wmXButtonDblClk (hwnd, wParam, lParam); break;
+            case OS.WM_XBUTTONDOWN:     result = wmXButtonDown (hwnd, wParam, lParam); break;
+            case OS.WM_XBUTTONUP:       result = wmXButtonUp (hwnd, wParam, lParam); break;
+
+            /* Focus Messages */
+            case OS.WM_SETFOCUS:        result = wmSetFocus (hwnd, wParam, lParam); break;
+            case OS.WM_KILLFOCUS:       result = wmKillFocus (hwnd, wParam, lParam); break;
+
+            /* Paint messages */
+            case OS.WM_PAINT:           result = wmPaint (hwnd, wParam, lParam); break;
+            case OS.WM_PRINT:           result = wmPrint (hwnd, wParam, lParam); break;
+
+            /* Menu messages */
+            case OS.WM_CONTEXTMENU:     result = wmContextMenu (hwnd, wParam, lParam); break;
+
+            /* Clipboard messages */
+            case OS.WM_CLEAR:
+            case OS.WM_CUT:
+            case OS.WM_PASTE:
+            case OS.WM_UNDO:
+            case OS.EM_UNDO:
+                if (hwnd is hwndText) {
+                    result = wmClipboard (hwnd, msg, wParam, lParam);
+                }
+                break;
+        }
+        if (result !is null) return result.value;
+        return callWindowProc (hwnd, msg, wParam, lParam);
+    }
+    return super.windowProc (hwnd, msg, wParam, lParam);
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    super.WM_ERASEBKGND (wParam, lParam);
+    drawBackground (wParam);
+    return LRESULT.ONE;
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    return null;
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    OS.SetFocus (hwndText);
+    return null;
+}
+
+LRESULT WM_SETFONT (int wParam, int lParam) {
+    LRESULT result = super.WM_SETFONT (wParam, lParam);
+    if (result !is null) return result;
+    OS.SendMessage (hwndText, OS.WM_SETFONT, wParam, lParam);
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    if (isDisposed ()) return result;
+    int width = lParam & 0xFFFF, height = lParam >> 16;
+    int upDownWidth = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+    int textWidth = width - upDownWidth;
+    int border = OS.GetSystemMetrics (OS.SM_CXEDGE);
+    int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE;
+    SetWindowPos (hwndText, 0, 0, 0, textWidth + border, height, flags);
+    SetWindowPos (hwndUpDown, 0, textWidth, 0, upDownWidth, height, flags);
+    return result;
+}
+
+LRESULT wmChar (int hwnd, int wParam, int lParam) {
+    LRESULT result = super.wmChar (hwnd, wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  For some reason, when the
+    * widget is a single line text widget, when the
+    * user presses tab, return or escape, Windows beeps.
+    * The fix is to look for these keys and not call
+    * the window proc.
+    */
+    switch (wParam) {
+        case DWT.CR:
+            postEvent (DWT.DefaultSelection);
+            // FALL THROUGH
+        case DWT.TAB:
+        case DWT.ESC: return LRESULT.ZERO;
+    }
+    return result;
+}
+
+LRESULT wmClipboard (int hwndText, int msg, int wParam, int lParam) {
+    if ((style & DWT.READ_ONLY) !is 0) return null;
+//  if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return null;
+    bool call = false;
+    int [] start = new int [1], end = new int [1];
+    String newText = null;
+    switch (msg) {
+        case OS.WM_CLEAR:
+        case OS.WM_CUT:
+            OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
+            if (start [0] !is end [0]) {
+                newText = "";
+                call = true;
+            }
+            break;
+        case OS.WM_PASTE:
+            OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
+            newText = getClipboardText ();
+            break;
+        case OS.EM_UNDO:
+        case OS.WM_UNDO:
+            if (OS.SendMessage (hwndText, OS.EM_CANUNDO, 0, 0) !is 0) {
+                ignoreModify = true;
+                OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
+                OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
+                int length = OS.GetWindowTextLength (hwndText);
+                int [] newStart = new int [1], newEnd = new int [1];
+                OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd);
+                if (length !is 0 && newStart [0] !is newEnd [0]) {
+                    TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+                    OS.GetWindowText (hwndText, buffer, length + 1);
+                    newText = buffer.toString (newStart [0], newEnd [0] - newStart [0]);
+                } else {
+                    newText = "";
+                }
+                OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
+                ignoreModify = false;
+            }
+            break;
+    }
+    if (newText !is null) {
+        String oldText = newText;
+        newText = verifyText (newText, start [0], end [0], null);
+        if (newText is null) return LRESULT.ZERO;
+        if (!newText.equals (oldText)) {
+            if (call) {
+                OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
+            }
+            TCHAR buffer = new TCHAR (getCodePage (), newText, true);
+            if (msg is OS.WM_SETTEXT) {
+                int hHeap = OS.GetProcessHeap ();
+                int byteCount = buffer.length () * TCHAR.sizeof;
+                int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+                OS.MoveMemory (pszText, buffer, byteCount);
+                int code = OS.CallWindowProc (EditProc, hwndText, msg, wParam, pszText);
+                OS.HeapFree (hHeap, 0, pszText);
+                return new LRESULT (code);
+            } else {
+                OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer);
+                return LRESULT.ZERO;
+            }
+        }
+    }
+    return null;
+}
+
+LRESULT wmCommandChild (int wParam, int lParam) {
+    int code = wParam >> 16;
+    switch (code) {
+        case OS.EN_CHANGE:
+            if (ignoreModify) break;
+            int value = getSelectionText ();
+            if (value !is -1) {
+                int pos;
+                if (OS.IsWinCE) {
+                    pos = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0) & 0xFFFF;
+                } else {
+                    pos = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0);
+                }
+                if (pos !is value) setSelection (value, true, false, true);
+            }
+            sendEvent (DWT.Modify);
+            if (isDisposed ()) return LRESULT.ZERO;
+            break;
+    }
+    return super.wmCommandChild (wParam, lParam);
+}
+
+LRESULT wmKeyDown (int hwnd, int wParam, int lParam) {
+    LRESULT result = super.wmKeyDown (hwnd, wParam, lParam);
+    if (result !is null) return result;
+
+    /* Increment the value */
+    UDACCEL udaccel = new UDACCEL ();
+    OS.SendMessage (hwndUpDown, OS.UDM_GETACCEL, 1, udaccel);
+    int delta = 0;
+    switch (wParam) {
+        case OS.VK_UP: delta = udaccel.nInc; break;
+        case OS.VK_DOWN: delta = -udaccel.nInc; break;
+        case OS.VK_PRIOR: delta = pageIncrement; break;
+        case OS.VK_NEXT: delta = -pageIncrement; break;
+    }
+    if (delta !is 0) {
+        int value = getSelectionText ();
+        if (value !is -1) {
+            if (OS.IsWinCE) {
+                value = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0) & 0xFFFF;
+            } else {
+                value = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0);
+            }
+        }
+        int newValue = value + delta;
+        int [] max = new int [1], min = new int [1];
+        OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, max);
+        if ((style & DWT.WRAP) !is 0) {
+            if (newValue < min [0]) newValue = max [0];
+            if (newValue > max [0]) newValue = min [0];
+        }
+        newValue = Math.min (Math.max (min [0], newValue), max [0]);
+        if (value !is newValue) setSelection (newValue, true, true, true);
+    }
+
+    /*  Stop the edit control from moving the caret */
+    switch (wParam) {
+        case OS.VK_UP:
+        case OS.VK_DOWN:
+            return LRESULT.ZERO;
+    }
+    return result;
+}
+
+LRESULT wmKillFocus (int hwnd, int wParam, int lParam) {
+    int value = getSelectionText ();
+    if (value is -1) {
+        if (OS.IsWinCE) {
+            value = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS, 0, 0) & 0xFFFF;
+        } else {
+            value = OS.SendMessage (hwndUpDown, OS.UDM_GETPOS32, 0, 0);
+        }
+        setSelection (value, false, true, false);
+    }
+    return super.wmKillFocus (hwnd, wParam, lParam);
+}
+
+LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
+    switch (hdr.code) {
+        case OS.UDN_DELTAPOS:
+            NMUPDOWN lpnmud = new NMUPDOWN ();
+            OS.MoveMemory (lpnmud, lParam, NMUPDOWN.sizeof);
+            int value = lpnmud.iPos + lpnmud.iDelta;
+            int [] max = new int [1], min = new int [1];
+            OS.SendMessage (hwndUpDown , OS.UDM_GETRANGE32, min, max);
+            if ((style & DWT.WRAP) !is 0) {
+                if (value < min [0]) value = max [0];
+                if (value > max [0]) value = min [0];
+            }
+            /*
+            * The DWT.Modify event is sent after the widget has been
+            * updated with the new state.  Rather than allowing
+            * the default updown window proc to set the value
+            * when the user clicks on the updown control, set
+            * the value explicitly and stop the window proc
+            * from running.
+            */
+            value = Math.min (Math.max (min [0], value), max [0]);
+            if (value !is lpnmud.iPos) {
+                setSelection (value, true, true, true);
+            }
+            return LRESULT.ONE;
+    }
+    return super.wmNotifyChild (hdr, wParam, lParam);
+}
+
+LRESULT wmScrollChild (int wParam, int lParam) {
+    int code = wParam & 0xFFFF;
+    switch (code) {
+        case OS.SB_THUMBPOSITION:
+            postEvent (DWT.Selection);
+            break;
+    }
+    return super.wmScrollChild (wParam, lParam);
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Synchronizer.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Synchronizer;
+
+import dwt.widgets.Display;
+import dwt.widgets.RunnableLock;
+import dwt.dwthelper.Runnable;
+import dwt.dwthelper.System;
+import dwt.internal.Compatibility;
+
+import dwt.DWT;
+import tango.core.Thread;
+import tango.core.Exception;
+
+/**
+ * Instances of this class provide synchronization support
+ * for displays. A default instance is created automatically
+ * for each display, and this instance is sufficient for almost
+ * all applications.
+ * <p>
+ * <b>IMPORTANT:</b> Typical application code <em>never</em>
+ * needs to deal with this class. It is provided only to
+ * allow applications which require non-standard
+ * synchronization behavior to plug in the support they
+ * require. <em>Subclasses which override the methods in
+ * this class must ensure that the superclass methods are
+ * invoked in their implementations</em>
+ * </p>
+ *
+ * @see Display#setSynchronizer
+ */
+public class Synchronizer {
+    Display display;
+    int messageCount;
+    RunnableLock [] messages;
+    Object messageLock;
+    Thread syncThread;
+
+/**
+ * Constructs a new instance of this class.
+ *
+ * @param display the display to create the synchronizer on
+ */
+public this (Display display) {
+    this.display = display;
+    messageLock = new Object ();
+}
+
+void addLast (RunnableLock lock) {
+    bool wake = false;
+    synchronized (messageLock) {
+        if (messages is null) messages = new RunnableLock [4];
+        if (messageCount is messages.length) {
+            RunnableLock[] newMessages = new RunnableLock [messageCount + 4];
+            System.arraycopy (messages, 0, newMessages, 0, messageCount);
+            messages = newMessages;
+        }
+        messages [messageCount++] = lock;
+        wake = messageCount is 1;
+    }
+    if (wake) display.wakeThread ();
+}
+
+/**
+ * Causes the <code>run()</code> method of the runnable to
+ * be invoked by the user-interface thread at the next
+ * reasonable opportunity. The caller of this method continues
+ * to run in parallel, and is not notified when the
+ * runnable has completed.
+ *
+ * @param runnable code to run on the user-interface thread.
+ *
+ * @see #syncExec
+ */
+public void asyncExec (Runnable runnable) {
+    if (runnable is null) {
+        display.wake ();
+        return;
+    }
+    addLast (new RunnableLock (runnable));
+}
+
+int getMessageCount () {
+    synchronized (messageLock) {
+        return messageCount;
+    }
+}
+
+void releaseSynchronizer () {
+    display = null;
+    messages = null;
+    messageLock = null;
+    syncThread = null;
+}
+
+RunnableLock removeFirst () {
+    synchronized (messageLock) {
+        if (messageCount is 0) return null;
+        RunnableLock lock = messages [0];
+        System.arraycopy (messages, 1, messages, 0, --messageCount);
+        messages [messageCount] = null;
+        if (messageCount is 0) {
+            if (messages.length > 64) messages = null;
+        }
+        return lock;
+    }
+}
+
+bool runAsyncMessages () {
+    return runAsyncMessages (false);
+}
+
+bool runAsyncMessages (bool all) {
+    bool run = false;
+    do {
+        RunnableLock lock = removeFirst ();
+        if (lock is null) return run;
+        run = true;
+        synchronized (lock) {
+            syncThread = lock.thread;
+            try {
+                lock.run ();
+            } catch (TracedException t) {
+                lock.throwable = t;
+                DWT.error (DWT.ERROR_FAILED_EXEC, t);
+            } finally {
+                syncThread = null;
+                lock.notifyAll ();
+            }
+        }
+    } while (all);
+    return run;
+}
+
+/**
+ * Causes the <code>run()</code> method of the runnable to
+ * be invoked by the user-interface thread at the next
+ * reasonable opportunity. The thread which calls this method
+ * is suspended until the runnable completes.
+ *
+ * @param runnable code to run on the user-interface thread.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_FAILED_EXEC - if an exception occurred when executing the runnable</li>
+ * </ul>
+ *
+ * @see #asyncExec
+ */
+public void syncExec (Runnable runnable) {
+    if (display.isValidThread ()) {
+        if (runnable !is null) runnable.run ();
+        return;
+    }
+    if (runnable is null) {
+        display.wake ();
+        return;
+    }
+    RunnableLock lock = new RunnableLock (runnable);
+    /*
+     * Only remember the syncThread for syncExec.
+     */
+    lock.thread = Thread.getThis();
+    synchronized (lock) {
+        addLast (lock);
+        bool interrupted = false;
+        while (!lock.done ()) {
+            try {
+                lock.wait ();
+            } catch (SyncException e) {
+                interrupted = true;
+            }
+        }
+        if (interrupted) {
+            Compatibility.interrupt();
+        }
+        if (lock.throwable !is null) {
+            DWT.error (DWT.ERROR_FAILED_EXEC, lock.throwable);
+        }
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/TabFolder.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,979 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.TabFolder;
+
+import dwt.widgets.Composite;
+class TabFolder : Composite {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.ImageList;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.NMTTDISPINFO;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TCITEM;
+import dwt.internal.win32.TOOLINFO;
+import dwt.internal.win32.WINDOWPOS;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class implement the notebook user interface
+ * metaphor.  It allows the user to select a notebook page from
+ * set of pages.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>TabItem</code>.
+ * <code>Control</code> children are created and then set into a
+ * tab item using <code>TabItem#setControl</code>.
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to set a layout on it.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>TOP, BOTTOM</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles TOP and BOTTOM may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+public class TabFolder extends Composite {
+    TabItem [] items;
+    ImageList imageList;
+    static final int TabFolderProc;
+    static final TCHAR TabFolderClass = new TCHAR (0, OS.WC_TABCONTROL, true);
+
+    /*
+    * These are the undocumented control id's for the children of
+    * a tab control.  Since there are no constants for these values,
+    * they may change with different versions of Windows.
+    */
+    static final int ID_UPDOWN = 1;
+
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, TabFolderClass, lpWndClass);
+        TabFolderProc = lpWndClass.lpfnWndProc;
+        /*
+        * Feature in Windows.  The tab control window class
+        * uses the CS_HREDRAW and CS_VREDRAW style bits to
+        * force a full redraw of the control and all children
+        * when resized.  This causes flashing.  The fix is to
+        * register a new window class without these bits and
+        * implement special code that damages only the exposed
+        * area.
+        *
+        * NOTE:  Screen readers look for the exact class name
+        * of the control in order to provide the correct kind
+        * of assistance.  Therefore, it is critical that the
+        * new window class have the same name.  It is possible
+        * to register a local window class with the same name
+        * as a global class.  Since bits that affect the class
+        * are being changed, it is possible that other native
+        * code, other than DWT, could create a control with
+        * this class name, and fail unexpectedly.
+        */
+        int hInstance = OS.GetModuleHandle (null);
+        int hHeap = OS.GetProcessHeap ();
+        lpWndClass.hInstance = hInstance;
+        lpWndClass.style &= ~(OS.CS_HREDRAW | OS.CS_VREDRAW | OS.CS_GLOBALCLASS);
+        int byteCount = TabFolderClass.length () * TCHAR.sizeof;
+        int lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+        OS.MoveMemory (lpszClassName, TabFolderClass, byteCount);
+        lpWndClass.lpszClassName = lpszClassName;
+        OS.RegisterClass (lpWndClass);
+        OS.HeapFree (hHeap, 0, lpszClassName);
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TabFolder (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's selection, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the item field of the event object is valid.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the user changes the receiver's selection
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener(listener);
+    addListener(DWT.Selection,typedListener);
+    addListener(DWT.DefaultSelection,typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.CallWindowProc (TabFolderProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    /*
+    * When the DWT.TOP style has not been set, force the
+    * tabs to be on the bottom for tab folders on PPC.
+    */
+    if (OS.IsPPC) {
+        if ((style & DWT.TOP) is 0) style |= DWT.BOTTOM;
+    }
+    style = checkBits (style, DWT.TOP, DWT.BOTTOM, 0, 0, 0, 0);
+
+    /*
+    * Even though it is legal to create this widget
+    * with scroll bars, they serve no useful purpose
+    * because they do not automatically scroll the
+    * widget's client area.  The fix is to clear
+    * the DWT style.
+    */
+    return style & ~(DWT.H_SCROLL | DWT.V_SCROLL);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    Point size = super.computeSize (wHint, hHint, changed);
+    RECT insetRect = new RECT (), itemRect = new RECT ();
+    OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, insetRect);
+    int width = insetRect.left - insetRect.right;
+    int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+    if (count !is 0) {
+        OS.SendMessage (handle, OS.TCM_GETITEMRECT, count - 1, itemRect);
+        width = Math.max (width, itemRect.right - insetRect.right);
+    }
+    RECT rect = new RECT ();
+    OS.SetRect (rect, 0, 0, width, size.y);
+    OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 1, rect);
+    int border = getBorderWidth ();
+    rect.left -= border;  rect.right += border;
+    width = rect.right - rect.left;
+    size.x = Math.max (width, size.x);
+    return size;
+}
+
+public Rectangle computeTrim (int x, int y, int width, int height) {
+    checkWidget ();
+    RECT rect = new RECT ();
+    OS.SetRect (rect, x, y, x + width, y + height);
+    OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 1, rect);
+    int border = getBorderWidth ();
+    rect.left -= border;  rect.right += border;
+    rect.top -= border;  rect.bottom += border;
+    int newWidth = rect.right - rect.left;
+    int newHeight = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, newWidth, newHeight);
+}
+
+void createItem (TabItem item, int index) {
+    int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= index && index <= count)) error (DWT.ERROR_INVALID_RANGE);
+    if (count is items.length) {
+        TabItem [] newItems = new TabItem [items.length + 4];
+        System.arraycopy (items, 0, newItems, 0, items.length);
+        items = newItems;
+    }
+    TCITEM tcItem = new TCITEM ();
+    if (OS.SendMessage (handle, OS.TCM_INSERTITEM, index, tcItem) is -1) {
+        error (DWT.ERROR_ITEM_NOT_ADDED);
+    }
+    System.arraycopy (items, index, items, index + 1, count - index);
+    items [index] = item;
+
+    /*
+    * Send a selection event when the item that is added becomes
+    * the new selection.  This only happens when the first item
+    * is added.
+    */
+    if (count is 0) {
+        Event event = new Event ();
+        event.item = items [0];
+        sendEvent (DWT.Selection, event);
+        // the widget could be destroyed at this point
+    }
+}
+
+void createHandle () {
+    super.createHandle ();
+    state &= ~(CANVAS | THEME_BACKGROUND);
+
+    /* Enable the flat look for tab folders on PPC */
+    if (OS.IsPPC) {
+        OS.SendMessage (handle, OS.CCM_SETVERSION, 0x020c /*COMCTL32_VERSION*/, 0);
+    }
+
+    /*
+    * Feature in Windows.  Despite the fact that the
+    * tool tip text contains \r\n, the tooltip will
+    * not honour the new line unless TTM_SETMAXTIPWIDTH
+    * is set.  The fix is to set TTM_SETMAXTIPWIDTH to
+    * a large value.
+    */
+    int hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
+    OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
+}
+
+void createWidget () {
+    super.createWidget ();
+    items = new TabItem [4];
+}
+
+void destroyItem (TabItem item) {
+    int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+    int index = 0;
+    while (index < count) {
+        if (items [index] is item) break;
+        index++;
+    }
+    if (index is count) return;
+    int selectionIndex = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
+    if (OS.SendMessage (handle, OS.TCM_DELETEITEM, index, 0) is 0) {
+        error (DWT.ERROR_ITEM_NOT_REMOVED);
+    }
+    System.arraycopy (items, index + 1, items, index, --count - index);
+    items [count] = null;
+    if (count is 0) {
+        if (imageList !is null) {
+            OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0);
+            display.releaseImageList (imageList);
+        }
+        imageList = null;
+        items = new TabItem [4];
+    }
+    if (count > 0 && index is selectionIndex) {
+        setSelection (Math.max (0, selectionIndex - 1), true);
+    }
+}
+
+void drawThemeBackground (int hDC, int hwnd, RECT rect) {
+    RECT rect2 = new RECT ();
+    OS.GetClientRect (handle, rect2);
+    OS.MapWindowPoints (handle, hwnd, rect2, 2);
+    if (OS.IntersectRect (new RECT (), rect2, rect)) {
+        OS.DrawThemeBackground (display.hTabTheme (), hDC, OS.TABP_BODY, 0, rect2, null);
+    }
+}
+
+Control findThemeControl () {
+    /* It is not possible to change the background of this control */
+    return this;
+}
+
+public Rectangle getClientArea () {
+    checkWidget ();
+    forceResize ();
+    RECT rect = new RECT ();
+    OS.GetClientRect (handle, rect);
+    OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, rect);
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TabItem getItem (int index) {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= index && index < count)) error (DWT.ERROR_INVALID_RANGE);
+    return items [index];
+}
+
+/**
+ * Returns the number of items contained in the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+}
+
+/**
+ * Returns an array of <code>TabItem</code>s which are the items
+ * in the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TabItem [] getItems () {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+    TabItem [] result = new TabItem [count];
+    System.arraycopy (items, 0, result, 0, count);
+    return result;
+}
+
+/**
+ * Returns an array of <code>TabItem</code>s that are currently
+ * selected in the receiver. An empty array indicates that no
+ * items are selected.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its selection, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ * @return an array representing the selection
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TabItem [] getSelection () {
+    checkWidget ();
+    int index = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
+    if (index is -1) return new TabItem [0];
+    return new TabItem [] {items [index]};
+}
+
+/**
+ * Returns the zero-relative index of the item which is currently
+ * selected in the receiver, or -1 if no item is selected.
+ *
+ * @return the index of the selected item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionIndex () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
+}
+
+int imageIndex (Image image) {
+    if (image is null) return OS.I_IMAGENONE;
+    if (imageList is null) {
+        Rectangle bounds = image.getBounds ();
+        imageList = display.getImageList (style & DWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
+        int index = imageList.add (image);
+        int hImageList = imageList.getHandle ();
+        OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, hImageList);
+        return index;
+    }
+    int index = imageList.indexOf (image);
+    if (index is -1) {
+        index = imageList.add (image);
+    } else {
+        imageList.put (index, image);
+    }
+    return index;
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (TabItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<count; i++) {
+        if (items [i] is item) return i;
+    }
+    return -1;
+}
+
+Point minimumSize (int wHint, int hHint, bool flushCache) {
+    Control [] children = _getChildren ();
+    int width = 0, height = 0;
+    for (int i=0; i<children.length; i++) {
+        Control child = children [i];
+        int index = 0;
+        int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+        while (index < count) {
+            if (items [index].control is child) break;
+            index++;
+        }
+        if (index is count) {
+            Rectangle rect = child.getBounds ();
+            width = Math.max (width, rect.x + rect.width);
+            height = Math.max (height, rect.y + rect.height);
+        } else {
+            Point size = child.computeSize (wHint, hHint, flushCache);
+            width = Math.max (width, size.x);
+            height = Math.max (height, size.y);
+        }
+    }
+    return new Point (width, height);
+}
+
+bool mnemonicHit (char key) {
+    for (int i=0; i<items.length; i++) {
+        TabItem item = items [i];
+        if (item !is null) {
+            char ch = findMnemonic (item.getText ());
+            if (Character.toUpperCase (key) is Character.toUpperCase (ch)) {
+                if (forceFocus ()) {
+                    if (i !is getSelectionIndex ()) setSelection (i, true);
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+bool mnemonicMatch (char key) {
+    for (int i=0; i<items.length; i++) {
+        TabItem item = items [i];
+        if (item !is null) {
+            char ch = findMnemonic (item.getText ());
+            if (Character.toUpperCase (key) is Character.toUpperCase (ch)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void releaseChildren (bool destroy) {
+    if (items !is null) {
+        int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+        for (int i=0; i<count; i++) {
+            TabItem item = items [i];
+            if (item !is null && !item.isDisposed ()) {
+                item.release (false);
+            }
+        }
+        items = null;
+    }
+    super.releaseChildren (destroy);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if (imageList !is null) {
+        OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0);
+        display.releaseImageList (imageList);
+    }
+    imageList = null;
+}
+
+void removeControl (Control control) {
+    super.removeControl (control);
+    int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<count; i++) {
+        TabItem item = items [i];
+        if (item.control is control) item.setControl (null);
+    }
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's selection.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Sets the receiver's selection to the given item.
+ * The current selected is first cleared, then the new item is
+ * selected.
+ *
+ * @param item the item to select
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSelection (TabItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setSelection (new TabItem [] {item});
+}
+
+/**
+ * Sets the receiver's selection to be the given array of items.
+ * The current selected is first cleared, then the new items are
+ * selected.
+ *
+ * @param items the array of items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the items array is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (TabItem [] items) {
+    checkWidget ();
+    if (items is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (items.length is 0) {
+        setSelection (-1, false);
+    } else {
+        for (int i=items.length-1; i>=0; --i) {
+            int index = indexOf (items [i]);
+            if (index !is -1) setSelection (index, false);
+        }
+    }
+}
+
+public void setFont (Font font) {
+    checkWidget ();
+    Rectangle oldRect = getClientArea ();
+    super.setFont (font);
+    Rectangle newRect = getClientArea ();
+    if (!oldRect.equals (newRect)) {
+        sendResize ();
+        int index = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
+        if (index !is -1) {
+            TabItem item = items [index];
+            Control control = item.control;
+            if (control !is null && !control.isDisposed ()) {
+                control.setBounds (getClientArea ());
+            }
+        }
+    }
+}
+
+/**
+ * Selects the item at the given zero-relative index in the receiver.
+ * If the item at the index was already selected, it remains selected.
+ * The current selection is first cleared, then the new items are
+ * selected. Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to select
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (int index) {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= index && index < count)) return;
+    setSelection (index, false);
+}
+
+void setSelection (int index, bool notify) {
+    int oldIndex = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
+    if (oldIndex is index) return;
+    if (oldIndex !is -1) {
+        TabItem item = items [oldIndex];
+        Control control = item.control;
+        if (control !is null && !control.isDisposed ()) {
+            control.setVisible (false);
+        }
+    }
+    OS.SendMessage (handle, OS.TCM_SETCURSEL, index, 0);
+    int newIndex = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
+    if (newIndex !is -1) {
+        TabItem item = items [newIndex];
+        Control control = item.control;
+        if (control !is null && !control.isDisposed ()) {
+            control.setBounds (getClientArea ());
+            control.setVisible (true);
+        }
+        if (notify) {
+            Event event = new Event ();
+            event.item = item;
+            sendEvent (DWT.Selection, event);
+        }
+    }
+}
+
+String toolTipText (NMTTDISPINFO hdr) {
+    if ((hdr.uFlags & OS.TTF_IDISHWND) !is 0) {
+        return null;
+    }
+    int index = hdr.idFrom;
+    int hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
+    if (hwndToolTip is hdr.hwndFrom) {
+        if (toolTipText !is null) return "";
+        if (0 <= index && index < items.length) {
+            TabItem item = items [index];
+            if (item !is null) return item.toolTipText;
+        }
+    }
+    return super.toolTipText (hdr);
+}
+
+bool traversePage (bool next) {
+    int count = getItemCount ();
+    if (count <= 1) return false;
+    int index = getSelectionIndex ();
+    if (index is -1) {
+        index = 0;
+    } else {
+        int offset = (next) ? 1 : -1;
+        index = (index + offset + count) % count;
+    }
+    setSelection (index, true);
+    if (index is getSelectionIndex ()) {
+        OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
+        return true;
+    }
+    return false;
+}
+
+int widgetStyle () {
+    /*
+    * Bug in Windows.  Under certain circumstances,
+    * when TCM_SETITEM is used to change the text
+    * in a tab item, the tab folder draws on top
+    * of the client area.  The fix is ensure that
+    * this cannot happen by setting WS_CLIPCHILDREN.
+    */
+    int bits = super.widgetStyle () | OS.WS_CLIPCHILDREN;
+    if ((style & DWT.NO_FOCUS) !is 0) bits |= OS.TCS_FOCUSNEVER;
+    if ((style & DWT.BOTTOM) !is 0) bits |= OS.TCS_BOTTOM;
+    return bits | OS.TCS_TABS | OS.TCS_TOOLTIPS;
+}
+
+TCHAR windowClass () {
+    return TabFolderClass;
+}
+
+int windowProc () {
+    return TabFolderProc;
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
+    /*
+    * Return DLGC_BUTTON so that mnemonics will be
+    * processed without needing to press the ALT key
+    * when the widget has focus.
+    */
+    if (result !is null) return result;
+    return new LRESULT (OS.DLGC_BUTTON);
+}
+
+LRESULT WM_MOUSELEAVE (int wParam, int lParam) {
+    LRESULT result = super.WM_MOUSELEAVE (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug in Windows.  On XP, when a tooltip is
+    * hidden due to a time out or mouse press,
+    * the tooltip remains active although no
+    * longer visible and won't show again until
+    * another tooltip becomes active.  If there
+    * is only one tooltip in the window,  it will
+    * never show again.  The fix is to remove the
+    * current tooltip and add it again every time
+    * the mouse leaves the control.
+    */
+    if (OS.COMCTL32_MAJOR >= 6) {
+        TOOLINFO lpti = new TOOLINFO ();
+        lpti.cbSize = TOOLINFO.sizeof;
+        int hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
+        if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) !is 0) {
+            if ((lpti.uFlags & OS.TTF_IDISHWND) is 0) {
+                OS.SendMessage (hwndToolTip, OS.TTM_DELTOOL, 0, lpti);
+                OS.SendMessage (hwndToolTip, OS.TTM_ADDTOOL, 0, lpti);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_NCHITTEST (int wParam, int lParam) {
+    LRESULT result = super.WM_NCHITTEST (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  The tab control implements
+    * WM_NCHITTEST to return HTCLIENT when the cursor
+    * is inside the tab buttons.  This causes mouse
+    * events like WM_MOUSEMOVE to be delivered to the
+    * parent.  Also, tool tips for the tab control are
+    * never invoked because tool tips rely on mouse
+    * events to be delivered to the window that wants
+    * to display the tool tip.  The fix is to call the
+    * default window proc that returns HTCLIENT when
+    * the mouse is in the client area.
+    */
+    int hittest = OS.DefWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam);
+    return new LRESULT (hittest);
+}
+
+LRESULT WM_NOTIFY (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When the tab folder window
+    * proc processes WM_NOTIFY, it forwards this
+    * message to its parent.  This is done so that
+    * children of this control that send this message
+    * type to their parent will notify not only
+    * this control but also the parent of this control,
+    * which is typically the application window and
+    * the window that is looking for the message.
+    * If the control did not forward the message,
+    * applications would have to subclass the control
+    * window to see the message. Because the control
+    * window is subclassed by DWT, the message
+    * is delivered twice, once by DWT and once when
+    * the message is forwarded by the window proc.
+    * The fix is to avoid calling the window proc
+    * for this control.
+    */
+    LRESULT result = super.WM_NOTIFY (wParam, lParam);
+    if (result !is null) return result;
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_PARENTNOTIFY (int wParam, int lParam) {
+    LRESULT result = super.WM_PARENTNOTIFY (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  Windows does not explicitly set the orientation of
+    * the buddy control.  Instead, the orientation is inherited when WS_EX_LAYOUTRTL
+    * is specified for the tab folder.  This means that when both WS_EX_LAYOUTRTL
+    * and WS_EX_NOINHERITLAYOUT are specified for the tab folder, the buddy control
+    * will not be oriented correctly.  The fix is to explicitly set the orientation
+    * for the buddy control.
+    *
+    * NOTE: WS_EX_LAYOUTRTL is not supported on Windows NT.
+    */
+    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return result;
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+        int code = wParam & 0xFFFF;
+        switch (code) {
+            case OS.WM_CREATE: {
+                int id = (wParam >> 16), hwnd = lParam;
+                if (id is ID_UPDOWN) {
+                    int bits = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE);
+                    OS.SetWindowLong (hwnd, OS.GWL_EXSTYLE, bits | OS.WS_EX_LAYOUTRTL);
+                }
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in the resize
+    * event.  If this happens, end the processing of the
+    * Windows message by returning the result of the
+    * WM_SIZE message.
+    */
+    if (isDisposed ()) return result;
+    int index = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
+    if (index !is -1) {
+        TabItem item = items [index];
+        Control control = item.control;
+        if (control !is null && !control.isDisposed ()) {
+            control.setBounds (getClientArea ());
+        }
+    }
+    return result;
+}
+
+LRESULT WM_WINDOWPOSCHANGING (int wParam, int lParam) {
+    LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam);
+    if (result !is null) return result;
+    if (!OS.IsWindowVisible (handle)) return result;
+    WINDOWPOS lpwp = new WINDOWPOS ();
+    OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
+    if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) !is 0) {
+        return result;
+    }
+    // TEMPORARY CODE
+//  if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+//      OS.InvalidateRect (handle, null, true);
+//      return result;
+//  }
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.TCS_MULTILINE) !is 0) {
+        OS.InvalidateRect (handle, null, true);
+        return result;
+    }
+    RECT rect = new RECT ();
+    OS.SetRect (rect, 0, 0, lpwp.cx, lpwp.cy);
+    OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, rect);
+    int newWidth = rect.right - rect.left;
+    int newHeight = rect.bottom - rect.top;
+    OS.GetClientRect (handle, rect);
+    int oldWidth = rect.right - rect.left;
+    int oldHeight = rect.bottom - rect.top;
+    if (newWidth is oldWidth && newHeight is oldHeight) {
+        return result;
+    }
+    RECT inset = new RECT ();
+    OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, inset);
+    int marginX = -inset.right, marginY = -inset.bottom;
+    if (newWidth !is oldWidth) {
+        int left = oldWidth;
+        if (newWidth < oldWidth) left = newWidth;
+        OS.SetRect (rect, left - marginX, 0, newWidth, newHeight);
+        OS.InvalidateRect (handle, rect, true);
+    }
+    if (newHeight !is oldHeight) {
+        int bottom = oldHeight;
+        if (newHeight < oldHeight) bottom = newHeight;
+        if (newWidth < oldWidth) oldWidth -= marginX;
+        OS.SetRect (rect, 0, bottom - marginY, oldWidth, newHeight);
+        OS.InvalidateRect (handle, rect, true);
+    }
+    return result;
+}
+
+LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
+    int code = hdr.code;
+    switch (code) {
+        case OS.TCN_SELCHANGE:
+        case OS.TCN_SELCHANGING:
+            TabItem item = null;
+            int index = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
+            if (index !is -1) item = items [index];
+            if (item !is null) {
+                Control control = item.control;
+                if (control !is null && !control.isDisposed ()) {
+                    if (code is OS.TCN_SELCHANGE) {
+                        control.setBounds (getClientArea ());
+                    }
+                    control.setVisible (code is OS.TCN_SELCHANGE);
+                }
+            }
+            if (code is OS.TCN_SELCHANGE) {
+                Event event = new Event ();
+                event.item = item;
+                postEvent (DWT.Selection, event);
+            }
+    }
+    return super.wmNotifyChild (hdr, wParam, lParam);
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/TabItem.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,344 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.TabItem;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+class TabItem : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Image;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TCITEM;
+
+/**
+ * Instances of this class represent a selectable user interface object
+ * corresponding to a tab for a page in a tab folder.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class TabItem extends Item {
+    TabFolder parent;
+    Control control;
+    String toolTipText;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>TabFolder</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TabItem (TabFolder parent, int style) {
+    super (parent, style);
+    this.parent = parent;
+    parent.createItem (this, parent.getItemCount ());
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>TabFolder</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TabItem (TabFolder parent, int style, int index) {
+    super (parent, style);
+    this.parent = parent;
+    parent.createItem (this, index);
+}
+
+void _setText (int index, String string) {
+    /*
+    * Bug in Windows.  In version 6.00 of COMCTL32.DLL, tab
+    * items with an image and a label that includes '&' cause
+    * the tab to draw incorrectly (even when doubled '&&').
+    * The image overlaps the label.  The fix is to remove
+    * all '&' characters from the string.
+    */
+    if (OS.COMCTL32_MAJOR >= 6 && image !is null) {
+        if (string.indexOf ('&') !is -1) {
+            int length = string.length ();
+            char[] text = new char [length];
+            string.getChars ( 0, length, text, 0);
+            int i = 0, j = 0;
+            for (i=0; i<length; i++) {
+                if (text[i] !is '&') text [j++] = text [i];
+            }
+            if (j < i) string = new String (text, 0, j);
+        }
+    }
+    int hwnd = parent.handle;
+    int hHeap = OS.GetProcessHeap ();
+    TCHAR buffer = new TCHAR (parent.getCodePage (), string, true);
+    int byteCount = buffer.length () * TCHAR.sizeof;
+    int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+    OS.MoveMemory (pszText, buffer, byteCount);
+    TCITEM tcItem = new TCITEM ();
+    tcItem.mask = OS.TCIF_TEXT;
+    tcItem.pszText = pszText;
+    OS.SendMessage (hwnd, OS.TCM_SETITEM, index, tcItem);
+    OS.HeapFree (hHeap, 0, pszText);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+void destroyWidget () {
+    parent.destroyItem (this);
+    releaseHandle ();
+}
+
+/**
+ * Returns the control that is used to fill the client area of
+ * the tab folder when the user selects the tab item.  If no
+ * control has been set, return <code>null</code>.
+ * <p>
+ * @return the control
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Control getControl () {
+    checkWidget();
+    return control;
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>TabFolder</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TabFolder getParent () {
+    checkWidget();
+    return parent;
+}
+
+/**
+ * Returns the receiver's tool tip text, or null if it has
+ * not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getToolTipText () {
+    checkWidget();
+    return toolTipText;
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+}
+
+void releaseParent () {
+    super.releaseParent ();
+    int index = parent.indexOf (this);
+    if (index is parent.getSelectionIndex ()) {
+        if (control !is null) control.setVisible (false);
+    }
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    control = null;
+}
+
+/**
+ * Sets the control that is used to fill the client area of
+ * the tab folder when the user selects the tab item.
+ * <p>
+ * @param control the new control (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setControl (Control control) {
+    checkWidget();
+    if (control !is null) {
+        if (control.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT);
+        if (control.parent !is parent) error (DWT.ERROR_INVALID_PARENT);
+    }
+    if (this.control !is null && this.control.isDisposed ()) {
+        this.control = null;
+    }
+    Control oldControl = this.control, newControl = control;
+    this.control = control;
+    int index = parent.indexOf (this);
+    if (index !is parent.getSelectionIndex ()) {
+        if (newControl !is null) newControl.setVisible (false);
+        return;
+    }
+    if (newControl !is null) {
+        newControl.setBounds (parent.getClientArea ());
+        newControl.setVisible (true);
+    }
+    if (oldControl !is null) oldControl.setVisible (false);
+}
+
+public void setImage (Image image) {
+    checkWidget();
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    super.setImage (image);
+    /*
+    * Bug in Windows.  In version 6.00 of COMCTL32.DLL, tab
+    * items with an image and a label that includes '&' cause
+    * the tab to draw incorrectly (even when doubled '&&').
+    * The image overlaps the label.  The fix is to remove
+    * all '&' characters from the string and set the text
+    * whenever the image or text is changed.
+    */
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if (text.indexOf ('&') !is -1) _setText (index, text);
+    }
+    int hwnd = parent.handle;
+    TCITEM tcItem = new TCITEM ();
+    tcItem.mask = OS.TCIF_IMAGE;
+    tcItem.iImage = parent.imageIndex (image);
+    OS.SendMessage (hwnd, OS.TCM_SETITEM, index, tcItem);
+}
+/**
+ * Sets the receiver's text.  The string may include
+ * the mnemonic character.
+ * </p>
+ * <p>
+ * Mnemonics are indicated by an '&amp;' that causes the next
+ * character to be the mnemonic.  When the user presses a
+ * key sequence that matches the mnemonic, a selection
+ * event occurs. On most platforms, the mnemonic appears
+ * underlined but may be emphasised in a platform specific
+ * manner.  The mnemonic indicator character '&amp;' can be
+ * escaped by doubling it in the string, causing a single
+ * '&amp;' to be displayed.
+ * </p>
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ */
+public void setText (String string) {
+    checkWidget();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (string.equals (text)) return;
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    super.setText (string);
+    _setText (index, string);
+}
+
+/**
+ * Sets the receiver's tool tip text to the argument, which
+ * may be null indicating that no tool tip text should be shown.
+ *
+ * @param string the new tool tip text (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setToolTipText (String string) {
+    checkWidget();
+    toolTipText = string;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Table.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,6147 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Table;
+
+import dwt.widgets.Composite;
+class Table : Composite {
+}
+/++
+
+//import java.util.*;
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.ImageList;
+import dwt.internal.win32.HDHITTESTINFO;
+import dwt.internal.win32.HDITEM;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.LVCOLUMN;
+import dwt.internal.win32.LVHITTESTINFO;
+import dwt.internal.win32.LVITEM;
+import dwt.internal.win32.MEASUREITEMSTRUCT;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.NMHEADER;
+import dwt.internal.win32.NMLISTVIEW;
+import dwt.internal.win32.NMLVCUSTOMDRAW;
+import dwt.internal.win32.NMLVDISPINFO;
+import dwt.internal.win32.NMLVODSTATECHANGE;
+import dwt.internal.win32.NMRGINFO;
+import dwt.internal.win32.NMTTDISPINFO;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.PAINTSTRUCT;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SCROLLINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+import dwt.internal.win32.TOOLINFO;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class implement a selectable user interface
+ * object that displays a list of images and strings and issues
+ * notification when selected.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>TableItem</code>.
+ * </p><p>
+ * Style <code>VIRTUAL</code> is used to create a <code>Table</code> whose
+ * <code>TableItem</code>s are to be populated by the client on an on-demand basis
+ * instead of up-front.  This can provide significant performance improvements for
+ * tables that are very large or for which <code>TableItem</code> population is
+ * expensive (for example, retrieving values from an external source).
+ * </p><p>
+ * Here is an example of using a <code>Table</code> with style <code>VIRTUAL</code>:
+ * <code><pre>
+ *  final Table table = new Table (parent, DWT.VIRTUAL | DWT.BORDER);
+ *  table.setItemCount (1000000);
+ *  table.addListener (DWT.SetData, new Listener () {
+ *      public void handleEvent (Event event) {
+ *          TableItem item = (TableItem) event.item;
+ *          int index = table.indexOf (item);
+ *          item.setText ("Item " + index);
+ *          System.out.println (item.getText ());
+ *      }
+ *  });
+ * </pre></code>
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to add <code>Control</code> children to it,
+ * or set a layout on it.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection, DefaultSelection, SetData, MeasureItem, EraseItem, PaintItem</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles SINGLE, and MULTI may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class Table extends Composite {
+    TableItem [] items;
+    TableColumn [] columns;
+    int columnCount, customCount;
+    ImageList imageList, headerImageList;
+    TableItem currentItem;
+    TableColumn sortColumn;
+    bool ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus, ignoreDrawSelection, ignoreDrawHot;
+    bool customDraw, dragStarted, explorerTheme, firstColumnImage, fixScrollWidth, tipRequested, wasSelected, wasResized;
+    bool ignoreActivate, ignoreSelect, ignoreShrink, ignoreResize, ignoreColumnMove, ignoreColumnResize;
+    int headerToolTipHandle, itemHeight, lastIndexOf, lastWidth, sortDirection, resizeCount, selectionForeground, hotIndex;
+    static /*final*/ int HeaderProc;
+    static final int INSET = 4;
+    static final int GRID_WIDTH = 1;
+    static final int SORT_WIDTH = 10;
+    static final int HEADER_MARGIN = 12;
+    static final int HEADER_EXTRA = 3;
+    static final int VISTA_EXTRA = 2;
+    static final int EXPLORER_EXTRA = 2;
+    static final bool EXPLORER_THEME = true;
+    static final int TableProc;
+    static final TCHAR TableClass = new TCHAR (0, OS.WC_LISTVIEW, true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, TableClass, lpWndClass);
+        TableProc = lpWndClass.lpfnWndProc;
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#SINGLE
+ * @see DWT#MULTI
+ * @see DWT#CHECK
+ * @see DWT#FULL_SELECTION
+ * @see DWT#HIDE_SELECTION
+ * @see DWT#VIRTUAL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Table (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+void _addListener (int eventType, Listener listener) {
+    super._addListener (eventType, listener);
+    switch (eventType) {
+        case DWT.MeasureItem:
+        case DWT.EraseItem:
+        case DWT.PaintItem:
+            setCustomDraw (true);
+            setBackgroundTransparent (true);
+            if (OS.COMCTL32_MAJOR < 6) style |= DWT.DOUBLE_BUFFERED;
+            //TODO - LVS_EX_LABELTIP causes white rectangles (turn it off)
+            OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_LABELTIP, 0);
+            break;
+    }
+}
+
+TableItem _getItem (int index) {
+    if ((style & DWT.VIRTUAL) is 0) return items [index];
+    if (items [index] !is null) return items [index];
+    return items [index] = new TableItem (this, DWT.NONE, -1, false);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's selection, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the item field of the event object is valid.
+ * If the receiver has the <code>DWT.CHECK</code> style and the check selection changes,
+ * the event object detail field contains the value <code>DWT.CHECK</code>.
+ * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
+ * The item field of the event object is valid for default selection, but the detail field is not used.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the user changes the receiver's selection
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    return callWindowProc (hwnd, msg, wParam, lParam, false);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam, bool forceSelect) {
+    if (handle is 0) return 0;
+    if (handle !is hwnd) {
+        return OS.CallWindowProc (HeaderProc, hwnd, msg, wParam, lParam);
+    }
+    int topIndex = 0;
+    bool checkSelection = false, checkActivate = false, redraw = false;
+    switch (msg) {
+        /* Keyboard messages */
+        /*
+        * Feature in Windows.  Windows sends LVN_ITEMACTIVATE from WM_KEYDOWN
+        * instead of WM_CHAR.  This means that application code that expects
+        * to consume the key press and therefore avoid a DWT.DefaultSelection
+        * event will fail.  The fix is to ignore LVN_ITEMACTIVATE when it is
+        * caused by WM_KEYDOWN and send DWT.DefaultSelection from WM_CHAR.
+        */
+        case OS.WM_KEYDOWN:
+            checkActivate = true;
+            //FALL THROUGH
+        case OS.WM_CHAR:
+        case OS.WM_IME_CHAR:
+        case OS.WM_KEYUP:
+        case OS.WM_SYSCHAR:
+        case OS.WM_SYSKEYDOWN:
+        case OS.WM_SYSKEYUP:
+            //FALL THROUGH
+
+        /* Scroll messages */
+        case OS.WM_HSCROLL:
+        case OS.WM_VSCROLL:
+            //FALL THROUGH
+
+        /* Resize messages */
+        case OS.WM_WINDOWPOSCHANGED:
+            redraw = findImageControl () !is null && drawCount is 0 && OS.IsWindowVisible (handle);
+            if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+            //FALL THROUGH
+
+        /* Mouse messages */
+        case OS.WM_LBUTTONDBLCLK:
+        case OS.WM_LBUTTONDOWN:
+        case OS.WM_LBUTTONUP:
+        case OS.WM_MBUTTONDBLCLK:
+        case OS.WM_MBUTTONDOWN:
+        case OS.WM_MBUTTONUP:
+        case OS.WM_MOUSEHOVER:
+        case OS.WM_MOUSELEAVE:
+        case OS.WM_MOUSEMOVE:
+        case OS.WM_MOUSEWHEEL:
+        case OS.WM_RBUTTONDBLCLK:
+        case OS.WM_RBUTTONDOWN:
+        case OS.WM_RBUTTONUP:
+        case OS.WM_XBUTTONDBLCLK:
+        case OS.WM_XBUTTONDOWN:
+        case OS.WM_XBUTTONUP:
+            checkSelection = true;
+            //FALL THROUGH
+
+        /* Other messages */
+        case OS.WM_SETFONT:
+        case OS.WM_TIMER: {
+            if (findImageControl () !is null) {
+                topIndex = OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
+            }
+        }
+    }
+    bool oldSelected = wasSelected;
+    if (checkSelection) wasSelected = false;
+    if (checkActivate) ignoreActivate = true;
+    int code = OS.CallWindowProc (TableProc, hwnd, msg, wParam, lParam);
+    if (checkActivate) ignoreActivate = false;
+    if (checkSelection) {
+        if (wasSelected || forceSelect) {
+            Event event = new Event ();
+            int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
+            if (index !is -1) event.item = _getItem (index);
+            postEvent (DWT.Selection, event);
+        }
+        wasSelected = oldSelected;
+    }
+    switch (msg) {
+        /* Keyboard messages */
+        case OS.WM_KEYDOWN:
+        case OS.WM_CHAR:
+        case OS.WM_IME_CHAR:
+        case OS.WM_KEYUP:
+        case OS.WM_SYSCHAR:
+        case OS.WM_SYSKEYDOWN:
+        case OS.WM_SYSKEYUP:
+            //FALL THROUGH
+
+        /* Scroll messages */
+        case OS.WM_HSCROLL:
+        case OS.WM_VSCROLL:
+            //FALL THROUGH
+
+        /* Resize messages */
+        case OS.WM_WINDOWPOSCHANGED:
+            if (redraw) {
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+                OS.InvalidateRect (handle, null, true);
+                int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                if (hwndHeader !is 0) OS.InvalidateRect (hwndHeader, null, true);
+            }
+            //FALL THROUGH
+
+        /* Mouse messages */
+        case OS.WM_LBUTTONDBLCLK:
+        case OS.WM_LBUTTONDOWN:
+        case OS.WM_LBUTTONUP:
+        case OS.WM_MBUTTONDBLCLK:
+        case OS.WM_MBUTTONDOWN:
+        case OS.WM_MBUTTONUP:
+        case OS.WM_MOUSEHOVER:
+        case OS.WM_MOUSELEAVE:
+        case OS.WM_MOUSEMOVE:
+        case OS.WM_MOUSEWHEEL:
+        case OS.WM_RBUTTONDBLCLK:
+        case OS.WM_RBUTTONDOWN:
+        case OS.WM_RBUTTONUP:
+        case OS.WM_XBUTTONDBLCLK:
+        case OS.WM_XBUTTONDOWN:
+        case OS.WM_XBUTTONUP:
+            //FALL THROUGH
+
+        /* Other messages */
+        case OS.WM_SETFONT:
+        case OS.WM_TIMER: {
+            if (findImageControl () !is null) {
+                if (topIndex !is OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
+                    OS.InvalidateRect (handle, null, true);
+                }
+            }
+        }
+    }
+    return code;
+}
+
+static int checkStyle (int style) {
+    /*
+    * Feature in Windows.  It is not possible to create
+    * a table that does not have scroll bars.  Therefore,
+    * no matter what style bits are specified, set the
+    * H_SCROLL and V_SCROLL bits so that the DWT style
+    * will match the widget that Windows creates.
+    */
+    style |= DWT.H_SCROLL | DWT.V_SCROLL;
+    return checkBits (style, DWT.SINGLE, DWT.MULTI, 0, 0, 0, 0);
+}
+
+LRESULT CDDS_ITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    int hDC = nmcd.hdc;
+    if (explorerTheme && !ignoreCustomDraw) {
+        hotIndex = -1;
+        if (hooks (DWT.EraseItem) && nmcd.left !is nmcd.right) {
+            OS.RestoreDC (hDC, -1);
+        }
+    }
+    /*
+    * Bug in Windows.  When the table has the extended style
+    * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
+    * CLR_NONE to make the table transparent, Windows fills
+    * a black rectangle around any column that contains an
+    * image.  The fix is clear LVS_EX_FULLROWSELECT during
+    * custom draw.
+    *
+    * NOTE: Since CDIS_FOCUS is cleared during custom draw,
+    * it is necessary to draw the focus rectangle after the
+    * item has been drawn.
+    */
+    if (!ignoreCustomDraw && !ignoreDrawFocus && nmcd.left !is nmcd.right) {
+        if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
+            if (!explorerTheme && (style & DWT.FULL_SELECTION) !is 0) {
+                if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) is OS.CLR_NONE) {
+                    int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+                    if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) is 0) {
+//                      if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) {
+                        if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) is nmcd.dwItemSpec) {
+                            if (handle is OS.GetFocus ()) {
+                                int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+                                if ((uiState & OS.UISF_HIDEFOCUS) is 0) {
+                                    RECT rect = new RECT ();
+                                    rect.left = OS.LVIR_BOUNDS;
+                                    bool oldIgnore = ignoreCustomDraw;
+                                    ignoreCustomDraw = true;
+                                    OS.SendMessage (handle, OS. LVM_GETITEMRECT, nmcd.dwItemSpec, rect);
+                                    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                                    int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
+                                    RECT itemRect = new RECT ();
+                                    if (index is 0) {
+                                        itemRect.left = OS.LVIR_LABEL;
+                                        OS.SendMessage (handle, OS. LVM_GETITEMRECT, index, itemRect);
+                                    } else {
+                                        itemRect.top = index;
+                                        itemRect.left = OS.LVIR_ICON;
+                                        OS.SendMessage (handle, OS. LVM_GETSUBITEMRECT, nmcd.dwItemSpec, itemRect);
+                                    }
+                                    ignoreCustomDraw = oldIgnore;
+                                    rect.left = itemRect.left;
+                                    OS.DrawFocusRect (nmcd.hdc, rect);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return null;
+}
+
+LRESULT CDDS_ITEMPREPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    /*
+    * Bug in Windows.  When the table has the extended style
+    * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
+    * CLR_NONE to make the table transparent, Windows fills
+    * a black rectangle around any column that contains an
+    * image.  The fix is clear LVS_EX_FULLROWSELECT during
+    * custom draw.
+    *
+    * NOTE: It is also necessary to clear CDIS_FOCUS to stop
+    * the table from drawing the focus rectangle around the
+    * first item instead of the full row.
+    */
+    if (!ignoreCustomDraw) {
+        if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
+            if (!explorerTheme && (style & DWT.FULL_SELECTION) !is 0) {
+                if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) is OS.CLR_NONE) {
+                    int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+                    if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) is 0) {
+                        if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) {
+                            nmcd.uItemState &= ~OS.CDIS_FOCUS;
+                            OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if (explorerTheme && !ignoreCustomDraw) {
+        hotIndex = (nmcd.uItemState & OS.CDIS_HOT) !is 0 ? nmcd.dwItemSpec : -1;
+        if (hooks (DWT.EraseItem) && nmcd.left !is nmcd.right) {
+            OS.SaveDC (nmcd.hdc);
+            int hrgn = OS.CreateRectRgn (0, 0, 0, 0);
+            OS.SelectClipRgn (nmcd.hdc, hrgn);
+            OS.DeleteObject (hrgn);
+        }
+    }
+    return new LRESULT (OS.CDRF_NOTIFYSUBITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
+}
+
+LRESULT CDDS_POSTPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    if (ignoreCustomDraw) return null;
+    /*
+    * Bug in Windows.  When the table has the extended style
+    * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
+    * CLR_NONE to make the table transparent, Windows fills
+    * a black rectangle around any column that contains an
+    * image.  The fix is clear LVS_EX_FULLROWSELECT during
+    * custom draw.
+    */
+    if (--customCount is 0 && OS.IsWindowVisible (handle)) {
+        if (!explorerTheme && (style & DWT.FULL_SELECTION) !is 0) {
+            if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) is OS.CLR_NONE) {
+                int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+                if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) is 0) {
+                    int bits = OS.LVS_EX_FULLROWSELECT;
+                    if (OS.IsWinCE) {
+                        RECT rect = new RECT ();
+                        bool damaged = OS.GetUpdateRect (handle, rect, true);
+                        OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
+                        OS.ValidateRect (handle, null);
+                        if (damaged) OS.InvalidateRect (handle, rect, true);
+                    } else {
+                        int rgn = OS.CreateRectRgn (0, 0, 0, 0);
+                        int result = OS.GetUpdateRgn (handle, rgn, true);
+                        OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
+                        OS.ValidateRect (handle, null);
+                        if (result !is OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
+                        OS.DeleteObject (rgn);
+                    }
+                }
+            }
+        }
+    }
+    return null;
+}
+
+LRESULT CDDS_PREPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    if (ignoreCustomDraw) {
+        return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
+    }
+    /*
+    * Bug in Windows.  When the table has the extended style
+    * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
+    * CLR_NONE to make the table transparent, Windows fills
+    * a black rectangle around any column that contains an
+    * image.  The fix is clear LVS_EX_FULLROWSELECT during
+    * custom draw.
+    */
+    if (customCount++ is 0 && OS.IsWindowVisible (handle)) {
+        if (!explorerTheme && (style & DWT.FULL_SELECTION) !is 0) {
+            if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) is OS.CLR_NONE) {
+                int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+                if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) !is 0) {
+                    int bits = OS.LVS_EX_FULLROWSELECT;
+                    if (OS.IsWinCE) {
+                        RECT rect = new RECT ();
+                        bool damaged = OS.GetUpdateRect (handle, rect, true);
+                        OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
+                        OS.ValidateRect (handle, null);
+                        if (damaged) OS.InvalidateRect (handle, rect, true);
+                    } else {
+                        int rgn = OS.CreateRectRgn (0, 0, 0, 0);
+                        int result = OS.GetUpdateRgn (handle, rgn, true);
+                        OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
+                        OS.ValidateRect (handle, null);
+                        if (result !is OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
+                        OS.DeleteObject (rgn);
+                    }
+                }
+            }
+        }
+    }
+    if (OS.IsWindowVisible (handle)) {
+        bool draw = true;
+        /*
+        * Feature in Windows.  On Vista using the explorer theme,
+        * Windows draws a vertical line to separate columns.  When
+        * there is only a single column, the line looks strange.
+        * The fix is to draw the background using custom draw.
+        */
+        if (explorerTheme && columnCount is 0) {
+            int hDC = nmcd.hdc;
+            RECT rect = new RECT ();
+            OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+            if (OS.IsWindowEnabled (handle) || findImageControl () !is null) {
+                drawBackground (hDC, rect);
+            } else {
+                fillBackground (hDC, OS.GetSysColor (OS.COLOR_3DFACE), rect);
+            }
+            draw = false;
+        }
+        if (draw) {
+            Control control = findBackgroundControl ();
+            if (control !is null && control.backgroundImage !is null) {
+                RECT rect = new RECT ();
+                OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+                fillImageBackground (nmcd.hdc, control, rect);
+            } else {
+                if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) is OS.CLR_NONE) {
+                    if (OS.IsWindowEnabled (handle)) {
+                        RECT rect = new RECT ();
+                        OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+                        if (control is null) control = this;
+                        fillBackground (nmcd.hdc, control.getBackgroundPixel (), rect);
+                        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                            if (sortColumn !is null && sortDirection !is DWT.NONE) {
+                                int index = indexOf (sortColumn);
+                                if (index !is -1) {
+                                    parent.forceResize ();
+                                    int clrSortBk = getSortColumnPixel ();
+                                    RECT columnRect = new RECT (), headerRect = new RECT ();
+                                    OS.GetClientRect (handle, columnRect);
+                                    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                                    if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) !is 0) {
+                                        OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
+                                        columnRect.left = headerRect.left;
+                                        columnRect.right = headerRect.right;
+                                        if (OS.IntersectRect(columnRect, columnRect, rect)) {
+                                            fillBackground (nmcd.hdc, clrSortBk, columnRect);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
+}
+
+LRESULT CDDS_SUBITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    if (ignoreCustomDraw) return null;
+    if (nmcd.left is nmcd.right) return new LRESULT (OS.CDRF_DODEFAULT);
+    int hDC = nmcd.hdc;
+    if (ignoreDrawForeground) OS.RestoreDC (hDC, -1);
+    if (OS.IsWindowVisible (handle)) {
+        /*
+        * Feature in Windows.  When there is a sort column, the sort column
+        * color draws on top of the background color for an item.  The fix
+        * is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it
+        * in CDDS_SUBITEMPOSTPAINT.
+        */
+        if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) !is OS.CLR_NONE) {
+            if ((sortDirection & (DWT.UP | DWT.DOWN)) !is 0) {
+                if (sortColumn !is null && !sortColumn.isDisposed ()) {
+                    int oldColumn = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
+                    if (oldColumn is -1) {
+                        int newColumn = indexOf (sortColumn);
+                        int result = 0, rgn = 0;
+                        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                            rgn = OS.CreateRectRgn (0, 0, 0, 0);
+                            result = OS.GetUpdateRgn (handle, rgn, true);
+                        }
+                        OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, newColumn, 0);
+                        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                            OS.ValidateRect (handle, null);
+                            if (result !is OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
+                            OS.DeleteObject (rgn);
+                        }
+                    }
+                }
+            }
+        }
+        if (hooks (DWT.PaintItem)) {
+            TableItem item = _getItem (nmcd.dwItemSpec);
+            sendPaintItemEvent (item, nmcd);
+            //widget could be disposed at this point
+        }
+    }
+    return null;
+}
+
+LRESULT CDDS_SUBITEMPREPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    int hDC = nmcd.hdc;
+    if (explorerTheme && !ignoreCustomDraw && hooks (DWT.EraseItem) && (nmcd.left !is nmcd.right)) {
+        OS.RestoreDC (hDC, -1);
+    }
+    /*
+    * Feature in Windows.  When a new table item is inserted
+    * using LVM_INSERTITEM in a table that is transparent
+    * (ie. LVM_SETBKCOLOR has been called with CLR_NONE),
+    * TVM_INSERTITEM calls NM_CUSTOMDRAW before the new item
+    * has been added to the array.  The fix is to check for
+    * null.
+    */
+    TableItem item = _getItem (nmcd.dwItemSpec);
+    if (item is null) return null;
+    int hFont = item.cellFont !is null ? item.cellFont [nmcd.iSubItem] : -1;
+    if (hFont is -1) hFont = item.font;
+    if (hFont !is -1) OS.SelectObject (hDC, hFont);
+    if (ignoreCustomDraw || (nmcd.left is nmcd.right)) {
+        return new LRESULT (hFont is -1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT);
+    }
+    int code = OS.CDRF_DODEFAULT;
+    selectionForeground = -1;
+    ignoreDrawForeground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawBackground = false;
+    if (OS.IsWindowVisible (handle)) {
+        if (hooks (DWT.MeasureItem)) {
+            sendMeasureItemEvent (item, nmcd.dwItemSpec, nmcd.iSubItem, nmcd.hdc);
+            if (isDisposed () || item.isDisposed ()) return null;
+        }
+        if (hooks (DWT.EraseItem)) {
+            sendEraseItemEvent (item, nmcd, lParam);
+            if (isDisposed () || item.isDisposed ()) return null;
+            code |= OS.CDRF_NOTIFYPOSTPAINT;
+        }
+        if (ignoreDrawForeground || hooks (DWT.PaintItem)) code |= OS.CDRF_NOTIFYPOSTPAINT;
+    }
+    int clrText = item.cellForeground !is null ? item.cellForeground [nmcd.iSubItem] : -1;
+    if (clrText is -1) clrText = item.foreground;
+    int clrTextBk = item.cellBackground !is null ? item.cellBackground [nmcd.iSubItem] : -1;
+    if (clrTextBk is -1) clrTextBk = item.background;
+    if (selectionForeground !is -1) clrText = selectionForeground;
+    /*
+    * Bug in Windows.  When the table has the extended style
+    * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
+    * CLR_NONE to make the table transparent, Windows draws
+    * a black rectangle around any column that contains an
+    * image.  The fix is emulate LVS_EX_FULLROWSELECT by
+    * drawing the selection.
+    */
+    if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
+        if (!explorerTheme && !ignoreDrawSelection && (style & DWT.FULL_SELECTION) !is 0) {
+            int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+            if ((bits & OS.LVS_EX_FULLROWSELECT) is 0) {
+                /*
+                * Bug in Windows.  For some reason, CDIS_SELECTED always set,
+                * even for items that are not selected.  The fix is to get
+                * the selection state from the item.
+                */
+                LVITEM lvItem = new LVITEM ();
+                lvItem.mask = OS.LVIF_STATE;
+                lvItem.stateMask = OS.LVIS_SELECTED;
+                lvItem.iItem = nmcd.dwItemSpec;
+                int result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
+                if ((result !is 0 && (lvItem.state & OS.LVIS_SELECTED) !is 0)) {
+                    int clrSelection = -1;
+                    if (nmcd.iSubItem is 0) {
+                        if (OS.GetFocus () is handle) {
+                            clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
+                        } else {
+                            if ((style & DWT.HIDE_SELECTION) is 0) {
+                                clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
+                            }
+                        }
+                    } else {
+                        if (OS.GetFocus () is handle) {
+                            clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
+                            clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
+                        } else {
+                            if ((style & DWT.HIDE_SELECTION) is 0) {
+                                clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
+                            }
+                        }
+                    }
+                    if (clrSelection !is -1) {
+                        RECT rect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, nmcd.iSubItem !is 0, true, false, hDC);
+                        fillBackground (hDC, clrSelection, rect);
+                    }
+                }
+            }
+        }
+    }
+    if (!ignoreDrawForeground) {
+        /*
+        * Bug in Windows.  When the attributes are for one cell in a table,
+        * Windows does not reset them for the next cell.  As a result, all
+        * subsequent cells are drawn using the previous font, foreground and
+        * background colors.  The fix is to set the all attributes when any
+        * attribute could have changed.
+        */
+        bool hasAttributes = true;
+        if (hFont is -1 && clrText is -1 && clrTextBk is -1) {
+            if (item.cellForeground is null && item.cellBackground is null && item.cellFont is null) {
+                int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                if (count is 1) hasAttributes = false;
+            }
+        }
+        if (hasAttributes) {
+            if (hFont is -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+            OS.SelectObject (hDC, hFont);
+            if (OS.IsWindowEnabled (handle)) {
+                nmcd.clrText = clrText is -1 ? getForegroundPixel () : clrText;
+                if (clrTextBk is -1) {
+                    nmcd.clrTextBk = OS.CLR_NONE;
+                    if (selectionForeground is -1) {
+                        Control control = findBackgroundControl ();
+                        if (control is null) control = this;
+                        if (control.backgroundImage is null) {
+                            if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) !is OS.CLR_NONE) {
+                                nmcd.clrTextBk = control.getBackgroundPixel ();
+                            }
+                        }
+                    }
+                } else {
+                    nmcd.clrTextBk = selectionForeground !is -1 ? OS.CLR_NONE : clrTextBk;
+                }
+                OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
+            }
+            code |= OS.CDRF_NEWFONT;
+        }
+    }
+    if (OS.IsWindowEnabled (handle)) {
+        /*
+        * Feature in Windows.  When there is a sort column, the sort column
+        * color draws on top of the background color for an item.  The fix
+        * is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it
+        * in CDDS_SUBITEMPOSTPAINT.
+        */
+        if (clrTextBk !is -1) {
+            int oldColumn = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
+            if (oldColumn !is -1 && oldColumn is nmcd.iSubItem) {
+                int result = 0, rgn = 0;
+                if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                    rgn = OS.CreateRectRgn (0, 0, 0, 0);
+                    result = OS.GetUpdateRgn (handle, rgn, true);
+                }
+                OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
+                if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                    OS.ValidateRect (handle, null);
+                    if (result !is OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
+                    OS.DeleteObject (rgn);
+                }
+                code |= OS.CDRF_NOTIFYPOSTPAINT;
+            }
+        }
+    } else {
+        /*
+        * Feature in Windows.  When the table is disabled, it draws
+        * with a gray background but does not gray the text.  The fix
+        * is to explicitly gray the text.
+        */
+        nmcd.clrText = OS.GetSysColor (OS.COLOR_GRAYTEXT);
+        if (findImageControl () !is null) {
+            nmcd.clrTextBk = OS.CLR_NONE;
+        } else {
+            nmcd.clrTextBk = OS.GetSysColor (OS.COLOR_3DFACE);
+        }
+        nmcd.uItemState &= ~OS.CDIS_SELECTED;
+        OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
+        code |= OS.CDRF_NEWFONT;
+    }
+    return new LRESULT (code);
+}
+
+void checkBuffered () {
+    super.checkBuffered ();
+    if (OS.COMCTL32_MAJOR >= 6) style |= DWT.DOUBLE_BUFFERED;
+    if ((style & DWT.VIRTUAL) !is 0) style |= DWT.DOUBLE_BUFFERED;
+}
+
+bool checkData (TableItem item, bool redraw) {
+    if ((style & DWT.VIRTUAL) is 0) return true;
+    return checkData (item, indexOf (item), redraw);
+}
+
+bool checkData (TableItem item, int index, bool redraw) {
+    if ((style & DWT.VIRTUAL) is 0) return true;
+    if (!item.cached) {
+        item.cached = true;
+        Event event = new Event ();
+        event.item = item;
+        event.index = index;
+        currentItem = item;
+        sendEvent (DWT.SetData, event);
+        //widget could be disposed at this point
+        currentItem = null;
+        if (isDisposed () || item.isDisposed ()) return false;
+        if (redraw) {
+            if (!setScrollWidth (item, false)) {
+                item.redraw ();
+            }
+        }
+    }
+    return true;
+}
+
+bool checkHandle (int hwnd) {
+    if (hwnd is handle) return true;
+    return hwnd is OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+/**
+ * Clears the item at the given zero-relative index in the receiver.
+ * The text, icon and other attributes of the item are set to the default
+ * value.  If the table was created with the <code>DWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param index the index of the item to clear
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT#VIRTUAL
+ * @see DWT#SetData
+ *
+ * @since 3.0
+ */
+public void clear (int index) {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= index && index < count)) error (DWT.ERROR_INVALID_RANGE);
+    TableItem item = items [index];
+    if (item !is null) {
+        if (item !is currentItem) item.clear ();
+        /*
+        * Bug in Windows.  Despite the fact that every item in the
+        * table always has LPSTR_TEXTCALLBACK, Windows caches the
+        * bounds for the selected items.  This means that
+        * when you change the string to be something else, Windows
+        * correctly asks you for the new string but when the item
+        * is selected, the selection draws using the bounds of the
+        * previous item.  The fix is to reset LPSTR_TEXTCALLBACK
+        * even though it has not changed, causing Windows to flush
+        * cached bounds.
+        */
+        if ((style & DWT.VIRTUAL) is 0 && item.cached) {
+            LVITEM lvItem = new LVITEM ();
+            lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
+            lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+            lvItem.iItem = index;
+            OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
+            item.cached = false;
+        }
+        if (currentItem is null && drawCount is 0 && OS.IsWindowVisible (handle)) {
+            OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index);
+        }
+        setScrollWidth (item, false);
+    }
+}
+
+/**
+ * Removes the items from the receiver which are between the given
+ * zero-relative start and end indices (inclusive).  The text, icon
+ * and other attributes of the items are set to their default values.
+ * If the table was created with the <code>DWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param start the start index of the item to clear
+ * @param end the end index of the item to clear
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT#VIRTUAL
+ * @see DWT#SetData
+ *
+ * @since 3.0
+ */
+public void clear (int start, int end) {
+    checkWidget ();
+    if (start > end) return;
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= start && start <= end && end < count)) {
+        error (DWT.ERROR_INVALID_RANGE);
+    }
+    if (start is 0 && end is count - 1) {
+        clearAll ();
+    } else {
+        LVITEM lvItem = null;
+        bool cleared = false;
+        for (int i=start; i<=end; i++) {
+            TableItem item = items [i];
+            if (item !is null) {
+                if (item !is currentItem) {
+                    cleared = true;
+                    item.clear ();
+                }
+                /*
+                * Bug in Windows.  Despite the fact that every item in the
+                * table always has LPSTR_TEXTCALLBACK, Windows caches the
+                * bounds for the selected items.  This means that
+                * when you change the string to be something else, Windows
+                * correctly asks you for the new string but when the item
+                * is selected, the selection draws using the bounds of the
+                * previous item.  The fix is to reset LPSTR_TEXTCALLBACK
+                * even though it has not changed, causing Windows to flush
+                * cached bounds.
+                */
+                if ((style & DWT.VIRTUAL) is 0 && item.cached) {
+                    if (lvItem is null) {
+                        lvItem = new LVITEM ();
+                        lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
+                        lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+                    }
+                    lvItem.iItem = i;
+                    OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
+                    item.cached = false;
+                }
+            }
+        }
+        if (cleared) {
+            if (currentItem is null && drawCount is 0 && OS.IsWindowVisible (handle)) {
+                OS.SendMessage (handle, OS.LVM_REDRAWITEMS, start, end);
+            }
+            TableItem item = start is end ? items [start] : null;
+            setScrollWidth (item, false);
+        }
+    }
+}
+
+/**
+ * Clears the items at the given zero-relative indices in the receiver.
+ * The text, icon and other attributes of the items are set to their default
+ * values.  If the table was created with the <code>DWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param indices the array of indices of the items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT#VIRTUAL
+ * @see DWT#SetData
+ *
+ * @since 3.0
+ */
+public void clear (int [] indices) {
+    checkWidget ();
+    if (indices is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (indices.length is 0) return;
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<indices.length; i++) {
+        if (!(0 <= indices [i] && indices [i] < count)) {
+            error (DWT.ERROR_INVALID_RANGE);
+        }
+    }
+    LVITEM lvItem = null;
+    bool cleared = false;
+    for (int i=0; i<indices.length; i++) {
+        int index = indices [i];
+        TableItem item = items [index];
+        if (item !is null) {
+            if (item !is currentItem) {
+                cleared = true;
+                item.clear ();
+            }
+            /*
+            * Bug in Windows.  Despite the fact that every item in the
+            * table always has LPSTR_TEXTCALLBACK, Windows caches the
+            * bounds for the selected items.  This means that
+            * when you change the string to be something else, Windows
+            * correctly asks you for the new string but when the item
+            * is selected, the selection draws using the bounds of the
+            * previous item.  The fix is to reset LPSTR_TEXTCALLBACK
+            * even though it has not changed, causing Windows to flush
+            * cached bounds.
+            */
+            if ((style & DWT.VIRTUAL) is 0 && item.cached) {
+                if (lvItem is null) {
+                    lvItem = new LVITEM ();
+                    lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
+                    lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+                }
+                lvItem.iItem = i;
+                OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
+                item.cached = false;
+            }
+            if (currentItem is null && drawCount is 0 && OS.IsWindowVisible (handle)) {
+                OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index);
+            }
+        }
+    }
+    if (cleared) setScrollWidth (null, false);
+}
+
+/**
+ * Clears all the items in the receiver. The text, icon and other
+ * attributes of the items are set to their default values. If the
+ * table was created with the <code>DWT.VIRTUAL</code> style, these
+ * attributes are requested again as needed.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT#VIRTUAL
+ * @see DWT#SetData
+ *
+ * @since 3.0
+ */
+public void clearAll () {
+    checkWidget ();
+    LVITEM lvItem = null;
+    bool cleared = false;
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<count; i++) {
+        TableItem item = items [i];
+        if (item !is null) {
+            if (item !is currentItem) {
+                cleared = true;
+                item.clear ();
+            }
+            /*
+            * Bug in Windows.  Despite the fact that every item in the
+            * table always has LPSTR_TEXTCALLBACK, Windows caches the
+            * bounds for the selected items.  This means that
+            * when you change the string to be something else, Windows
+            * correctly asks you for the new string but when the item
+            * is selected, the selection draws using the bounds of the
+            * previous item.  The fix is to reset LPSTR_TEXTCALLBACK
+            * even though it has not changed, causing Windows to flush
+            * cached bounds.
+            */
+            if ((style & DWT.VIRTUAL) is 0 && item.cached) {
+                if (lvItem is null) {
+                    lvItem = new LVITEM ();
+                    lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
+                    lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+                }
+                lvItem.iItem = i;
+                OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
+                item.cached = false;
+            }
+        }
+    }
+    if (cleared) {
+        if (currentItem is null && drawCount is 0 && OS.IsWindowVisible (handle)) {
+            OS.SendMessage (handle, OS.LVM_REDRAWITEMS, 0, count - 1);
+        }
+        setScrollWidth (null, false);
+    }
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    if (fixScrollWidth) setScrollWidth (null, true);
+    //This code is intentionally commented
+//  if (itemHeight is -1 && hooks (DWT.MeasureItem)) {
+//      int i = 0;
+//      TableItem item = items [i];
+//      if (item !is null) {
+//          int hDC = OS.GetDC (handle);
+//          int oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+//          if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+//          int index = 0, count = Math.max (1, columnCount);
+//          while (index < count) {
+//              int hFont = item.cellFont !is null ? item.cellFont [index] : -1;
+//              if (hFont is -1) hFont = item.font;
+//              if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+//              sendMeasureItemEvent (item, i, index, hDC);
+//              if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+//              if (isDisposed () || item.isDisposed ()) break;
+//              index++;
+//          }
+//          if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+//          OS.ReleaseDC (handle, hDC);
+//      }
+//  }
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    RECT rect = new RECT ();
+    OS.GetWindowRect (hwndHeader, rect);
+    int height = rect.bottom - rect.top;
+    int bits = 0;
+    if (wHint !is DWT.DEFAULT) {
+        bits |= wHint & 0xFFFF;
+    } else {
+        int width = 0;
+        int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+        for (int i=0; i<count; i++) {
+            width += OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, i, 0);
+        }
+        bits |= width & 0xFFFF;
+    }
+    int result = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, -1, bits | 0xFFFF0000);
+    int width = result & 0xFFFF;
+    int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+    int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+    int itemHeight = (oneItem >> 16) - (empty >> 16);
+    height += OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0) * itemHeight;
+    if (width is 0) width = DEFAULT_WIDTH;
+    if (height is 0) height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    int border = getBorderWidth ();
+    width += border * 2;  height += border * 2;
+    if ((style & DWT.V_SCROLL) !is 0) {
+        width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+    }
+    if ((style & DWT.H_SCROLL) !is 0) {
+        height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    }
+    return new Point (width, height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state &= ~(CANVAS | THEME_BACKGROUND);
+
+    /* Use the Explorer theme */
+    if (EXPLORER_THEME) {
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+            explorerTheme = true;
+            OS.SetWindowTheme (handle, Display.EXPLORER, null);
+        }
+    }
+
+    /* Get the header window proc */
+    if (HeaderProc is 0) {
+        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+        HeaderProc = OS.GetWindowLong (hwndHeader, OS.GWL_WNDPROC);
+    }
+
+    /*
+    * Feature in Windows.  In version 5.8 of COMCTL32.DLL,
+    * if the font is changed for an item, the bounds for the
+    * item are not updated, causing the text to be clipped.
+    * The fix is to detect the version of COMCTL32.DLL, and
+    * if it is one of the versions with the problem, then
+    * use version 5.00 of the control (a version that does
+    * not have the problem).  This is the recommended work
+    * around from the MSDN.
+    */
+    if (!OS.IsWinCE) {
+        if (OS.COMCTL32_MAJOR < 6) {
+            OS.SendMessage (handle, OS.CCM_SETVERSION, 5, 0);
+        }
+    }
+
+    /*
+    * This code is intentionally commented.  According to
+    * the documentation, setting the default item size is
+    * supposed to improve performance.  By experimentation,
+    * this does not seem to have much of an effect.
+    */
+//  OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, 1024 * 2, 0);
+
+    /* Set the checkbox image list */
+    if ((style & DWT.CHECK) !is 0) {
+        int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+        int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+        int width = (oneItem >> 16) - (empty >> 16), height = width;
+        setCheckboxImageList (width, height, false);
+        OS.SendMessage (handle, OS. LVM_SETCALLBACKMASK, OS.LVIS_STATEIMAGEMASK, 0);
+    }
+
+    /*
+    * Feature in Windows.  When the control is created,
+    * it does not use the default system font.  A new HFONT
+    * is created and destroyed when the control is destroyed.
+    * This means that a program that queries the font from
+    * this control, uses the font in another control and then
+    * destroys this control will have the font unexpectedly
+    * destroyed in the other control.  The fix is to assign
+    * the font ourselves each time the control is created.
+    * The control will not destroy a font that it did not
+    * create.
+    */
+    int hFont = OS.GetStockObject (OS.SYSTEM_FONT);
+    OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
+
+    /*
+    * Bug in Windows.  When the first column is inserted
+    * without setting the header text, Windows will never
+    * allow the header text for the first column to be set.
+    * The fix is to set the text to an empty string when
+    * the column is inserted.
+    */
+    LVCOLUMN lvColumn = new LVCOLUMN ();
+    lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_WIDTH;
+    int hHeap = OS.GetProcessHeap ();
+    int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
+    lvColumn.pszText = pszText;
+    OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, 0, lvColumn);
+    OS.HeapFree (hHeap, 0, pszText);
+
+    /* Set the extended style bits */
+    int bits1 = OS.LVS_EX_LABELTIP;
+    if ((style & DWT.FULL_SELECTION) !is 0) bits1 |= OS.LVS_EX_FULLROWSELECT;
+    if (OS.COMCTL32_MAJOR >= 6) bits1 |= OS.LVS_EX_DOUBLEBUFFER;
+    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits1, bits1);
+
+    /*
+    * Feature in Windows.  Windows does not explicitly set the orientation of
+    * the header.  Instead, the orientation is inherited when WS_EX_LAYOUTRTL
+    * is specified for the table.  This means that when both WS_EX_LAYOUTRTL
+    * and WS_EX_NOINHERITLAYOUT are specified for the table, the header will
+    * not be oriented correctly.  The fix is to explicitly set the orientation
+    * for the header.
+    *
+    * NOTE: WS_EX_LAYOUTRTL is not supported on Windows NT.
+    */
+    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return;
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+        int bits2 = OS.GetWindowLong (hwndHeader, OS.GWL_EXSTYLE);
+        OS.SetWindowLong (hwndHeader, OS.GWL_EXSTYLE, bits2 | OS.WS_EX_LAYOUTRTL);
+    }
+}
+
+void createHeaderToolTips () {
+    if (OS.IsWinCE) return;
+    if (headerToolTipHandle !is 0) return;
+    headerToolTipHandle = OS.CreateWindowEx (
+        0,
+        new TCHAR (0, OS.TOOLTIPS_CLASS, true),
+        null,
+        0,
+        OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
+        handle,
+        0,
+        OS.GetModuleHandle (null),
+        null);
+    if (headerToolTipHandle is 0) error (DWT.ERROR_NO_HANDLES);
+    /*
+    * Feature in Windows.  Despite the fact that the
+    * tool tip text contains \r\n, the tooltip will
+    * not honour the new line unless TTM_SETMAXTIPWIDTH
+    * is set.  The fix is to set TTM_SETMAXTIPWIDTH to
+    * a large value.
+    */
+    OS.SendMessage (headerToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
+}
+
+void createItem (TableColumn column, int index) {
+    if (!(0 <= index && index <= columnCount)) error (DWT.ERROR_INVALID_RANGE);
+    int oldColumn = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
+    if (oldColumn >= index) {
+        OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn + 1, 0);
+    }
+    if (columnCount is columns.length) {
+        TableColumn [] newColumns = new TableColumn [columns.length + 4];
+        System.arraycopy (columns, 0, newColumns, 0, columns.length);
+        columns = newColumns;
+    }
+    int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<itemCount; i++) {
+        TableItem item = items [i];
+        if (item !is null) {
+            String [] strings = item.strings;
+            if (strings !is null) {
+                String [] temp = new String [columnCount + 1];
+                System.arraycopy (strings, 0, temp, 0, index);
+                System.arraycopy (strings, index, temp, index + 1, columnCount -  index);
+                item.strings = temp;
+            }
+            Image [] images = item.images;
+            if (images !is null) {
+                Image [] temp = new Image [columnCount + 1];
+                System.arraycopy (images, 0, temp, 0, index);
+                System.arraycopy (images, index, temp, index + 1, columnCount - index);
+                item.images = temp;
+            }
+            if (index is 0) {
+                if (columnCount !is 0) {
+                    if (strings is null) {
+                        item.strings = new String [columnCount + 1];
+                        item.strings [1] = item.text;
+                    }
+                    item.text = ""; //$NON-NLS-1$
+                    if (images is null) {
+                        item.images = new Image [columnCount + 1];
+                        item.images [1] = item.image;
+                    }
+                    item.image = null;
+                }
+            }
+            if (item.cellBackground !is null) {
+                int [] cellBackground = item.cellBackground;
+                int [] temp = new int [columnCount + 1];
+                System.arraycopy (cellBackground, 0, temp, 0, index);
+                System.arraycopy (cellBackground, index, temp, index + 1, columnCount - index);
+                temp [index] = -1;
+                item.cellBackground = temp;
+            }
+            if (item.cellForeground !is null) {
+                int [] cellForeground = item.cellForeground;
+                int [] temp = new int [columnCount + 1];
+                System.arraycopy (cellForeground, 0, temp, 0, index);
+                System.arraycopy (cellForeground, index, temp, index + 1, columnCount - index);
+                temp [index] = -1;
+                item.cellForeground = temp;
+            }
+            if (item.cellFont !is null) {
+                int [] cellFont = item.cellFont;
+                int [] temp = new int [columnCount + 1];
+                System.arraycopy (cellFont, 0, temp, 0, index);
+                System.arraycopy (cellFont, index, temp, index + 1, columnCount - index);
+                temp [index] = -1;
+                item.cellFont = temp;
+            }
+        }
+    }
+    /*
+    * Insert the column into the columns array before inserting
+    * it into the widget so that the column will be present when
+    * any callbacks are issued as a result of LVM_INSERTCOLUMN
+    * or LVM_SETCOLUMN.
+    */
+    System.arraycopy (columns, index, columns, index + 1, columnCount++ - index);
+    columns [index] = column;
+
+    /*
+    * Ensure that resize listeners for the table and for columns
+    * within the table are not called.  This can happen when the
+    * first column is inserted into a table or when a new column
+    * is inserted in the first position.
+    */
+    ignoreColumnResize = true;
+    if (index is 0) {
+        if (columnCount > 1) {
+            LVCOLUMN lvColumn = new LVCOLUMN ();
+            lvColumn.mask = OS.LVCF_WIDTH;
+            OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, 1, lvColumn);
+            OS.SendMessage (handle, OS.LVM_GETCOLUMN, 1, lvColumn);
+            int width = lvColumn.cx;
+            int cchTextMax = 1024;
+            int hHeap = OS.GetProcessHeap ();
+            int byteCount = cchTextMax * TCHAR.sizeof;
+            int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+            lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
+            lvColumn.pszText = pszText;
+            lvColumn.cchTextMax = cchTextMax;
+            OS.SendMessage (handle, OS.LVM_GETCOLUMN, 0, lvColumn);
+            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 1, lvColumn);
+            lvColumn.fmt = OS.LVCFMT_IMAGE;
+            lvColumn.cx = width;
+            lvColumn.iImage = OS.I_IMAGENONE;
+            lvColumn.pszText = lvColumn.cchTextMax = 0;
+            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
+            lvColumn.mask = OS.LVCF_FMT;
+            lvColumn.fmt = OS.LVCFMT_LEFT;
+            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
+            if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+        } else {
+            OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0);
+        }
+        if ((parent.style & DWT.VIRTUAL) is 0) {
+            LVITEM lvItem = new LVITEM ();
+            lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
+            lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+            lvItem.iImage = OS.I_IMAGECALLBACK;
+            for (int i=0; i<itemCount; i++) {
+                lvItem.iItem = i;
+                OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
+            }
+        }
+    } else {
+        int fmt = OS.LVCFMT_LEFT;
+        if ((column.style & DWT.CENTER) is DWT.CENTER) fmt = OS.LVCFMT_CENTER;
+        if ((column.style & DWT.RIGHT) is DWT.RIGHT) fmt = OS.LVCFMT_RIGHT;
+        LVCOLUMN lvColumn = new LVCOLUMN ();
+        lvColumn.mask = OS.LVCF_WIDTH | OS.LVCF_FMT;
+        lvColumn.fmt = fmt;
+        OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, index, lvColumn);
+    }
+    ignoreColumnResize = false;
+
+    /* Add the tool tip item for the header */
+    if (headerToolTipHandle !is 0) {
+        RECT rect = new RECT ();
+        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) !is 0) {
+            TOOLINFO lpti = new TOOLINFO ();
+            lpti.cbSize = TOOLINFO.sizeof;
+            lpti.uFlags = OS.TTF_SUBCLASS;
+            lpti.hwnd = hwndHeader;
+            lpti.uId = column.id = display.nextToolTipId++;
+            lpti.left = rect.left;
+            lpti.top = rect.top;
+            lpti.right = rect.right;
+            lpti.bottom = rect.bottom;
+            lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
+            OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
+        }
+    }
+}
+
+void createItem (TableItem item, int index) {
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= index && index <= count)) error (DWT.ERROR_INVALID_RANGE);
+    if (count is items.length) {
+        /*
+        * Grow the array faster when redraw is off or the
+        * table is not visible.  When the table is painted,
+        * the items array is resized to be smaller to reduce
+        * memory usage.
+        */
+        bool small = drawCount is 0 && OS.IsWindowVisible (handle);
+        int length = small ? items.length + 4 : Math.max (4, items.length * 3 / 2);
+        TableItem [] newItems = new TableItem [length];
+        System.arraycopy (items, 0, newItems, 0, items.length);
+        items = newItems;
+    }
+    LVITEM lvItem = new LVITEM ();
+    lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
+    lvItem.iItem = index;
+    lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+    /*
+    * Bug in Windows.  Despite the fact that the image list
+    * index has never been set for the item, Windows always
+    * assumes that the image index for the item is valid.
+    * When an item is inserted, the image index is zero.
+    * Therefore, when the first image is inserted and is
+    * assigned image index zero, every item draws with this
+    * image.  The fix is to set the image index when the
+    * the item is created.
+    */
+    lvItem.iImage = OS.I_IMAGECALLBACK;
+
+    /* Insert the item */
+    setDeferResize (true);
+    ignoreSelect = ignoreShrink = true;
+    int result = OS.SendMessage (handle, OS.LVM_INSERTITEM, 0, lvItem);
+    ignoreSelect = ignoreShrink = false;
+    if (result is -1) error (DWT.ERROR_ITEM_NOT_ADDED);
+    System.arraycopy (items, index, items, index + 1, count - index);
+    items [index] = item;
+    setDeferResize (false);
+
+    /* Resize to show the first item */
+    if (count is 0) setScrollWidth (item, false);
+}
+
+void createWidget () {
+    super.createWidget ();
+    itemHeight = hotIndex = -1;
+    items = new TableItem [4];
+    columns = new TableColumn [4];
+}
+
+int defaultBackground () {
+    return OS.GetSysColor (OS.COLOR_WINDOW);
+}
+
+void deregister () {
+    super.deregister ();
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    if (hwndHeader !is 0) display.removeControl (hwndHeader);
+}
+
+/**
+ * Deselects the items at the given zero-relative indices in the receiver.
+ * If the item at the given zero-relative index in the receiver
+ * is selected, it is deselected.  If the item at the index
+ * was not selected, it remains deselected. Indices that are out
+ * of range and duplicate indices are ignored.
+ *
+ * @param indices the array of indices for the items to deselect
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int [] indices) {
+    checkWidget ();
+    if (indices is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (indices.length is 0) return;
+    LVITEM lvItem = new LVITEM ();
+    lvItem.stateMask = OS.LVIS_SELECTED;
+    for (int i=0; i<indices.length; i++) {
+        /*
+        * An index of -1 will apply the change to all
+        * items.  Ensure that indices are greater than -1.
+        */
+        if (indices [i] >= 0) {
+            ignoreSelect = true;
+            OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem);
+            ignoreSelect = false;
+        }
+    }
+}
+
+/**
+ * Deselects the item at the given zero-relative index in the receiver.
+ * If the item at the index was already deselected, it remains
+ * deselected. Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to deselect
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int index) {
+    checkWidget ();
+    /*
+    * An index of -1 will apply the change to all
+    * items.  Ensure that index is greater than -1.
+    */
+    if (index < 0) return;
+    LVITEM lvItem = new LVITEM ();
+    lvItem.stateMask = OS.LVIS_SELECTED;
+    ignoreSelect = true;
+    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
+    ignoreSelect = false;
+}
+
+/**
+ * Deselects the items at the given zero-relative indices in the receiver.
+ * If the item at the given zero-relative index in the receiver
+ * is selected, it is deselected.  If the item at the index
+ * was not selected, it remains deselected.  The range of the
+ * indices is inclusive. Indices that are out of range are ignored.
+ *
+ * @param start the start index of the items to deselect
+ * @param end the end index of the items to deselect
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int start, int end) {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (start is 0 && end is count - 1) {
+        deselectAll ();
+    } else {
+        LVITEM lvItem = new LVITEM ();
+        lvItem.stateMask = OS.LVIS_SELECTED;
+        /*
+        * An index of -1 will apply the change to all
+        * items.  Ensure that indices are greater than -1.
+        */
+        start = Math.max (0, start);
+        for (int i=start; i<=end; i++) {
+            ignoreSelect = true;
+            OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem);
+            ignoreSelect = false;
+        }
+    }
+}
+
+/**
+ * Deselects all selected items in the receiver.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselectAll () {
+    checkWidget ();
+    LVITEM lvItem = new LVITEM ();
+    lvItem.mask = OS.LVIF_STATE;
+    lvItem.stateMask = OS.LVIS_SELECTED;
+    ignoreSelect = true;
+    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem);
+    ignoreSelect = false;
+}
+
+void destroyItem (TableColumn column) {
+    int index = 0;
+    while (index < columnCount) {
+        if (columns [index] is column) break;
+        index++;
+    }
+    int oldColumn = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
+    if (oldColumn is index) {
+        OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
+    } else {
+        if (oldColumn > index) {
+            OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn - 1, 0);
+        }
+    }
+    int orderIndex = 0;
+    int [] oldOrder = new int [columnCount];
+    OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
+    while (orderIndex < columnCount) {
+        if (oldOrder [orderIndex] is index) break;
+        orderIndex++;
+    }
+    ignoreColumnResize = true;
+    bool first = false;
+    if (index is 0) {
+        first = true;
+        if (columnCount > 1) {
+            index = 1;
+            int cchTextMax = 1024;
+            int hHeap = OS.GetProcessHeap ();
+            int byteCount = cchTextMax * TCHAR.sizeof;
+            int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+            LVCOLUMN lvColumn = new LVCOLUMN ();
+            lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
+            lvColumn.pszText = pszText;
+            lvColumn.cchTextMax = cchTextMax;
+            OS.SendMessage (handle, OS.LVM_GETCOLUMN, 1, lvColumn);
+            lvColumn.fmt &= ~(OS.LVCFMT_CENTER | OS.LVCFMT_RIGHT);
+            lvColumn.fmt |= OS.LVCFMT_LEFT;
+            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
+            if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+        } else {
+            int hHeap = OS.GetProcessHeap ();
+            int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
+            LVCOLUMN lvColumn = new LVCOLUMN ();
+            lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
+            lvColumn.pszText = pszText;
+            lvColumn.iImage = OS.I_IMAGENONE;
+            lvColumn.fmt = OS.LVCFMT_LEFT;
+            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
+            if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+            if (OS.COMCTL32_MAJOR >= 6) {
+                HDITEM hdItem = new HDITEM ();
+                hdItem.mask = OS.HDI_FORMAT;
+                hdItem.fmt = OS.HDF_LEFT;
+                int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
+            }
+        }
+        if ((parent.style & DWT.VIRTUAL) is 0) {
+            LVITEM lvItem = new LVITEM ();
+            lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
+            lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+            lvItem.iImage = OS.I_IMAGECALLBACK;
+            int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+            for (int i=0; i<itemCount; i++) {
+                lvItem.iItem = i;
+                OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
+            }
+        }
+    }
+    if (columnCount > 1) {
+        if (OS.SendMessage (handle, OS.LVM_DELETECOLUMN, index, 0) is 0) {
+            error (DWT.ERROR_ITEM_NOT_REMOVED);
+        }
+    }
+    if (first) index = 0;
+    System.arraycopy (columns, index + 1, columns, index, --columnCount - index);
+    columns [columnCount] = null;
+    int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<itemCount; i++) {
+        TableItem item = items [i];
+        if (item !is null) {
+            if (columnCount is 0) {
+                item.strings = null;
+                item.images = null;
+                item.cellBackground = null;
+                item.cellForeground = null;
+                item.cellFont = null;
+            } else {
+                if (item.strings !is null) {
+                    String [] strings = item.strings;
+                    if (index is 0) {
+                        item.text = strings [1] !is null ? strings [1] : ""; //$NON-NLS-1$
+                    }
+                    String [] temp = new String [columnCount];
+                    System.arraycopy (strings, 0, temp, 0, index);
+                    System.arraycopy (strings, index + 1, temp, index, columnCount - index);
+                    item.strings = temp;
+                } else {
+                    if (index is 0) item.text = ""; //$NON-NLS-1$
+                }
+                if (item.images !is null) {
+                    Image [] images = item.images;
+                    if (index is 0) item.image = images [1];
+                    Image [] temp = new Image [columnCount];
+                    System.arraycopy (images, 0, temp, 0, index);
+                    System.arraycopy (images, index + 1, temp, index, columnCount - index);
+                    item.images = temp;
+                } else {
+                    if (index is 0) item.image = null;
+                }
+                if (item.cellBackground !is null) {
+                    int [] cellBackground = item.cellBackground;
+                    int [] temp = new int [columnCount];
+                    System.arraycopy (cellBackground, 0, temp, 0, index);
+                    System.arraycopy (cellBackground, index + 1, temp, index, columnCount - index);
+                    item.cellBackground = temp;
+                }
+                if (item.cellForeground !is null) {
+                    int [] cellForeground = item.cellForeground;
+                    int [] temp = new int [columnCount];
+                    System.arraycopy (cellForeground, 0, temp, 0, index);
+                    System.arraycopy (cellForeground, index + 1, temp, index, columnCount - index);
+                    item.cellForeground = temp;
+                }
+                if (item.cellFont !is null) {
+                    int [] cellFont = item.cellFont;
+                    int [] temp = new int [columnCount];
+                    System.arraycopy (cellFont, 0, temp, 0, index);
+                    System.arraycopy (cellFont, index + 1, temp, index, columnCount - index);
+                    item.cellFont = temp;
+                }
+            }
+        }
+    }
+    if (columnCount is 0) setScrollWidth (null, true);
+    updateMoveable ();
+    ignoreColumnResize = false;
+    if (columnCount !is 0) {
+        /*
+        * Bug in Windows.  When LVM_DELETECOLUMN is used to delete a
+        * column zero when that column is both the first column in the
+        * table and the first column in the column order array, Windows
+        * incorrectly computes the new column order.  For example, both
+        * the orders {0, 3, 1, 2} and {0, 3, 2, 1} give a new column
+        * order of {0, 2, 1}, while {0, 2, 1, 3} gives {0, 1, 2, 3}.
+        * The fix is to compute the new order and compare it with the
+        * order that Windows is using.  If the two differ, the new order
+        * is used.
+        */
+        int count = 0;
+        int oldIndex = oldOrder [orderIndex];
+        int [] newOrder = new int [columnCount];
+        for (int i=0; i<oldOrder.length; i++) {
+            if (oldOrder [i] !is oldIndex) {
+                int newIndex = oldOrder [i] <= oldIndex ? oldOrder [i] : oldOrder [i] - 1;
+                newOrder [count++] = newIndex;
+            }
+        }
+        OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
+        int j = 0;
+        while (j < newOrder.length) {
+            if (oldOrder [j] !is newOrder [j]) break;
+            j++;
+        }
+        if (j !is newOrder.length) {
+            OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, newOrder.length, newOrder);
+            /*
+            * Bug in Windows.  When LVM_SETCOLUMNORDERARRAY is used to change
+            * the column order, the header redraws correctly but the table does
+            * not.  The fix is to force a redraw.
+            */
+            OS.InvalidateRect (handle, null, true);
+        }
+        TableColumn [] newColumns = new TableColumn [columnCount - orderIndex];
+        for (int i=orderIndex; i<newOrder.length; i++) {
+            newColumns [i - orderIndex] = columns [newOrder [i]];
+            newColumns [i - orderIndex].updateToolTip (newOrder [i]);
+        }
+        for (int i=0; i<newColumns.length; i++) {
+            if (!newColumns [i].isDisposed ()) {
+                newColumns [i].sendEvent (DWT.Move);
+            }
+        }
+    }
+
+    /* Remove the tool tip item for the header */
+    if (headerToolTipHandle !is 0) {
+        TOOLINFO lpti = new TOOLINFO ();
+        lpti.cbSize = TOOLINFO.sizeof;
+        lpti.uId = column.id;
+        lpti.hwnd = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+        OS.SendMessage (headerToolTipHandle, OS.TTM_DELTOOL, 0, lpti);
+    }
+}
+
+void destroyItem (TableItem item) {
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    int index = 0;
+    while (index < count) {
+        if (items [index] is item) break;
+        index++;
+    }
+    if (index is count) return;
+    setDeferResize (true);
+    ignoreSelect = ignoreShrink = true;
+    int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
+    ignoreSelect = ignoreShrink = false;
+    if (code is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
+    System.arraycopy (items, index + 1, items, index, --count - index);
+    items [count] = null;
+    if (count is 0) setTableEmpty ();
+    setDeferResize (false);
+}
+
+void fixCheckboxImageList (bool fixScroll) {
+    /*
+    * Bug in Windows.  When the state image list is larger than the
+    * image list, Windows incorrectly positions the state images.  When
+    * the table is scrolled, Windows draws garbage.  The fix is to force
+    * the state image list to be the same size as the image list.
+    */
+    if ((style & DWT.CHECK) is 0) return;
+    int hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
+    if (hImageList is 0) return;
+    int [] cx = new int [1], cy = new int [1];
+    OS.ImageList_GetIconSize (hImageList, cx, cy);
+    int hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
+    if (hStateList is 0) return;
+    int [] stateCx = new int [1], stateCy = new int [1];
+    OS.ImageList_GetIconSize (hStateList, stateCx, stateCy);
+    if (cx [0] is stateCx [0] && cy [0] is stateCy [0]) return;
+    setCheckboxImageList (cx [0], cy [0], fixScroll);
+}
+
+void fixCheckboxImageListColor (bool fixScroll) {
+    if ((style & DWT.CHECK) is 0) return;
+    int hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
+    if (hStateList is 0) return;
+    int [] cx = new int [1], cy = new int [1];
+    OS.ImageList_GetIconSize (hStateList, cx, cy);
+    setCheckboxImageList (cx [0], cy [0], fixScroll);
+}
+
+void fixItemHeight (bool fixScroll) {
+    /*
+    * Bug in Windows.  When both a header and grid lines are
+    * displayed, the grid lines do not take into account the
+    * height of the header and draw in the wrong place.  The
+    * fix is to set the height of the table items to be the
+    * height of the header so that the lines draw in the right
+    * place.  The height of a table item is the maximum of the
+    * height of the font or the height of image list.
+    *
+    * NOTE: In version 5.80 of COMCTL32.DLL, the bug is fixed.
+    */
+    if (itemHeight !is -1) return;
+    if (OS.COMCTL32_VERSION >= OS.VERSION (5, 80)) return;
+    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+    if ((bits & OS.LVS_EX_GRIDLINES) is 0) return;
+    bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.LVS_NOCOLUMNHEADER) !is 0) return;
+    /*
+    * Bug in Windows.  Making any change to an item that
+    * changes the item height of a table while the table
+    * is scrolled can cause the lines to draw incorrectly.
+    * This happens even when the lines are not currently
+    * visible and are shown afterwards.  The fix is to
+    * save the top index, scroll to the top of the table
+    * and then restore the original top index.
+    */
+    int topIndex = getTopIndex ();
+    if (fixScroll && topIndex !is 0) {
+        setRedraw (false);
+        setTopIndex (0);
+    }
+    int hOldList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
+    if (hOldList !is 0) return;
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    RECT rect = new RECT ();
+    OS.GetWindowRect (hwndHeader, rect);
+    int height = rect.bottom - rect.top - 1;
+    int hImageList = OS.ImageList_Create (1, height, 0, 0, 0);
+    OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
+    fixCheckboxImageList (false);
+    OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0);
+    if (headerImageList !is null) {
+        int hHeaderImageList = headerImageList.getHandle ();
+        OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
+    }
+    OS.ImageList_Destroy (hImageList);
+    if (fixScroll && topIndex !is 0) {
+        setTopIndex (topIndex);
+        setRedraw (true);
+    }
+}
+
+/**
+ * Returns the column at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ * Columns are returned in the order that they were created.
+ * If no <code>TableColumn</code>s were created by the programmer,
+ * this method will throw <code>ERROR_INVALID_RANGE</code> despite
+ * the fact that a single column of data may be visible in the table.
+ * This occurs when the programmer uses the table like a list, adding
+ * items but never creating a column.
+ *
+ * @param index the index of the column to return
+ * @return the column at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#getColumnOrder()
+ * @see Table#setColumnOrder(int[])
+ * @see TableColumn#getMoveable()
+ * @see TableColumn#setMoveable(bool)
+ * @see DWT#Move
+ */
+public TableColumn getColumn (int index) {
+    checkWidget ();
+    if (!(0 <= index && index < columnCount)) error (DWT.ERROR_INVALID_RANGE);
+    return columns [index];
+}
+
+/**
+ * Returns the number of columns contained in the receiver.
+ * If no <code>TableColumn</code>s were created by the programmer,
+ * this value is zero, despite the fact that visually, one column
+ * of items may be visible. This occurs when the programmer uses
+ * the table like a list, adding items but never creating a column.
+ *
+ * @return the number of columns
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getColumnCount () {
+    checkWidget ();
+    return columnCount;
+}
+
+/**
+ * Returns an array of zero-relative integers that map
+ * the creation order of the receiver's items to the
+ * order in which they are currently being displayed.
+ * <p>
+ * Specifically, the indices of the returned array represent
+ * the current visual order of the items, and the contents
+ * of the array represent the creation order of the items.
+ * </p><p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the current visual order of the receiver's items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#setColumnOrder(int[])
+ * @see TableColumn#getMoveable()
+ * @see TableColumn#setMoveable(bool)
+ * @see DWT#Move
+ *
+ * @since 3.1
+ */
+public int[] getColumnOrder () {
+    checkWidget ();
+    if (columnCount is 0) return new int [0];
+    int [] order = new int [columnCount];
+    OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
+    return order;
+}
+
+/**
+ * Returns an array of <code>TableColumn</code>s which are the
+ * columns in the receiver.  Columns are returned in the order
+ * that they were created.  If no <code>TableColumn</code>s were
+ * created by the programmer, the array is empty, despite the fact
+ * that visually, one column of items may be visible. This occurs
+ * when the programmer uses the table like a list, adding items but
+ * never creating a column.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#getColumnOrder()
+ * @see Table#setColumnOrder(int[])
+ * @see TableColumn#getMoveable()
+ * @see TableColumn#setMoveable(bool)
+ * @see DWT#Move
+ */
+public TableColumn [] getColumns () {
+    checkWidget ();
+    TableColumn [] result = new TableColumn [columnCount];
+    System.arraycopy (columns, 0, result, 0, columnCount);
+    return result;
+}
+
+int getFocusIndex () {
+//  checkWidget ();
+    return OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
+}
+
+/**
+ * Returns the width in pixels of a grid line.
+ *
+ * @return the width of a grid line in pixels
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getGridLineWidth () {
+    checkWidget ();
+    return GRID_WIDTH;
+}
+
+/**
+ * Returns the height of the receiver's header
+ *
+ * @return the height of the header or zero if the header is not visible
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public int getHeaderHeight () {
+    checkWidget ();
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    if (hwndHeader is 0) return 0;
+    RECT rect = new RECT ();
+    OS.GetWindowRect (hwndHeader, rect);
+    return rect.bottom - rect.top;
+}
+
+/**
+ * Returns <code>true</code> if the receiver's header is visible,
+ * and <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's header's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getHeaderVisible () {
+    checkWidget ();
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    return (bits & OS.LVS_NOCOLUMNHEADER) is 0;
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableItem getItem (int index) {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= index && index < count)) error (DWT.ERROR_INVALID_RANGE);
+    return _getItem (index);
+}
+
+/**
+ * Returns the item at the given point in the receiver
+ * or null if no such item exists. The point is in the
+ * coordinate system of the receiver.
+ * <p>
+ * The item that is returned represents an item that could be selected by the user.
+ * For example, if selection only occurs in items in the first column, then null is
+ * returned if the point is outside of the item.
+ * Note that the DWT.FULL_SELECTION style hint, which specifies the selection policy,
+ * determines the extent of the selection.
+ * </p>
+ *
+ * @param point the point used to locate the item
+ * @return the item at the given point, or null if the point is not in a selectable item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableItem getItem (Point point) {
+    checkWidget ();
+    if (point is null) error (DWT.ERROR_NULL_ARGUMENT);
+    LVHITTESTINFO pinfo = new LVHITTESTINFO ();
+    pinfo.x = point.x;  pinfo.y = point.y;
+    OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
+    if (pinfo.iItem !is -1) {
+        /*
+        * Bug in Windows.  When the point that is used by
+        * LVM_HITTEST is inside the header, Windows returns
+        * the first item in the table.  The fix is to check
+        * when LVM_HITTEST returns the first item and make
+        * sure that when the point is within the header,
+        * the first item is not returned.
+        */
+        if (pinfo.iItem is 0) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            if ((bits & OS.LVS_NOCOLUMNHEADER) is 0) {
+                int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                if (hwndHeader !is 0) {
+                    RECT rect = new RECT ();
+                    OS.GetWindowRect (hwndHeader, rect);
+                    POINT pt = new POINT ();
+                    pt.x = pinfo.x;
+                    pt.y = pinfo.y;
+                    OS.MapWindowPoints (handle, 0, pt, 1);
+                    if (OS.PtInRect (rect, pt)) return null;
+                }
+            }
+        }
+        return _getItem (pinfo.iItem);
+    }
+    return null;
+}
+
+/**
+ * Returns the number of items contained in the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+}
+
+/**
+ * Returns the height of the area which would be used to
+ * display <em>one</em> of the items in the receiver's.
+ *
+ * @return the height of one item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemHeight () {
+    checkWidget ();
+    int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+    int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+    return (oneItem >> 16) - (empty >> 16);
+}
+
+/**
+ * Returns a (possibly empty) array of <code>TableItem</code>s which
+ * are the items in the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableItem [] getItems () {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    TableItem [] result = new TableItem [count];
+    if ((style & DWT.VIRTUAL) !is 0) {
+        for (int i=0; i<count; i++) {
+            result [i] = _getItem (i);
+        }
+    } else {
+        System.arraycopy (items, 0, result, 0, count);
+    }
+    return result;
+}
+
+/**
+ * Returns <code>true</code> if the receiver's lines are visible,
+ * and <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the visibility state of the lines
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getLinesVisible () {
+    checkWidget ();
+    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+    return (bits & OS.LVS_EX_GRIDLINES) !is 0;
+}
+
+/**
+ * Returns an array of <code>TableItem</code>s that are currently
+ * selected in the receiver. The order of the items is unspecified.
+ * An empty array indicates that no items are selected.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its selection, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ * @return an array representing the selection
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableItem [] getSelection () {
+    checkWidget ();
+    int i = -1, j = 0, count = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
+    TableItem [] result = new TableItem [count];
+    while ((i = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) !is -1) {
+        result [j++] = _getItem (i);
+    }
+    return result;
+}
+
+/**
+ * Returns the number of selected items contained in the receiver.
+ *
+ * @return the number of selected items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionCount () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
+}
+
+/**
+ * Returns the zero-relative index of the item which is currently
+ * selected in the receiver, or -1 if no item is selected.
+ *
+ * @return the index of the selected item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionIndex () {
+    checkWidget ();
+    int focusIndex = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
+    int selectedIndex = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED);
+    if (focusIndex is selectedIndex) return selectedIndex;
+    int i = -1;
+    while ((i = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) !is -1) {
+        if (i is focusIndex) return i;
+    }
+    return selectedIndex;
+}
+
+/**
+ * Returns the zero-relative indices of the items which are currently
+ * selected in the receiver. The order of the indices is unspecified.
+ * The array is empty if no items are selected.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its selection, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ * @return the array of indices of the selected items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int [] getSelectionIndices () {
+    checkWidget ();
+    int i = -1, j = 0, count = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
+    int [] result = new int [count];
+    while ((i = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) !is -1) {
+        result [j++] = i;
+    }
+    return result;
+}
+
+/**
+ * Returns the column which shows the sort indicator for
+ * the receiver. The value may be null if no column shows
+ * the sort indicator.
+ *
+ * @return the sort indicator
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setSortColumn(TableColumn)
+ *
+ * @since 3.2
+ */
+public TableColumn getSortColumn () {
+    checkWidget ();
+    return sortColumn;
+}
+
+int getSortColumnPixel () {
+    int pixel = OS.IsWindowEnabled (handle) ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE);
+    int red = pixel & 0xFF;
+    int green = (pixel & 0xFF00) >> 8;
+    int blue = (pixel & 0xFF0000) >> 16;
+    if (red > 240 && green > 240 && blue > 240) {
+        red -= 8;
+        green -= 8;
+        blue -= 8;
+    } else {
+        red = Math.min (0xFF, (red / 10) + red);
+        green = Math.min (0xFF, (green / 10) + green);
+        blue = Math.min (0xFF, (blue / 10) + blue);
+    }
+    return (red & 0xFF) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16);
+}
+
+/**
+ * Returns the direction of the sort indicator for the receiver.
+ * The value will be one of <code>UP</code>, <code>DOWN</code>
+ * or <code>NONE</code>.
+ *
+ * @return the sort direction
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setSortDirection(int)
+ *
+ * @since 3.2
+ */
+public int getSortDirection () {
+    checkWidget ();
+    return sortDirection;
+}
+
+/**
+ * Returns the zero-relative index of the item which is currently
+ * at the top of the receiver. This index can change when items are
+ * scrolled or new items are added or removed.
+ *
+ * @return the index of the top item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTopIndex () {
+    checkWidget ();
+    /*
+    * Bug in Windows.  Under rare circumstances, LVM_GETTOPINDEX
+    * can return a negative number.  When this happens, the table
+    * is displaying blank lines at the top of the controls.  The
+    * fix is to check for a negative number and return zero instead.
+    */
+    return Math.max (0, OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0));
+}
+
+bool hasChildren () {
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    int hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
+    while (hwndChild !is 0) {
+        if (hwndChild !is hwndHeader) return true;
+        hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
+    }
+    return false;
+}
+
+int imageIndex (Image image, int column) {
+    if (image is null) return OS.I_IMAGENONE;
+    if (column is 0) {
+        firstColumnImage = true;
+    } else {
+        setSubImagesVisible (true);
+    }
+    if (imageList is null) {
+        Rectangle bounds = image.getBounds ();
+        imageList = display.getImageList (style & DWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
+        int index = imageList.indexOf (image);
+        if (index is -1) index = imageList.add (image);
+        int hImageList = imageList.getHandle ();
+        /*
+        * Bug in Windows.  Making any change to an item that
+        * changes the item height of a table while the table
+        * is scrolled can cause the lines to draw incorrectly.
+        * This happens even when the lines are not currently
+        * visible and are shown afterwards.  The fix is to
+        * save the top index, scroll to the top of the table
+        * and then restore the original top index.
+        */
+        int topIndex = getTopIndex ();
+        if (topIndex !is 0) {
+            setRedraw (false);
+            setTopIndex (0);
+        }
+        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
+        if (headerImageList !is null) {
+            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+            int hHeaderImageList = headerImageList.getHandle ();
+            OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
+        }
+        fixCheckboxImageList (false);
+        if (itemHeight !is -1) setItemHeight (false);
+        if (topIndex !is 0) {
+            setTopIndex (topIndex);
+            setRedraw (true);
+        }
+        return index;
+    }
+    int index = imageList.indexOf (image);
+    if (index !is -1) return index;
+    return imageList.add (image);
+}
+
+int imageIndexHeader (Image image) {
+    if (image is null) return OS.I_IMAGENONE;
+    if (headerImageList is null) {
+        Rectangle bounds = image.getBounds ();
+        headerImageList = display.getImageList (style & DWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
+        int index = headerImageList.indexOf (image);
+        if (index is -1) index = headerImageList.add (image);
+        int hImageList = headerImageList.getHandle ();
+        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+        OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hImageList);
+        return index;
+    }
+    int index = headerImageList.indexOf (image);
+    if (index !is -1) return index;
+    return headerImageList.add (image);
+}
+
+/**
+ * Searches the receiver's list starting at the first column
+ * (index 0) until a column is found that is equal to the
+ * argument, and returns the index of that column. If no column
+ * is found, returns -1.
+ *
+ * @param column the search column
+ * @return the index of the column
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the column is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (TableColumn column) {
+    checkWidget ();
+    if (column is null) error (DWT.ERROR_NULL_ARGUMENT);
+    for (int i=0; i<columnCount; i++) {
+        if (columns [i] is column) return i;
+    }
+    return -1;
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (TableItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (1 <= lastIndexOf && lastIndexOf < count - 1) {
+        if (items [lastIndexOf] is item) return lastIndexOf;
+        if (items [lastIndexOf + 1] is item) return ++lastIndexOf;
+        if (items [lastIndexOf - 1] is item) return --lastIndexOf;
+    }
+    if (lastIndexOf < count / 2) {
+        for (int i=0; i<count; i++) {
+            if (items [i] is item) return lastIndexOf = i;
+        }
+    } else {
+        for (int i=count - 1; i>=0; --i) {
+            if (items [i] is item) return lastIndexOf = i;
+        }
+    }
+    return -1;
+}
+
+/**
+ * Returns <code>true</code> if the item is selected,
+ * and <code>false</code> otherwise.  Indices out of
+ * range are ignored.
+ *
+ * @param index the index of the item
+ * @return the selection state of the item at the index
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool isSelected (int index) {
+    checkWidget ();
+    LVITEM lvItem = new LVITEM ();
+    lvItem.mask = OS.LVIF_STATE;
+    lvItem.stateMask = OS.LVIS_SELECTED;
+    lvItem.iItem = index;
+    int result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
+    return (result !is 0) && ((lvItem.state & OS.LVIS_SELECTED) !is 0);
+}
+
+void register () {
+    super.register ();
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    if (hwndHeader !is 0) display.addControl (hwndHeader, this);
+}
+
+void releaseChildren (bool destroy) {
+    if (items !is null) {
+        int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+        /*
+        * Feature in Windows 98.  When there are a large number
+        * of columns and items in a table (>1000) where each
+        * of the subitems in the table has a string, it is much
+        * faster to delete each item with LVM_DELETEITEM rather
+        * than using LVM_DELETEALLITEMS.  The fix is to detect
+        * this case and delete the items, one by one.  The fact
+        * that the fix is only necessary on Windows 98 was
+        * confirmed using version 5.81 of COMCTL32.DLL on both
+        * Windows 98 and NT.
+        *
+        * NOTE: LVM_DELETEALLITEMS is also sent by the table
+        * when the table is destroyed.
+        */
+        if (OS.IsWin95 && columnCount > 1) {
+            /* Turn off redraw and resize events and leave them off */
+            resizeCount = 1;
+            OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+            for (int i=itemCount-1; i>=0; --i) {
+                TableItem item = items [i];
+                if (item !is null && !item.isDisposed ()) item.release (false);
+                ignoreSelect = ignoreShrink = true;
+                OS.SendMessage (handle, OS.LVM_DELETEITEM, i, 0);
+                ignoreSelect = ignoreShrink = false;
+            }
+        } else {
+            for (int i=0; i<itemCount; i++) {
+                TableItem item = items [i];
+                if (item !is null && !item.isDisposed ()) item.release (false);
+            }
+        }
+        items = null;
+    }
+    if (columns !is null) {
+        for (int i=0; i<columnCount; i++) {
+            TableColumn column = columns [i];
+            if (!column.isDisposed ()) column.release (false);
+        }
+        columns = null;
+    }
+    super.releaseChildren (destroy);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    customDraw = false;
+    currentItem = null;
+    if (imageList !is null) {
+        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0);
+        display.releaseImageList (imageList);
+    }
+    if (headerImageList !is null) {
+        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+        OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, 0);
+        display.releaseImageList (headerImageList);
+    }
+    imageList = headerImageList = null;
+    int hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
+    OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_STATE, 0);
+    if (hStateList !is 0) OS.ImageList_Destroy (hStateList);
+    if (headerToolTipHandle !is 0) OS.DestroyWindow (headerToolTipHandle);
+    headerToolTipHandle = 0;
+}
+
+/**
+ * Removes the items from the receiver's list at the given
+ * zero-relative indices.
+ *
+ * @param indices the array of indices of the items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ *    <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int [] indices) {
+    checkWidget ();
+    if (indices is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (indices.length is 0) return;
+    int [] newIndices = new int [indices.length];
+    System.arraycopy (indices, 0, newIndices, 0, indices.length);
+    sort (newIndices);
+    int start = newIndices [newIndices.length - 1], end = newIndices [0];
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= start && start <= end && end < count)) {
+        error (DWT.ERROR_INVALID_RANGE);
+    }
+    setDeferResize (true);
+    int last = -1;
+    for (int i=0; i<newIndices.length; i++) {
+        int index = newIndices [i];
+        if (index !is last) {
+            TableItem item = items [index];
+            if (item !is null && !item.isDisposed ()) item.release (false);
+            ignoreSelect = ignoreShrink = true;
+            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
+            ignoreSelect = ignoreShrink = false;
+            if (code is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
+            System.arraycopy (items, index + 1, items, index, --count - index);
+            items [count] = null;
+            last = index;
+        }
+    }
+    if (count is 0) setTableEmpty ();
+    setDeferResize (false);
+}
+
+/**
+ * Removes the item from the receiver at the given
+ * zero-relative index.
+ *
+ * @param index the index for the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int index) {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= index && index < count)) error (DWT.ERROR_INVALID_RANGE);
+    TableItem item = items [index];
+    if (item !is null && !item.isDisposed ()) item.release (false);
+    setDeferResize (true);
+    ignoreSelect = ignoreShrink = true;
+    int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
+    ignoreSelect = ignoreShrink = false;
+    if (code is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
+    System.arraycopy (items, index + 1, items, index, --count - index);
+    items [count] = null;
+    if (count is 0) setTableEmpty ();
+    setDeferResize (false);
+}
+
+/**
+ * Removes the items from the receiver which are
+ * between the given zero-relative start and end
+ * indices (inclusive).
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int start, int end) {
+    checkWidget ();
+    if (start > end) return;
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= start && start <= end && end < count)) {
+        error (DWT.ERROR_INVALID_RANGE);
+    }
+    if (start is 0 && end is count - 1) {
+        removeAll ();
+    } else {
+        setDeferResize (true);
+        int index = start;
+        while (index <= end) {
+            TableItem item = items [index];
+            if (item !is null && !item.isDisposed ()) item.release (false);
+            ignoreSelect = ignoreShrink = true;
+            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, start, 0);
+            ignoreSelect = ignoreShrink = false;
+            if (code is 0) break;
+            index++;
+        }
+        System.arraycopy (items, index, items, start, count - index);
+        for (int i=count-(index-start); i<count; i++) items [i] = null;
+        if (index <= end) error (DWT.ERROR_ITEM_NOT_REMOVED);
+        /*
+        * This code is intentionally commented.  It is not necessary
+        * to check for an empty table because removeAll() was called
+        * when the start is 0 and end is count - 1.
+        */
+        //if (count - index is 0) setTableEmpty ();
+        setDeferResize (false);
+    }
+}
+
+/**
+ * Removes all of the items from the receiver.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void removeAll () {
+    checkWidget ();
+    int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<itemCount; i++) {
+        TableItem item = items [i];
+        if (item !is null && !item.isDisposed ()) item.release (false);
+    }
+    /*
+    * Feature in Windows 98.  When there are a large number
+    * of columns and items in a table (>1000) where each
+    * of the subitems in the table has a string, it is much
+    * faster to delete each item with LVM_DELETEITEM rather
+    * than using LVM_DELETEALLITEMS.  The fix is to detect
+    * this case and delete the items, one by one.  The fact
+    * that the fix is only necessary on Windows 98 was
+    * confirmed using version 5.81 of COMCTL32.DLL on both
+    * Windows 98 and NT.
+    *
+    * NOTE: LVM_DELETEALLITEMS is also sent by the table
+    * when the table is destroyed.
+    */
+    setDeferResize (true);
+    if (OS.IsWin95 && columnCount > 1) {
+        bool redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+        if (redraw) OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+        int index = itemCount - 1;
+        while (index >= 0) {
+            ignoreSelect = ignoreShrink = true;
+            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
+            ignoreSelect = ignoreShrink = false;
+            if (code is 0) break;
+            --index;
+        }
+        if (redraw) {
+            OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+            /*
+            * This code is intentionally commented.  The window proc
+            * for the table implements WM_SETREDRAW to invalidate
+            * and erase the table so it is not necessary to do this
+            * again.
+            */
+//          int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
+//          OS.RedrawWindow (handle, null, 0, flags);
+        }
+        if (index !is -1) error (DWT.ERROR_ITEM_NOT_REMOVED);
+    } else {
+        ignoreSelect = ignoreShrink = true;
+        int code = OS.SendMessage (handle, OS.LVM_DELETEALLITEMS, 0, 0);
+        ignoreSelect = ignoreShrink = false;
+        if (code is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
+    }
+    setTableEmpty ();
+    setDeferResize (false);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's selection.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener(SelectionListener)
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Selects the items at the given zero-relative indices in the receiver.
+ * The current selection is not cleared before the new items are selected.
+ * <p>
+ * If the item at a given index is not selected, it is selected.
+ * If the item at a given index was already selected, it remains selected.
+ * Indices that are out of range and duplicate indices are ignored.
+ * If the receiver is single-select and multiple indices are specified,
+ * then all indices are ignored.
+ * </p>
+ *
+ * @param indices the array of indices for the items to select
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#setSelection(int[])
+ */
+public void select (int [] indices) {
+    checkWidget ();
+    if (indices is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int length = indices.length;
+    if (length is 0 || ((style & DWT.SINGLE) !is 0 && length > 1)) return;
+    LVITEM lvItem = new LVITEM ();
+    lvItem.state = OS.LVIS_SELECTED;
+    lvItem.stateMask = OS.LVIS_SELECTED;
+    for (int i=length-1; i>=0; --i) {
+        /*
+        * An index of -1 will apply the change to all
+        * items.  Ensure that indices are greater than -1.
+        */
+        if (indices [i] >= 0) {
+            ignoreSelect = true;
+            OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem);
+            ignoreSelect = false;
+        }
+    }
+}
+
+/**
+ * Selects the item at the given zero-relative index in the receiver.
+ * If the item at the index was already selected, it remains
+ * selected. Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to select
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void select (int index) {
+    checkWidget ();
+    /*
+    * An index of -1 will apply the change to all
+    * items.  Ensure that index is greater than -1.
+    */
+    if (index < 0) return;
+    LVITEM lvItem = new LVITEM ();
+    lvItem.state = OS.LVIS_SELECTED;
+    lvItem.stateMask = OS.LVIS_SELECTED;
+    ignoreSelect = true;
+    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
+    ignoreSelect = false;
+}
+
+/**
+ * Selects the items in the range specified by the given zero-relative
+ * indices in the receiver. The range of indices is inclusive.
+ * The current selection is not cleared before the new items are selected.
+ * <p>
+ * If an item in the given range is not selected, it is selected.
+ * If an item in the given range was already selected, it remains selected.
+ * Indices that are out of range are ignored and no items will be selected
+ * if start is greater than end.
+ * If the receiver is single-select and there is more than one item in the
+ * given range, then all indices are ignored.
+ * </p>
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#setSelection(int,int)
+ */
+public void select (int start, int end) {
+    checkWidget ();
+    if (end < 0 || start > end || ((style & DWT.SINGLE) !is 0 && start !is end)) return;
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (count is 0 || start >= count) return;
+    start = Math.max (0, start);
+    end = Math.min (end, count - 1);
+    if (start is 0 && end is count - 1) {
+        selectAll ();
+    } else {
+        /*
+        * An index of -1 will apply the change to all
+        * items.  Indices must be greater than -1.
+        */
+        LVITEM lvItem = new LVITEM ();
+        lvItem.state = OS.LVIS_SELECTED;
+        lvItem.stateMask = OS.LVIS_SELECTED;
+        for (int i=start; i<=end; i++) {
+            ignoreSelect = true;
+            OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem);
+            ignoreSelect = false;
+        }
+    }
+}
+
+/**
+ * Selects all of the items in the receiver.
+ * <p>
+ * If the receiver is single-select, do nothing.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void selectAll () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) return;
+    LVITEM lvItem = new LVITEM ();
+    lvItem.mask = OS.LVIF_STATE;
+    lvItem.state = OS.LVIS_SELECTED;
+    lvItem.stateMask = OS.LVIS_SELECTED;
+    ignoreSelect = true;
+    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem);
+    ignoreSelect = false;
+}
+
+void sendEraseItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd, int lParam) {
+    int hDC = nmcd.hdc;
+    int hFont = item.cellFont !is null ? item.cellFont [nmcd.iSubItem] : -1;
+    if (hFont is -1) hFont = item.font;
+    int clrText = item.cellForeground !is null ? item.cellForeground [nmcd.iSubItem] : -1;
+    if (clrText is -1) clrText = item.foreground;
+    int clrTextBk = -1;
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        if (sortColumn !is null && sortDirection !is DWT.NONE) {
+            if (findImageControl () is null) {
+                if (indexOf (sortColumn) is nmcd.iSubItem) {
+                    clrTextBk = getSortColumnPixel ();
+                }
+            }
+        }
+    }
+    clrTextBk = item.cellBackground !is null ? item.cellBackground [nmcd.iSubItem] : -1;
+    if (clrTextBk is -1) clrTextBk = item.background;
+    /*
+    * Bug in Windows.  For some reason, CDIS_SELECTED always set,
+    * even for items that are not selected.  The fix is to get
+    * the selection state from the item.
+    */
+    LVITEM lvItem = new LVITEM ();
+    lvItem.mask = OS.LVIF_STATE;
+    lvItem.stateMask = OS.LVIS_SELECTED;
+    lvItem.iItem = nmcd.dwItemSpec;
+    int result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
+    bool selected = (result !is 0 && (lvItem.state & OS.LVIS_SELECTED) !is 0);
+    GCData data = new GCData ();
+    data.device = display;
+    int clrSelectionBk = -1;
+    bool drawSelected = false, drawBackground = false, drawHot = false;
+    if (nmcd.iSubItem is 0 || (style & DWT.FULL_SELECTION) !is 0) {
+        drawHot = hotIndex is nmcd.dwItemSpec;
+    }
+    if (OS.IsWindowEnabled (handle)) {
+        if (selected && (nmcd.iSubItem is 0 || (style & DWT.FULL_SELECTION) !is 0)) {
+            if (OS.GetFocus () is handle) {
+                drawSelected = true;
+                data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
+                data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
+            } else {
+                drawSelected = (style & DWT.HIDE_SELECTION) is 0;
+                data.foreground = OS.GetTextColor (hDC);
+                data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_3DFACE);
+            }
+            if (explorerTheme) {
+                data.foreground = clrText !is -1 ? clrText : getForegroundPixel ();
+            }
+        } else {
+            drawBackground = clrTextBk !is -1;
+            /*
+            * Bug in Windows.  When LVM_SETTEXTBKCOLOR or LVM_SETBKCOLOR
+            * is used to set the background color of the the text or the
+            * control, the color is not set in the HDC that is provided
+            * in Custom Draw.  The fix is to explicitly set the background
+            * color.
+            */
+            if (clrTextBk is -1) {
+                Control control = findBackgroundControl ();
+                if (control is null) control = this;
+                clrTextBk = control.getBackgroundPixel ();
+            }
+            data.foreground = clrText !is -1 ? clrText : OS.GetTextColor (hDC);
+            data.background = clrTextBk !is -1 ? clrTextBk : OS.GetBkColor (hDC);
+        }
+    } else {
+        data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
+        data.background = OS.GetSysColor (OS.COLOR_3DFACE);
+        if (selected) clrSelectionBk = data.background;
+    }
+    data.hFont = hFont;
+    data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+    int nSavedDC = OS.SaveDC (hDC);
+    GC gc = GC.win32_new (hDC, data);
+    RECT cellRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC);
+    Event event = new Event ();
+    event.item = item;
+    event.gc = gc;
+    event.index = nmcd.iSubItem;
+    event.detail |= DWT.FOREGROUND;
+//  if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) {
+    if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) is nmcd.dwItemSpec) {
+        if (nmcd.iSubItem is 0 || (style & DWT.FULL_SELECTION) !is 0) {
+            if (handle is OS.GetFocus ()) {
+                int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+                if ((uiState & OS.UISF_HIDEFOCUS) is 0) event.detail |= DWT.FOCUSED;
+            }
+        }
+    }
+    if (drawHot) event.detail |= DWT.HOT;
+    if (drawSelected) event.detail |= DWT.SELECTED;
+    if (drawBackground) event.detail |= DWT.BACKGROUND;
+    event.x = cellRect.left;
+    event.y = cellRect.top;
+    event.width = cellRect.right - cellRect.left;
+    event.height = cellRect.bottom - cellRect.top;
+    gc.setClipping (event.x, event.y, event.width, event.height);
+    sendEvent (DWT.EraseItem, event);
+    event.gc = null;
+    int clrSelectionText = data.foreground;
+    gc.dispose ();
+    OS.RestoreDC (hDC, nSavedDC);
+    if (isDisposed () || item.isDisposed ()) return;
+    if (event.doit) {
+        ignoreDrawForeground = (event.detail & DWT.FOREGROUND) is 0;
+        ignoreDrawBackground = (event.detail & DWT.BACKGROUND) is 0;
+        ignoreDrawSelection = (event.detail & DWT.SELECTED) is 0;
+        ignoreDrawFocus = (event.detail & DWT.FOCUSED) is 0;
+        ignoreDrawHot = (event.detail & DWT.HOT) is 0;
+    } else {
+        ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true;
+    }
+    if (drawSelected) {
+        if (ignoreDrawSelection) {
+            ignoreDrawHot = true;
+            if (nmcd.iSubItem is 0 || (style & DWT.FULL_SELECTION) !is 0) {
+                selectionForeground = clrSelectionText;
+            }
+            nmcd.uItemState &= ~OS.CDIS_SELECTED;
+            OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
+        }
+    } else {
+        if (ignoreDrawSelection) {
+            nmcd.uItemState |= OS.CDIS_SELECTED;
+            OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
+        }
+    }
+    if (ignoreDrawFocus) {
+        nmcd.uItemState &= ~OS.CDIS_FOCUS;
+        OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
+    }
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    bool firstColumn = nmcd.iSubItem is OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
+    if (ignoreDrawForeground && ignoreDrawHot) {
+        if (!ignoreDrawBackground && drawBackground) {
+            RECT backgroundRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, false, true, false, hDC);
+            fillBackground (hDC, clrTextBk, backgroundRect);
+        }
+    }
+    if (!ignoreDrawHot || (!ignoreDrawSelection && clrSelectionBk !is -1)) {
+        if (explorerTheme) {
+            bool hot = drawHot;
+            RECT pClipRect = new RECT ();
+            OS.SetRect (pClipRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+            RECT rect = new RECT ();
+            OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+            if ((style & DWT.FULL_SELECTION) !is 0) {
+                int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0);
+                RECT headerRect = new RECT ();
+                OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+                OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
+                rect.left = 0;
+                rect.right = headerRect.right;
+                pClipRect.left = cellRect.left;
+                pClipRect.right += EXPLORER_EXTRA;
+            } else {
+                rect.right += EXPLORER_EXTRA;
+                pClipRect.right += EXPLORER_EXTRA;
+            }
+            int hTheme = OS.OpenThemeData (handle, Display.LISTVIEW);
+            int iStateId = selected ? OS.LISS_SELECTED : OS.LISS_HOT;
+            if (OS.GetFocus () !is handle && selected && !hot) iStateId = OS.LISS_SELECTEDNOTFOCUS;
+            OS.DrawThemeBackground (hTheme, hDC, OS.LVP_LISTITEM, iStateId, rect, pClipRect);
+            OS.CloseThemeData (hTheme);
+        } else {
+            bool fullText = ((style & DWT.FULL_SELECTION) !is 0 || !firstColumn);
+            RECT textRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, false, fullText, false, hDC);
+            fillBackground (hDC, clrSelectionBk, textRect);
+        }
+    }
+    if (ignoreDrawForeground) {
+        RECT clipRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, false, hDC);
+        OS.SaveDC (hDC);
+        OS.SelectClipRgn (hDC, 0);
+        OS.ExcludeClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+    }
+}
+
+Event sendMeasureItemEvent (TableItem item, int row, int column, int hDC) {
+    int hFont = item.cellFont !is null ? item.cellFont [column] : -1;
+    if (hFont is -1) hFont = item.font;
+    GCData data = new GCData ();
+    data.device = display;
+    data.hFont = hFont;
+    int nSavedDC = OS.SaveDC (hDC);
+    GC gc = GC.win32_new (hDC, data);
+    RECT itemRect = item.getBounds (row, column, true, true, false, false, hDC);
+    Event event = new Event ();
+    event.item = item;
+    event.gc = gc;
+    event.index = column;
+    event.x = itemRect.left;
+    event.y = itemRect.top;
+    event.width = itemRect.right - itemRect.left;
+    event.height = itemRect.bottom - itemRect.top;
+    sendEvent (DWT.MeasureItem, event);
+    event.gc = null;
+    gc.dispose ();
+    OS.RestoreDC (hDC, nSavedDC);
+    if (!isDisposed () && !item.isDisposed ()) {
+        if (columnCount is 0) {
+            int width = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
+            if (event.x + event.width > width) {
+                OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, event.x + event.width);
+            }
+        }
+        if (event.height > getItemHeight ()) setItemHeight (event.height);
+    }
+    return event;
+}
+
+LRESULT sendMouseDownEvent (int type, int button, int msg, int wParam, int lParam) {
+    /*
+    * Feature in Windows.  Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
+    * the widget starts a modal loop to determine if the user wants
+    * to begin a drag/drop operation or marque select.  Unfortunately,
+    * this modal loop eats the corresponding mouse up.  The fix is to
+    * detect the cases when the modal loop has eaten the mouse up and
+    * issue a fake mouse up.
+    *
+    * By observation, when the mouse is clicked anywhere but the check
+    * box, the widget eats the mouse up.  When the mouse is dragged,
+    * the widget does not eat the mouse up.
+    */
+    LVHITTESTINFO pinfo = new LVHITTESTINFO ();
+    pinfo.x = (short) (lParam & 0xFFFF);
+    pinfo.y = (short) (lParam >> 16);
+    OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
+    Display display = this.display;
+    display.captureChanged = false;
+    if (!sendMouseEvent (type, button, handle, msg, wParam, lParam)) {
+        if (!display.captureChanged && !isDisposed ()) {
+            if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+        }
+        return LRESULT.ZERO;
+    }
+
+    /*
+    * Force the table to have focus so that when the user
+    * reselects the focus item, the LVIS_FOCUSED state bits
+    * for the item will be set.  If the user did not click on
+    * an item, then set focus to the table so that it will
+    * come to the front and take focus in the work around
+    * below.
+    */
+    OS.SetFocus (handle);
+
+    /*
+    * Feature in Windows.  When the user selects outside of
+    * a table item, Windows deselects all the items, even
+    * when the table is multi-select.  While not strictly
+    * wrong, this is unexpected.  The fix is to detect the
+    * case and avoid calling the window proc.
+    */
+    if ((style & DWT.SINGLE) !is 0 || hooks (DWT.MouseDown) || hooks (DWT.MouseUp)) {
+        if (pinfo.iItem is -1) {
+            if (!display.captureChanged && !isDisposed ()) {
+                if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+            }
+            return LRESULT.ZERO;
+        }
+    }
+
+    /*
+    * Feature in Windows.  When a table item is reselected
+    * in a single-select table, Windows does not issue a
+    * WM_NOTIFY because the item state has not changed.
+    * This is strictly correct but is inconsistent with the
+    * list widget and other widgets in Windows.  The fix is
+    * to detect the case when an item is reselected and mark
+    * it as selected.
+    */
+    bool forceSelect = false;
+    int count = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
+    if (count is 1 && pinfo.iItem !is -1) {
+        LVITEM lvItem = new LVITEM ();
+        lvItem.mask = OS.LVIF_STATE;
+        lvItem.stateMask = OS.LVIS_SELECTED;
+        lvItem.iItem = pinfo.iItem;
+        OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
+        if ((lvItem.state & OS.LVIS_SELECTED) !is 0) {
+            forceSelect = true;
+        }
+    }
+    /*
+    * Feature in Windows.  Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
+    * the widget starts a modal loop to determine if the user wants
+    * to begin a drag/drop operation or marque select.  This modal
+    * loop eats mouse events until a drag is detected.  The fix is
+    * to avoid this behavior by only running the drag and drop when
+    * the event is hooked and the mouse is over an item.
+    */
+    bool dragDetect = (state & DRAG_DETECT) !is 0 && hooks (DWT.DragDetect);
+    if (!dragDetect) {
+        int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL;
+        dragDetect = pinfo.iItem is -1 || (pinfo.flags & flags) is 0;
+    }
+    if (!dragDetect) display.runDragDrop = false;
+    int code = callWindowProc (handle, msg, wParam, lParam, forceSelect);
+    if (!dragDetect) display.runDragDrop = true;
+    if (dragStarted || !dragDetect) {
+        if (!display.captureChanged && !isDisposed ()) {
+            if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+        }
+    } else {
+        int flags = OS.LVHT_ONITEMLABEL | OS.LVHT_ONITEMICON;
+        bool fakeMouseUp = (pinfo.flags & flags) !is 0;
+        if (!fakeMouseUp && (style & DWT.MULTI) !is 0) {
+            fakeMouseUp = (pinfo.flags & OS.LVHT_ONITEMSTATEICON) is 0;
+        }
+        if (fakeMouseUp) {
+            sendMouseEvent (DWT.MouseUp, button, handle, msg, wParam, lParam);
+        }
+    }
+    dragStarted = false;
+    return new LRESULT (code);
+}
+
+void sendPaintItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd) {
+    int hDC = nmcd.hdc;
+    GCData data = new GCData ();
+    data.device = display;
+    int hFont = item.cellFont !is null ? item.cellFont [nmcd.iSubItem] : -1;
+    if (hFont is -1) hFont = item.font;
+    data.hFont = hFont;
+    /*
+    * Bug in Windows.  For some reason, CDIS_SELECTED always set,
+    * even for items that are not selected.  The fix is to get
+    * the selection state from the item.
+    */
+    LVITEM lvItem = new LVITEM ();
+    lvItem.mask = OS.LVIF_STATE;
+    lvItem.stateMask = OS.LVIS_SELECTED;
+    lvItem.iItem = nmcd.dwItemSpec;
+    int result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
+    bool selected = result !is 0 && (lvItem.state & OS.LVIS_SELECTED) !is 0;
+    bool drawSelected = false, drawBackground = false, drawHot = false;
+    if (nmcd.iSubItem is 0 || (style & DWT.FULL_SELECTION) !is 0) {
+        drawHot = hotIndex is nmcd.dwItemSpec;
+    }
+    if (OS.IsWindowEnabled (handle)) {
+        if (selected && (nmcd.iSubItem is 0 || (style & DWT.FULL_SELECTION) !is 0)) {
+            if (OS.GetFocus () is handle) {
+                drawSelected = true;
+                if (selectionForeground !is -1) {
+                    data.foreground = selectionForeground;
+                } else {
+                    data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
+                }
+                data.background = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
+            } else {
+                drawSelected = (style & DWT.HIDE_SELECTION) is 0;
+                data.foreground = OS.GetTextColor (hDC);
+                data.background = OS.GetSysColor (OS.COLOR_3DFACE);
+            }
+            if (explorerTheme && selectionForeground is -1) {
+                int clrText = item.cellForeground !is null ? item.cellForeground [nmcd.iSubItem] : -1;
+                if (clrText is -1) clrText = item.foreground;
+                data.foreground = clrText !is -1 ? clrText : getForegroundPixel ();
+            }
+        } else {
+            int clrText = item.cellForeground !is null ? item.cellForeground [nmcd.iSubItem] : -1;
+            if (clrText is -1) clrText = item.foreground;
+            int clrTextBk = item.cellBackground !is null ? item.cellBackground [nmcd.iSubItem] : -1;
+            if (clrTextBk is -1) clrTextBk = item.background;
+            drawBackground = clrTextBk !is -1;
+            /*
+            * Bug in Windows.  When LVM_SETTEXTBKCOLOR or LVM_SETBKCOLOR
+            * is used to set the background color of the the text or the
+            * control, the color is not set in the HDC that is provided
+            * in Custom Draw.  The fix is to explicitly set the background
+            * color.
+            */
+            if (clrTextBk is -1) {
+                Control control = findBackgroundControl ();
+                if (control is null) control = this;
+                clrTextBk = control.getBackgroundPixel ();
+            }
+            data.foreground = clrText !is -1 ? clrText : OS.GetTextColor (hDC);
+            data.background = clrTextBk !is -1 ? clrTextBk : OS.GetBkColor (hDC);
+        }
+    } else {
+        data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
+        data.background = OS.GetSysColor (OS.COLOR_3DFACE);
+    }
+    data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+    int nSavedDC = OS.SaveDC (hDC);
+    GC gc = GC.win32_new (hDC, data);
+    RECT itemRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, true, false, false, hDC);
+    Event event = new Event ();
+    event.item = item;
+    event.gc = gc;
+    event.index = nmcd.iSubItem;
+    event.detail |= DWT.FOREGROUND;
+//  if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) {
+    if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) is nmcd.dwItemSpec) {
+        if (nmcd.iSubItem is 0 || (style & DWT.FULL_SELECTION) !is 0) {
+            if (handle is OS.GetFocus ()) {
+                int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+                if ((uiState & OS.UISF_HIDEFOCUS) is 0) event.detail |= DWT.FOCUSED;
+            }
+        }
+    }
+    if (drawHot) event.detail |= DWT.HOT;
+    if (drawSelected) event.detail |= DWT.SELECTED;
+    if (drawBackground) event.detail |= DWT.BACKGROUND;
+    event.x = itemRect.left;
+    event.y = itemRect.top;
+    event.width = itemRect.right - itemRect.left;
+    event.height = itemRect.bottom - itemRect.top;
+    RECT cellRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC);
+    int cellWidth = cellRect.right - cellRect.left;
+    int cellHeight = cellRect.bottom - cellRect.top;
+    gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight);
+    sendEvent (DWT.PaintItem, event);
+    event.gc = null;
+    gc.dispose ();
+    OS.RestoreDC (hDC, nSavedDC);
+}
+
+void setBackgroundImage (int hBitmap) {
+    super.setBackgroundImage (hBitmap);
+    if (!customDraw) setBackgroundTransparent (hBitmap !is 0);
+}
+
+void setBackgroundPixel (int newPixel) {
+    if (!customDraw) {
+        if (findImageControl () !is null) return;
+        if (newPixel is -1) newPixel = defaultBackground ();
+        int oldPixel = OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
+        if (oldPixel !is newPixel) {
+            OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel);
+            OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel);
+            if ((style & DWT.CHECK) !is 0) fixCheckboxImageListColor (true);
+        }
+    }
+    /*
+    * Feature in Windows.  When the background color is changed,
+    * the table does not redraw until the next WM_PAINT.  The fix
+    * is to force a redraw.
+    */
+    OS.InvalidateRect (handle, null, true);
+}
+
+void setBackgroundTransparent (bool transparent) {
+    /*
+    * Bug in Windows.  When the table has the extended style
+    * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
+    * CLR_NONE to make the table transparent, Windows draws
+    * a black rectangle around the first column.  The fix is
+    * clear LVS_EX_FULLROWSELECT.
+    *
+    * Feature in Windows.  When LVM_SETBKCOLOR is used with
+    * CLR_NONE and LVM_SETSELECTEDCOLUMN is used to select
+    * a column, Windows fills the column with the selection
+    * color, drawing on top of the background image and any
+    * other custom drawing.  The fix is to clear the selected
+    * column.
+    */
+    int oldPixel = OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
+    if (transparent) {
+        if (oldPixel !is OS.CLR_NONE) {
+            /*
+            * Bug in Windows.  When the background color is changed,
+            * the table does not redraw until the next WM_PAINT.  The
+            * fix is to force a redraw.
+            */
+            OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
+            OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, OS.CLR_NONE);
+            OS.InvalidateRect (handle, null, true);
+
+            /* Clear LVS_EX_FULLROWSELECT */
+            if (!explorerTheme && (style & DWT.FULL_SELECTION) !is 0) {
+                int bits = OS.LVS_EX_FULLROWSELECT;
+                OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
+            }
+
+            /* Clear LVM_SETSELECTEDCOLUMN */
+            if ((sortDirection & (DWT.UP | DWT.DOWN)) !is 0) {
+                if (sortColumn !is null && !sortColumn.isDisposed ()) {
+                    OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
+                    /*
+                    * Bug in Windows.  When LVM_SETSELECTEDCOLUMN is set, Windows
+                    * does not redraw either the new or the previous selected column.
+                    * The fix is to force a redraw.
+                    */
+                    OS.InvalidateRect (handle, null, true);
+                }
+            }
+        }
+    } else {
+        if (oldPixel is OS.CLR_NONE) {
+            Control control = findBackgroundControl ();
+            if (control is null) control = this;
+            if (control.backgroundImage is null) {
+                setBackgroundPixel (control.getBackgroundPixel ());
+            }
+
+            /* Set LVS_EX_FULLROWSELECT */
+            if (!explorerTheme && (style & DWT.FULL_SELECTION) !is 0) {
+                if (!hooks (DWT.EraseItem) && !hooks (DWT.PaintItem)) {
+                    int bits = OS.LVS_EX_FULLROWSELECT;
+                    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
+                }
+            }
+
+            /* Set LVM_SETSELECTEDCOLUMN */
+            if ((sortDirection & (DWT.UP | DWT.DOWN)) !is 0) {
+                if (sortColumn !is null && !sortColumn.isDisposed ()) {
+                    int column = indexOf (sortColumn);
+                    if (column !is -1) {
+                        OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, column, 0);
+                        /*
+                        * Bug in Windows.  When LVM_SETSELECTEDCOLUMN is set, Windows
+                        * does not redraw either the new or the previous selected column.
+                        * The fix is to force a redraw.
+                        */
+                        OS.InvalidateRect (handle, null, true);
+                    }
+                }
+            }
+        }
+    }
+}
+
+void setBounds (int x, int y, int width, int height, int flags, bool defer) {
+    /*
+    * Bug in Windows.  If the table column widths are adjusted
+    * in WM_SIZE or WM_POSITIONCHANGED using LVM_SETCOLUMNWIDTH
+    * blank lines may be inserted at the top of the table.  A
+    * call to LVM_GETTOPINDEX will return a negative number (this
+    * is an impossible result).  Once the blank lines appear,
+    * there seems to be no way to get rid of them, other than
+    * destroying and recreating the table.  The fix is to send
+    * the resize notification after the size has been changed in
+    * the operating system.
+    *
+    * NOTE:  This does not fix the case when the user is resizing
+    * columns dynamically.  There is no fix for this case at this
+    * time.
+    */
+    setDeferResize (true);
+    super.setBounds (x, y, width, height, flags, false);
+    setDeferResize (false);
+}
+
+/**
+ * Sets the order that the items in the receiver should
+ * be displayed in to the given argument which is described
+ * in terms of the zero-relative ordering of when the items
+ * were added.
+ *
+ * @param order the new order to display the items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
+ * </ul>
+ *
+ * @see Table#getColumnOrder()
+ * @see TableColumn#getMoveable()
+ * @see TableColumn#setMoveable(bool)
+ * @see DWT#Move
+ *
+ * @since 3.1
+ */
+public void setColumnOrder (int [] order) {
+    checkWidget ();
+    if (order is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    if (columnCount is 0) {
+        if (order.length !is 0) error (DWT.ERROR_INVALID_ARGUMENT);
+        return;
+    }
+    if (order.length !is columnCount) error (DWT.ERROR_INVALID_ARGUMENT);
+    int [] oldOrder = new int [columnCount];
+    OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
+    bool reorder = false;
+    bool [] seen = new bool [columnCount];
+    for (int i=0; i<order.length; i++) {
+        int index = order [i];
+        if (index < 0 || index >= columnCount) error (DWT.ERROR_INVALID_RANGE);
+        if (seen [index]) error (DWT.ERROR_INVALID_ARGUMENT);
+        seen [index] = true;
+        if (index !is oldOrder [i]) reorder = true;
+    }
+    if (reorder) {
+        RECT [] oldRects = new RECT [columnCount];
+        for (int i=0; i<columnCount; i++) {
+            oldRects [i] = new RECT ();
+            OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, oldRects [i]);
+        }
+        OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, order.length, order);
+        /*
+        * Bug in Windows.  When LVM_SETCOLUMNORDERARRAY is used to change
+        * the column order, the header redraws correctly but the table does
+        * not.  The fix is to force a redraw.
+        */
+        OS.InvalidateRect (handle, null, true);
+        TableColumn[] newColumns = new TableColumn [columnCount];
+        System.arraycopy (columns, 0, newColumns, 0, columnCount);
+        RECT newRect = new RECT ();
+        for (int i=0; i<columnCount; i++) {
+            TableColumn column = newColumns [i];
+            if (!column.isDisposed ()) {
+                OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, newRect);
+                if (newRect.left !is oldRects [i].left) {
+                    column.updateToolTip (i);
+                    column.sendEvent (DWT.Move);
+                }
+            }
+        }
+    }
+}
+
+void setCustomDraw (bool customDraw) {
+    if (this.customDraw is customDraw) return;
+    if (!this.customDraw && customDraw && currentItem !is null) {
+        OS.InvalidateRect (handle, null, true);
+    }
+    this.customDraw = customDraw;
+}
+
+void setDeferResize (bool defer) {
+    if (defer) {
+        if (resizeCount++ is 0) {
+            wasResized = false;
+            if (hooks (DWT.MeasureItem) || hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) {
+                if (drawCount++ is 0 && OS.IsWindowVisible (handle)) {
+                    OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+                }
+            }
+        }
+    } else {
+        if (--resizeCount is 0) {
+            if (hooks (DWT.MeasureItem) || hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) {
+                if (--drawCount is 0 /*&& OS.IsWindowVisible (handle)*/) {
+                    OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+                    if (OS.IsWinCE) {
+                        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                        if (hwndHeader !is 0) OS.InvalidateRect (hwndHeader, null, true);
+                        OS.InvalidateRect (handle, null, true);
+                    } else {
+                        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+                        OS.RedrawWindow (handle, null, 0, flags);
+                    }
+                }
+            }
+            if (wasResized) {
+                wasResized = false;
+                setResizeChildren (false);
+                sendEvent (DWT.Resize);
+                if (isDisposed ()) return;
+                if (layout !is null) {
+                    markLayout (false, false);
+                    updateLayout (false, false);
+                }
+                setResizeChildren (true);
+            }
+        }
+    }
+}
+
+void setCheckboxImageList (int width, int height, bool fixScroll) {
+    if ((style & DWT.CHECK) is 0) return;
+    int count = 4, flags = 0;
+    if (OS.IsWinCE) {
+        flags |= OS.ILC_COLOR;
+    } else {
+        int hDC = OS.GetDC (handle);
+        int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL);
+        int planes = OS.GetDeviceCaps (hDC, OS.PLANES);
+        OS.ReleaseDC (handle, hDC);
+        int depth = bits * planes;
+        switch (depth) {
+            case 4: flags |= OS.ILC_COLOR4; break;
+            case 8: flags |= OS.ILC_COLOR8; break;
+            case 16: flags |= OS.ILC_COLOR16; break;
+            case 24: flags |= OS.ILC_COLOR24; break;
+            case 32: flags |= OS.ILC_COLOR32; break;
+            default: flags |= OS.ILC_COLOR; break;
+        }
+    }
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) flags |= OS.ILC_MIRROR;
+    if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) flags |= OS.ILC_MASK;
+    int hStateList = OS.ImageList_Create (width, height, flags, count, count);
+    int hDC = OS.GetDC (handle);
+    int memDC = OS.CreateCompatibleDC (hDC);
+    int hBitmap = OS.CreateCompatibleBitmap (hDC, width * count, height);
+    int hOldBitmap = OS.SelectObject (memDC, hBitmap);
+    RECT rect = new RECT ();
+    OS.SetRect (rect, 0, 0, width * count, height);
+    int clrBackground;
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        Control control = findBackgroundControl ();
+        if (control is null) control = this;
+        clrBackground = control.getBackgroundPixel ();
+    } else {
+        clrBackground = 0x020000FF;
+        if ((clrBackground & 0xFFFFFF) is OS.GetSysColor (OS.COLOR_WINDOW)) {
+            clrBackground = 0x0200FF00;
+        }
+    }
+    int hBrush = OS.CreateSolidBrush (clrBackground);
+    OS.FillRect (memDC, rect, hBrush);
+    OS.DeleteObject (hBrush);
+    int oldFont = OS.SelectObject (hDC, defaultFont ());
+    TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+    OS.GetTextMetrics (hDC, tm);
+    OS.SelectObject (hDC, oldFont);
+    int itemWidth = Math.min (tm.tmHeight, width);
+    int itemHeight = Math.min (tm.tmHeight, height);
+    int left = (width - itemWidth) / 2, top = (height - itemHeight) / 2 + 1;
+    OS.SetRect (rect, left, top, left + itemWidth, top + itemHeight);
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        int hTheme = display.hButtonTheme ();
+        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
+        rect.left += width;  rect.right += width;
+        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_CHECKEDNORMAL, rect, null);
+        rect.left += width;  rect.right += width;
+        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
+        rect.left += width;  rect.right += width;
+        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_MIXEDNORMAL, rect, null);
+    } else {
+        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_FLAT);
+        rect.left += width;  rect.right += width;
+        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_FLAT);
+        rect.left += width;  rect.right += width;
+        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
+        rect.left += width;  rect.right += width;
+        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
+    }
+    OS.SelectObject (memDC, hOldBitmap);
+    OS.DeleteDC (memDC);
+    OS.ReleaseDC (handle, hDC);
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        OS.ImageList_Add (hStateList, hBitmap, 0);
+    } else {
+        OS.ImageList_AddMasked (hStateList, hBitmap, clrBackground);
+    }
+    OS.DeleteObject (hBitmap);
+    /*
+    * Bug in Windows.  Making any change to an item that
+    * changes the item height of a table while the table
+    * is scrolled can cause the lines to draw incorrectly.
+    * This happens even when the lines are not currently
+    * visible and are shown afterwards.  The fix is to
+    * save the top index, scroll to the top of the table
+    * and then restore the original top index.
+    */
+    int topIndex = getTopIndex ();
+    if (fixScroll && topIndex !is 0) {
+        setRedraw (false);
+        setTopIndex (0);
+    }
+    int hOldStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
+    OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_STATE, hStateList);
+    if (hOldStateList !is 0) OS.ImageList_Destroy (hOldStateList);
+    /*
+    * Bug in Windows.  Setting the LVSIL_STATE state image list
+    * when the table already has a LVSIL_SMALL image list causes
+    * pixel corruption of the images.  The fix is to reset the
+    * LVSIL_SMALL image list.
+    */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        int hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
+        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
+    }
+    if (fixScroll && topIndex !is 0) {
+        setTopIndex (topIndex);
+        setRedraw (true);
+    }
+}
+
+void setFocusIndex (int index) {
+//  checkWidget ();
+    /*
+    * An index of -1 will apply the change to all
+    * items.  Ensure that index is greater than -1.
+    */
+    if (index < 0) return;
+    LVITEM lvItem = new LVITEM ();
+    lvItem.state = OS.LVIS_FOCUSED;
+    lvItem.stateMask = OS.LVIS_FOCUSED;
+    ignoreSelect = true;
+    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
+    ignoreSelect = false;
+    OS.SendMessage (handle, OS.LVM_SETSELECTIONMARK, 0, index);
+}
+
+public void setFont (Font font) {
+    checkWidget ();
+    /*
+    * Bug in Windows.  Making any change to an item that
+    * changes the item height of a table while the table
+    * is scrolled can cause the lines to draw incorrectly.
+    * This happens even when the lines are not currently
+    * visible and are shown afterwards.  The fix is to
+    * save the top index, scroll to the top of the table
+    * and then restore the original top index.
+    */
+    int topIndex = getTopIndex ();
+    if (topIndex !is 0) {
+        setRedraw (false);
+        setTopIndex (0);
+    }
+    if (itemHeight !is -1) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.LVS_OWNERDRAWFIXED);
+    }
+    super.setFont (font);
+    if (itemHeight !is -1) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits & ~OS.LVS_OWNERDRAWFIXED);
+    }
+    setScrollWidth (null, true);
+    if (topIndex !is 0) {
+        setTopIndex (topIndex);
+        setRedraw (true);
+    }
+
+    /*
+    * Bug in Windows.  Setting the font will cause the table
+    * to be redrawn but not the column headers.  The fix is
+    * to force a redraw of the column headers.
+    */
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    OS.InvalidateRect (hwndHeader, null, true);
+}
+
+void setForegroundPixel (int pixel) {
+    /*
+    * The Windows table control uses CLR_DEFAULT to indicate
+    * that it is using the default foreground color.  This
+    * is undocumented.
+    */
+    if (pixel is -1) pixel = OS.CLR_DEFAULT;
+    OS.SendMessage (handle, OS.LVM_SETTEXTCOLOR, 0, pixel);
+
+    /*
+    * Feature in Windows.  When the foreground color is
+    * changed, the table does not redraw until the next
+    * WM_PAINT.  The fix is to force a redraw.
+    */
+    OS.InvalidateRect (handle, null, true);
+}
+
+/**
+ * Marks the receiver's header as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param show the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setHeaderVisible (bool show) {
+    checkWidget ();
+    int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    newBits &= ~OS.LVS_NOCOLUMNHEADER;
+    if (!show) newBits |= OS.LVS_NOCOLUMNHEADER;
+    /*
+    * Feature in Windows.  Setting or clearing LVS_NOCOLUMNHEADER
+    * causes the table to scroll to the beginning.  The fix is to
+    * save and restore the top index causing the table to scroll
+    * to the new location.
+    */
+    int oldIndex = getTopIndex ();
+    OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+
+    /*
+    * Bug in Windows.  Making any change to an item that
+    * changes the item height of a table while the table
+    * is scrolled can cause the lines to draw incorrectly.
+    * This happens even when the lines are not currently
+    * visible and are shown afterwards.  The fix is to
+    * save the top index, scroll to the top of the table
+    * and then restore the original top index.
+    */
+    int newIndex = getTopIndex ();
+    if (newIndex !is 0) {
+        setRedraw (false);
+        setTopIndex (0);
+    }
+    if (show) {
+        int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+        if ((bits & OS.LVS_EX_GRIDLINES) !is 0) fixItemHeight (false);
+    }
+    setTopIndex (oldIndex);
+    if (newIndex !is 0) {
+        setRedraw (true);
+    }
+    updateHeaderToolTips ();
+}
+
+/**
+ * Sets the number of items contained in the receiver.
+ *
+ * @param count the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setItemCount (int count) {
+    checkWidget ();
+    count = Math.max (0, count);
+    int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (count is itemCount) return;
+    setDeferResize (true);
+    bool isVirtual = (style & DWT.VIRTUAL) !is 0;
+    if (!isVirtual) setRedraw (false);
+    int index = count;
+    while (index < itemCount) {
+        TableItem item = items [index];
+        if (item !is null && !item.isDisposed ()) item.release (false);
+        if (!isVirtual) {
+            ignoreSelect = ignoreShrink = true;
+            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, count, 0);
+            ignoreSelect = ignoreShrink = false;
+            if (code is 0) break;
+        }
+        index++;
+    }
+    if (index < itemCount) error (DWT.ERROR_ITEM_NOT_REMOVED);
+    int length = Math.max (4, (count + 3) / 4 * 4);
+    TableItem [] newItems = new TableItem [length];
+    System.arraycopy (items, 0, newItems, 0, Math.min (count, itemCount));
+    items = newItems;
+    if (isVirtual) {
+        int flags = OS.LVSICF_NOINVALIDATEALL | OS.LVSICF_NOSCROLL;
+        OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, count, flags);
+        /*
+        * Bug in Windows.  When a virtual table contains items and
+        * LVM_SETITEMCOUNT is used to set the new item count to zero,
+        * Windows does not redraw the table.  Note that simply not
+        * specifying LVSICF_NOINVALIDATEALL or LVSICF_NOSCROLL does
+        * correct the problem.  The fix is to force a redraw.
+        */
+        if (count is 0 && itemCount !is 0) {
+            OS.InvalidateRect (handle, null, true);
+        }
+    } else {
+        for (int i=itemCount; i<count; i++) {
+            items [i] = new TableItem (this, DWT.NONE, i, true);
+        }
+    }
+    if (!isVirtual) setRedraw (true);
+    if (itemCount is 0) setScrollWidth (null, false);
+    setDeferResize (false);
+}
+
+void setItemHeight (bool fixScroll) {
+    /*
+    * Bug in Windows.  Making any change to an item that
+    * changes the item height of a table while the table
+    * is scrolled can cause the lines to draw incorrectly.
+    * This happens even when the lines are not currently
+    * visible and are shown afterwards.  The fix is to
+    * save the top index, scroll to the top of the table
+    * and then restore the original top index.
+    */
+    int topIndex = getTopIndex ();
+    if (fixScroll && topIndex !is 0) {
+        setRedraw (false);
+        setTopIndex (0);
+    }
+    if (itemHeight is -1) {
+        /*
+        * Feature in Windows.  Windows has no API to restore the
+        * defualt item height for a table.  The fix is to use
+        * WM_SETFONT which recomputes and assigns the default item
+        * height.
+        */
+        int hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
+    } else {
+        /*
+        * Feature in Windows.  Window has no API to set the item
+        * height for a table.  The fix is to set temporarily set
+        * LVS_OWNERDRAWFIXED then resize the table, causing a
+        * WM_MEASUREITEM to be sent, then clear LVS_OWNERDRAWFIXED.
+        */
+        forceResize ();
+        RECT rect = new RECT ();
+        OS.GetWindowRect (handle, rect);
+        int width = rect.right - rect.left, height = rect.bottom - rect.top;
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.LVS_OWNERDRAWFIXED);
+        int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER;
+        ignoreResize = true;
+        SetWindowPos (handle, 0 , 0, 0, width, height + 1, flags);
+        SetWindowPos (handle, 0 , 0, 0, width, height, flags);
+        ignoreResize = false;
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+    }
+    if (fixScroll && topIndex !is 0) {
+        setTopIndex (topIndex);
+        setRedraw (true);
+    }
+}
+
+/**
+ * Sets the height of the area which would be used to
+ * display <em>one</em> of the items in the table.
+ *
+ * @param itemHeight the height of one item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+/*public*/ void setItemHeight (int itemHeight) {
+    checkWidget ();
+    if (itemHeight < -1) error (DWT.ERROR_INVALID_ARGUMENT);
+    this.itemHeight = itemHeight;
+    setItemHeight (true);
+    setScrollWidth (null, true);
+}
+
+/**
+ * Marks the receiver's lines as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param show the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLinesVisible (bool show) {
+    checkWidget ();
+    int newBits = show  ? OS.LVS_EX_GRIDLINES : 0;
+    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_GRIDLINES, newBits);
+    if (show) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if ((bits & OS.LVS_NOCOLUMNHEADER) is 0) fixItemHeight (true);
+    }
+}
+
+public void setRedraw (bool redraw) {
+    checkWidget ();
+    /*
+     * Feature in Windows.  When WM_SETREDRAW is used to turn
+     * off drawing in a widget, it clears the WS_VISIBLE bits
+     * and then sets them when redraw is turned back on.  This
+     * means that WM_SETREDRAW will make a widget unexpectedly
+     * visible.  The fix is to track the visibility state while
+     * drawing is turned off and restore it when drawing is turned
+     * back on.
+     */
+    if (drawCount is 0) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if ((bits & OS.WS_VISIBLE) is 0) state |= HIDDEN;
+    }
+    if (redraw) {
+        if (--drawCount is 0) {
+            /*
+            * When many items are added to a table, it is faster to
+            * temporarily unsubclass the window proc so that messages
+            * are dispatched directly to the table.
+            *
+            * NOTE: This is optimization somewhat dangerous because any
+            * operation can occur when redraw is turned off, even operations
+            * where the table must be subclassed in order to have the correct
+            * behavior or work around a Windows bug.
+            *
+            * This code is intentionally commented.
+            */
+//          subclass ();
+
+            /* Set the width of the horizontal scroll bar */
+            setScrollWidth (null, true);
+
+            /*
+            * Bug in Windows.  For some reason, when WM_SETREDRAW is used
+            * to turn redraw back on this may result in a WM_SIZE.  If the
+            * table column widths are adjusted in WM_SIZE, blank lines may
+            * be inserted at the top of the widget.  A call to LVM_GETTOPINDEX
+            * will return a negative number (this is an impossible result).
+            * The fix is to send the resize notification after the size has
+            * been changed in the operating system.
+            */
+            setDeferResize (true);
+            OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+            if (hwndHeader !is 0) OS.SendMessage (hwndHeader, OS.WM_SETREDRAW, 1, 0);
+            if ((state & HIDDEN) !is 0) {
+                state &= ~HIDDEN;
+                OS.ShowWindow (handle, OS.SW_HIDE);
+            } else {
+                if (OS.IsWinCE) {
+                    OS.InvalidateRect (handle, null, false);
+                    if (hwndHeader !is 0) {
+                        OS.InvalidateRect (hwndHeader, null, false);
+                    }
+                } else {
+                    int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+                    OS.RedrawWindow (handle, null, 0, flags);
+                }
+            }
+            setDeferResize (false);
+        }
+    } else {
+        if (drawCount++ is 0) {
+            OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+            if (hwndHeader !is 0) OS.SendMessage (hwndHeader, OS.WM_SETREDRAW, 0, 0);
+
+            /*
+            * When many items are added to a table, it is faster to
+            * temporarily unsubclass the window proc so that messages
+            * are dispatched directly to the table.
+            *
+            * NOTE: This is optimization somewhat dangerous because any
+            * operation can occur when redraw is turned off, even operations
+            * where the table must be subclassed in order to have the correct
+            * behavior or work around a Windows bug.
+            *
+            * This code is intentionally commented.
+            */
+//          unsubclass ();
+        }
+    }
+}
+
+bool setScrollWidth (TableItem item, bool force) {
+    if (currentItem !is null) {
+        if (currentItem !is item) fixScrollWidth = true;
+        return false;
+    }
+    if (!force && (drawCount !is 0 || !OS.IsWindowVisible (handle))) {
+        fixScrollWidth = true;
+        return false;
+    }
+    fixScrollWidth = false;
+    /*
+    * NOTE: It is much faster to measure the strings and compute the
+    * width of the scroll bar in non-virtual table rather than using
+    * LVM_SETCOLUMNWIDTH with LVSCW_AUTOSIZE.
+    */
+    if (columnCount is 0) {
+        int newWidth = 0, imageIndent = 0, index = 0;
+        int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+        while (index < itemCount) {
+            String string = null;
+            int font = -1;
+            if (item !is null) {
+                string = item.text;
+                imageIndent = Math.max (imageIndent, item.imageIndent);
+                if (item.cellFont !is null) font = item.cellFont [0];
+                if (font is -1) font = item.font;
+            } else {
+                if (items [index] !is null) {
+                    TableItem tableItem = items [index];
+                    string = tableItem.text;
+                    imageIndent = Math.max (imageIndent, tableItem.imageIndent);
+                    if (tableItem.cellFont !is null) font = tableItem.cellFont [0];
+                    if (font is -1) font = tableItem.font;
+                }
+            }
+            if (string !is null && string.length () !is 0) {
+                if (font !is -1) {
+                    int hDC = OS.GetDC (handle);
+                    int oldFont = OS.SelectObject (hDC, font);
+                    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+                    TCHAR buffer = new TCHAR (getCodePage (), string, false);
+                    RECT rect = new RECT ();
+                    OS.DrawText (hDC, buffer, buffer.length (), rect, flags);
+                    OS.SelectObject (hDC, oldFont);
+                    OS.ReleaseDC (handle, hDC);
+                    newWidth = Math.max (newWidth, rect.right - rect.left);
+                } else {
+                    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+                    newWidth = Math.max (newWidth, OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer));
+                }
+            }
+            if (item !is null) break;
+            index++;
+        }
+        /*
+        * Bug in Windows.  When the width of the first column is
+        * small but not zero, Windows draws '...' outside of the
+        * bounds of the text.  This is strange, but only causes
+        * problems when the item is selected.  In this case, Windows
+        * clears the '...' but doesn't redraw it when the item is
+        * deselected, causing pixel corruption.  The fix is to ensure
+        * that the column is at least wide enough to draw a single
+        * space.
+        */
+        if (newWidth is 0) {
+            TCHAR buffer = new TCHAR (getCodePage (), " ", true);
+            newWidth = Math.max (newWidth, OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer));
+        }
+        int hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
+        if (hStateList !is 0) {
+            int [] cx = new int [1], cy = new int [1];
+            OS.ImageList_GetIconSize (hStateList, cx, cy);
+            newWidth += cx [0] + INSET;
+        }
+        int hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
+        if (hImageList !is 0) {
+            int [] cx = new int [1], cy = new int [1];
+            OS.ImageList_GetIconSize (hImageList, cx, cy);
+            newWidth += (imageIndent + 1) * cx [0];
+        } else {
+            /*
+            * Bug in Windows.  When LVM_SETIMAGELIST is used to remove the
+            * image list by setting it to NULL, the item width and height
+            * is not changed and space is reserved for icons despite the
+            * fact that there are none.  The fix is to set the image list
+            * to be very small before setting it to NULL.  This causes
+            * Windows to reserve the smallest possible space when an image
+            * list is removed.  In this case, the scroll width must be one
+            * pixel larger.
+            */
+            newWidth++;
+        }
+        newWidth += INSET * 2;
+        int oldWidth = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+            newWidth += VISTA_EXTRA;
+        }
+        if (newWidth > oldWidth) {
+            /*
+            * Feature in Windows.  When LVM_SETCOLUMNWIDTH is sent,
+            * Windows draws right away instead of queuing a WM_PAINT.
+            * This can cause recursive calls when called from paint
+            * or from messages that are retrieving the item data,
+            * such as WM_NOTIFY, causing a stack overflow.  The fix
+            * is to turn off redraw and queue a repaint, collapsing
+            * the recursive calls.
+            */
+            bool redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+            if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+            OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, newWidth);
+            if (redraw) {
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+                if (OS.IsWinCE) {
+                    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                    if (hwndHeader !is 0) OS.InvalidateRect (hwndHeader, null, true);
+                    OS.InvalidateRect (handle, null, true);
+                } else {
+                    int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+                    OS.RedrawWindow (handle, null, 0, flags);
+                }
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * Selects the items at the given zero-relative indices in the receiver.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Indices that are out of range and duplicate indices are ignored.
+ * If the receiver is single-select and multiple indices are specified,
+ * then all indices are ignored.
+ * </p>
+ *
+ * @param indices the indices of the items to select
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#deselectAll()
+ * @see Table#select(int[])
+ */
+public void setSelection (int [] indices) {
+    checkWidget ();
+    if (indices is null) error (DWT.ERROR_NULL_ARGUMENT);
+    deselectAll ();
+    int length = indices.length;
+    if (length is 0 || ((style & DWT.SINGLE) !is 0 && length > 1)) return;
+    select (indices);
+    int focusIndex = indices [0];
+    if (focusIndex !is -1) setFocusIndex (focusIndex);
+    showSelection ();
+}
+
+/**
+ * Sets the receiver's selection to the given item.
+ * The current selection is cleared before the new item is selected.
+ * <p>
+ * If the item is not in the receiver, then it is ignored.
+ * </p>
+ *
+ * @param item the item to select
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSelection (TableItem  item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setSelection (new TableItem [] {item});
+}
+
+/**
+ * Sets the receiver's selection to be the given array of items.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Items that are not in the receiver are ignored.
+ * If the receiver is single-select and multiple items are specified,
+ * then all items are ignored.
+ * </p>
+ *
+ * @param items the array of items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#deselectAll()
+ * @see Table#select(int[])
+ * @see Table#setSelection(int[])
+ */
+public void setSelection (TableItem [] items) {
+    checkWidget ();
+    if (items is null) error (DWT.ERROR_NULL_ARGUMENT);
+    deselectAll ();
+    int length = items.length;
+    if (length is 0 || ((style & DWT.SINGLE) !is 0 && length > 1)) return;
+    int focusIndex = -1;
+    for (int i=length-1; i>=0; --i) {
+        int index = indexOf (items [i]);
+        if (index !is -1) {
+            select (focusIndex = index);
+        }
+    }
+    if (focusIndex !is -1) setFocusIndex (focusIndex);
+    showSelection ();
+}
+
+/**
+ * Selects the item at the given zero-relative index in the receiver.
+ * The current selection is first cleared, then the new item is selected.
+ *
+ * @param index the index of the item to select
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#deselectAll()
+ * @see Table#select(int)
+ */
+public void setSelection (int index) {
+    checkWidget ();
+    deselectAll ();
+    select (index);
+    if (index !is -1) setFocusIndex (index);
+    showSelection ();
+}
+
+/**
+ * Selects the items in the range specified by the given zero-relative
+ * indices in the receiver. The range of indices is inclusive.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Indices that are out of range are ignored and no items will be selected
+ * if start is greater than end.
+ * If the receiver is single-select and there is more than one item in the
+ * given range, then all indices are ignored.
+ * </p>
+ *
+ * @param start the start index of the items to select
+ * @param end the end index of the items to select
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#deselectAll()
+ * @see Table#select(int,int)
+ */
+public void setSelection (int start, int end) {
+    checkWidget ();
+    deselectAll ();
+    if (end < 0 || start > end || ((style & DWT.SINGLE) !is 0 && start !is end)) return;
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (count is 0 || start >= count) return;
+    start = Math.max (0, start);
+    end = Math.min (end, count - 1);
+    select (start, end);
+    setFocusIndex (start);
+    showSelection ();
+}
+
+/**
+ * Sets the column used by the sort indicator for the receiver. A null
+ * value will clear the sort indicator.  The current sort column is cleared
+ * before the new column is set.
+ *
+ * @param column the column used by the sort indicator or <code>null</code>
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSortColumn (TableColumn column) {
+    checkWidget ();
+    if (column !is null && column.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+    if (sortColumn !is null && !sortColumn.isDisposed ()) {
+        sortColumn.setSortDirection (DWT.NONE);
+    }
+    sortColumn = column;
+    if (sortColumn !is null && sortDirection !is DWT.NONE) {
+        sortColumn.setSortDirection (sortDirection);
+    }
+}
+
+/**
+ * Sets the direction of the sort indicator for the receiver. The value
+ * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>.
+ *
+ * @param direction the direction of the sort indicator
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSortDirection (int direction) {
+    checkWidget ();
+    if ((direction & (DWT.UP | DWT.DOWN)) is 0 && direction !is DWT.NONE) return;
+    sortDirection = direction;
+    if (sortColumn !is null && !sortColumn.isDisposed ()) {
+        sortColumn.setSortDirection (direction);
+    }
+}
+
+void setSubImagesVisible (bool visible) {
+    int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+    if ((dwExStyle & OS.LVS_EX_SUBITEMIMAGES) !is 0 is visible) return;
+    int bits = visible ? OS.LVS_EX_SUBITEMIMAGES : 0;
+    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_SUBITEMIMAGES, bits);
+}
+
+void setTableEmpty () {
+    if (imageList !is null) {
+        /*
+        * Bug in Windows.  When LVM_SETIMAGELIST is used to remove the
+        * image list by setting it to NULL, the item width and height
+        * is not changed and space is reserved for icons despite the
+        * fact that there are none.  The fix is to set the image list
+        * to be very small before setting it to NULL.  This causes
+        * Windows to reserve the smallest possible space when an image
+        * list is removed.
+        */
+        int hImageList = OS.ImageList_Create (1, 1, 0, 0, 0);
+        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
+        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0);
+        if (headerImageList !is null) {
+            int hHeaderImageList = headerImageList.getHandle ();
+            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+            OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
+        }
+        OS.ImageList_Destroy (hImageList);
+        display.releaseImageList (imageList);
+        imageList = null;
+        if (itemHeight !is -1) setItemHeight (false);
+    }
+    if (!hooks (DWT.MeasureItem) && !hooks (DWT.EraseItem) && !hooks (DWT.PaintItem)) {
+        Control control = findBackgroundControl ();
+        if (control is null) control = this;
+        if (control.backgroundImage is null) {
+            setCustomDraw (false);
+            setBackgroundTransparent (false);
+            if (OS.COMCTL32_MAJOR < 6) style &= ~DWT.DOUBLE_BUFFERED;
+        }
+    }
+    items = new TableItem [4];
+    if (columnCount is 0) {
+        OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0);
+        setScrollWidth (null, false);
+    }
+}
+
+/**
+ * Sets the zero-relative index of the item which is currently
+ * at the top of the receiver. This index can change when items
+ * are scrolled or new items are added and removed.
+ *
+ * @param index the index of the top item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setTopIndex (int index) {
+    checkWidget ();
+    int topIndex = OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
+    if (index is topIndex) return;
+
+    /*
+    * Bug in Windows.  For some reason, LVM_SCROLL refuses to
+    * scroll a table vertically when the width and height of
+    * the table is smaller than a certain size.  The values
+    * that seem to cause the problem are width=68 and height=6
+    * but there is no guarantee that these values cause the
+    * failure on different machines or on different versions
+    * of Windows.  It may depend on the font and any number
+    * of other factors.  For example, setting the font to
+    * anything but the default sometimes fixes the problem.
+    * The fix is to use LVM_GETCOUNTPERPAGE to detect the
+    * case when the number of visible items is zero and
+    * use LVM_ENSUREVISIBLE to scroll the table to make the
+    * index visible.
+    */
+
+    /*
+    * Bug in Windows.  When the table header is visible and
+    * there is not enough space to show a single table item,
+    * LVM_GETCOUNTPERPAGE can return a negative number instead
+    * of zero.  The fix is to test for negative or zero.
+    */
+    if (OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0) <= 0) {
+        /*
+        * Bug in Windows.  For some reason, LVM_ENSUREVISIBLE can
+        * scroll one item more or one item less when there is not
+        * enough space to show a single table item.  The fix is
+        * to detect the case and call LVM_ENSUREVISIBLE again with
+        * the same arguments.  It seems that once LVM_ENSUREVISIBLE
+        * has scrolled into the general area, it is able to scroll
+        * to the exact item.
+        */
+        OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
+        if (index !is OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
+            OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
+        }
+        return;
+    }
+
+    /* Use LVM_SCROLL to scroll the table */
+    RECT rect = new RECT ();
+    rect.left = OS.LVIR_BOUNDS;
+    ignoreCustomDraw = true;
+    OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect);
+    ignoreCustomDraw = false;
+    int dy = (index - topIndex) * (rect.bottom - rect.top);
+    OS.SendMessage (handle, OS.LVM_SCROLL, 0, dy);
+}
+
+/**
+ * Shows the column.  If the column is already showing in the receiver,
+ * this method simply returns.  Otherwise, the columns are scrolled until
+ * the column is visible.
+ *
+ * @param column the column to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the column is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the column has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void showColumn (TableColumn column) {
+    checkWidget ();
+    if (column is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (column.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    if (column.parent !is this) return;
+    int index = indexOf (column);
+    if (!(0 <= index && index < columnCount)) return;
+    /*
+    * Feature in Windows.  Calling LVM_GETSUBITEMRECT with -1 for the
+    * row number gives the bounds of the item that would be above the
+    * first row in the table.  This is undocumented and does not work
+    * for the first column. In this case, to get the bounds of the
+    * first column, get the bounds of the second column and subtract
+    * the width of the first. The left edge of the second column is
+    * also used as the right edge of the first.
+    */
+    RECT itemRect = new RECT ();
+    itemRect.left = OS.LVIR_BOUNDS;
+    if (index is 0) {
+        itemRect.top = 1;
+        ignoreCustomDraw = true;
+        OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, -1, itemRect);
+        ignoreCustomDraw = false;
+        itemRect.right = itemRect.left;
+        int width = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
+        itemRect.left = itemRect.right - width;
+    } else {
+        itemRect.top = index;
+        ignoreCustomDraw = true;
+        OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, -1, itemRect);
+        ignoreCustomDraw = false;
+    }
+    /*
+    * Bug in Windows.  When a table that is drawing grid lines
+    * is slowly scrolled horizontally to the left, the table does
+    * not redraw the newly exposed vertical grid lines.  The fix
+    * is to save the old scroll position, call the window proc,
+    * get the new scroll position and redraw the new area.
+    */
+    int oldPos = 0;
+    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+    if ((bits & OS.LVS_EX_GRIDLINES) !is 0) {
+        SCROLLINFO info = new SCROLLINFO ();
+        info.cbSize = SCROLLINFO.sizeof;
+        info.fMask = OS.SIF_POS;
+        OS.GetScrollInfo (handle, OS.SB_HORZ, info);
+        oldPos = info.nPos;
+    }
+    RECT rect = new RECT ();
+    OS.GetClientRect (handle, rect);
+    if (itemRect.left < rect.left) {
+        int dx = itemRect.left - rect.left;
+        OS.SendMessage (handle, OS.LVM_SCROLL, dx, 0);
+    } else {
+        int width = Math.min (rect.right - rect.left, itemRect.right - itemRect.left);
+        if (itemRect.left + width > rect.right) {
+            int dx = itemRect.left + width - rect.right;
+            OS.SendMessage (handle, OS.LVM_SCROLL, dx, 0);
+        }
+    }
+    /*
+    * Bug in Windows.  When a table that is drawing grid lines
+    * is slowly scrolled horizontally to the left, the table does
+    * not redraw the newly exposed vertical grid lines.  The fix
+    * is to save the old scroll position, call the window proc,
+    * get the new scroll position and redraw the new area.
+    */
+    if ((bits & OS.LVS_EX_GRIDLINES) !is 0) {
+        SCROLLINFO info = new SCROLLINFO ();
+        info.cbSize = SCROLLINFO.sizeof;
+        info.fMask = OS.SIF_POS;
+        OS.GetScrollInfo (handle, OS.SB_HORZ, info);
+        int newPos = info.nPos;
+        if (newPos < oldPos) {
+            rect.right = oldPos - newPos + GRID_WIDTH;
+            OS.InvalidateRect (handle, rect, true);
+        }
+    }
+}
+
+void showItem (int index) {
+    /*
+    * Bug in Windows.  For some reason, when there is insufficient space
+    * to show an item, LVM_ENSUREVISIBLE causes blank lines to be
+    * inserted at the top of the widget.  A call to LVM_GETTOPINDEX will
+    * return a negative number (this is an impossible result).  The fix
+    * is to use LVM_GETCOUNTPERPAGE to detect the case when the number
+    * of visible items is zero and use LVM_ENSUREVISIBLE with the
+    * fPartialOK flag set to true to scroll the table.
+    */
+    if (OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0) <= 0) {
+        /*
+        * Bug in Windows.  For some reason, LVM_ENSUREVISIBLE can
+        * scroll one item more or one item less when there is not
+        * enough space to show a single table item.  The fix is
+        * to detect the case and call LVM_ENSUREVISIBLE again with
+        * the same arguments.  It seems that once LVM_ENSUREVISIBLE
+        * has scrolled into the general area, it is able to scroll
+        * to the exact item.
+        */
+        OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
+        if (index !is OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
+            OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
+        }
+    } else {
+        OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 0);
+    }
+}
+
+/**
+ * Shows the item.  If the item is already showing in the receiver,
+ * this method simply returns.  Otherwise, the items are scrolled until
+ * the item is visible.
+ *
+ * @param item the item to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#showSelection()
+ */
+public void showItem (TableItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (item.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    int index = indexOf (item);
+    if (index !is -1) showItem (index);
+}
+
+/**
+ * Shows the selection.  If the selection is already showing in the receiver,
+ * this method simply returns.  Otherwise, the items are scrolled until
+ * the selection is visible.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#showItem(TableItem)
+ */
+public void showSelection () {
+    checkWidget ();
+    int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED);
+    if (index !is -1) showItem (index);
+}
+
+/*public*/ void sort () {
+    checkWidget ();
+//  if ((style & DWT.VIRTUAL) !is 0) return;
+//  int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+//  if (itemCount is 0 || itemCount is 1) return;
+//  Comparator comparator = new Comparator () {
+//      int index = sortColumn is null ? 0 : indexOf (sortColumn);
+//      public int compare (Object object1, Object object2) {
+//          TableItem item1 = (TableItem) object1, item2 = (TableItem) object2;
+//          if (sortDirection is DWT.UP || sortDirection is DWT.NONE) {
+//              return item1.getText (index).compareTo (item2.getText (index));
+//          } else {
+//              return item2.getText (index).compareTo (item1.getText (index));
+//          }
+//      }
+//  };
+//  Arrays.sort (items, 0, itemCount, comparator);
+//  redraw ();
+}
+
+void subclass () {
+    super.subclass ();
+    if (HeaderProc !is 0) {
+        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, display.windowProc);
+    }
+}
+
+String toolTipText (NMTTDISPINFO hdr) {
+    int hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
+    if (hwndToolTip is hdr.hwndFrom && toolTipText !is null) return ""; //$NON-NLS-1$
+    if (headerToolTipHandle is hdr.hwndFrom) {
+        for (int i=0; i<columnCount; i++) {
+            TableColumn column = columns [i];
+            if (column.id is hdr.idFrom) return column.toolTipText;
+        }
+    }
+    return super.toolTipText (hdr);
+}
+
+void unsubclass () {
+    super.unsubclass ();
+    if (HeaderProc !is 0) {
+        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
+    }
+}
+
+void update (bool all) {
+//  checkWidget ();
+    /*
+    * When there are many columns in a table, scrolling performance
+    * can be improved by temporarily unsubclassing the window proc
+    * so that internal messages are dispatched directly to the table.
+    * If the application expects to see a paint event or has a child
+    * whose font, foreground or background color might be needed,
+    * the window proc cannot be unsubclassed.
+    *
+    * NOTE: The header tooltip can subclass the header proc so the
+    * current proc must be restored or header tooltips stop working.
+    */
+    int oldHeaderProc = 0, oldTableProc = 0;
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    bool fixSubclass = !hasChildren () && !hooks (DWT.Paint) && !filters (DWT.Paint);
+    if (fixSubclass) {
+        oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, TableProc);
+        oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
+    }
+    super.update (all);
+    if (fixSubclass) {
+        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
+        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
+    }
+}
+
+void updateHeaderToolTips () {
+    if (headerToolTipHandle is 0) return;
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    RECT rect = new RECT ();
+    TOOLINFO lpti = new TOOLINFO ();
+    lpti.cbSize = TOOLINFO.sizeof;
+    lpti.uFlags = OS.TTF_SUBCLASS;
+    lpti.hwnd = hwndHeader;
+    lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
+    for (int i=0; i<columnCount; i++) {
+        TableColumn column = columns [i];
+        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, rect) !is 0) {
+            lpti.uId = column.id = display.nextToolTipId++;
+            lpti.left = rect.left;
+            lpti.top = rect.top;
+            lpti.right = rect.right;
+            lpti.bottom = rect.bottom;
+            OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
+        }
+    }
+}
+
+void updateImages () {
+    if (sortColumn !is null && !sortColumn.isDisposed ()) {
+        if (OS.COMCTL32_MAJOR < 6) {
+            switch (sortDirection) {
+                case DWT.UP:
+                case DWT.DOWN:
+                    sortColumn.setImage (display.getSortImage (sortDirection), true, true);
+                    break;
+            }
+        }
+    }
+}
+
+void updateMoveable () {
+    int index = 0;
+    while (index < columnCount) {
+        if (columns [index].moveable) break;
+        index++;
+    }
+    int newBits = index < columnCount ? OS.LVS_EX_HEADERDRAGDROP : 0;
+    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_HEADERDRAGDROP, newBits);
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.LVS_SHAREIMAGELISTS;
+    if ((style & DWT.HIDE_SELECTION) is 0) bits |= OS.LVS_SHOWSELALWAYS;
+    if ((style & DWT.SINGLE) !is 0) bits |= OS.LVS_SINGLESEL;
+    /*
+    * This code is intentionally commented.  In the future,
+    * the FLAT bit may be used to make the header flat and
+    * unresponsive to mouse clicks.
+    */
+//  if ((style & DWT.FLAT) !is 0) bits |= OS.LVS_NOSORTHEADER;
+    bits |= OS.LVS_REPORT | OS.LVS_NOCOLUMNHEADER;
+    if ((style & DWT.VIRTUAL) !is 0) bits |= OS.LVS_OWNERDATA;
+    return bits;
+}
+
+TCHAR windowClass () {
+    return TableClass;
+}
+
+int windowProc () {
+    return TableProc;
+}
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    if (hwnd !is handle) {
+        switch (msg) {
+            /* This code is intentionally commented */
+//          case OS.WM_CONTEXTMENU: {
+//              LRESULT result = wmContextMenu (hwnd, wParam, lParam);
+//              if (result !is null) return result.value;
+//              break;
+//          }
+            case OS.WM_CAPTURECHANGED: {
+                /*
+                * Bug in Windows.  When the capture changes during a
+                * header drag, Windows does not redraw the header item
+                * such that the header remains pressed.  For example,
+                * when focus is assigned to a push button, the mouse is
+                * pressed (but not released), then the SPACE key is
+                * pressed to activate the button, the capture changes,
+                * the header not notified and NM_RELEASEDCAPTURE is not
+                * sent.  The fix is to redraw the header when the capture
+                * changes to another control.
+                *
+                * This does not happen on XP.
+                */
+                if (OS.COMCTL32_MAJOR < 6) {
+                    if (lParam !is 0) {
+                        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                        if (lParam !is hwndHeader) OS.InvalidateRect (hwndHeader, null, true);
+                    }
+                }
+                break;
+            }
+            case OS.WM_MOUSELEAVE: {
+                /*
+                * Bug in Windows.  On XP, when a tooltip is hidden
+                * due to a time out or mouse press, the tooltip
+                * remains active although no longer visible and
+                * won't show again until another tooltip becomes
+                * active.  The fix is to reset the tooltip bounds.
+                */
+                if (OS.COMCTL32_MAJOR >= 6) updateHeaderToolTips ();
+                updateHeaderToolTips ();
+                break;
+            }
+            case OS.WM_NOTIFY: {
+                NMHDR hdr = new NMHDR ();
+                OS.MoveMemory (hdr, lParam, NMHDR.sizeof);
+                switch (hdr.code) {
+                    case OS.TTN_SHOW:
+                    case OS.TTN_POP:
+                    case OS.TTN_GETDISPINFOA:
+                    case OS.TTN_GETDISPINFOW:
+                        return OS.SendMessage (handle, msg, wParam, lParam);
+                }
+                break;
+            }
+            case OS.WM_SETCURSOR: {
+                if (wParam is hwnd) {
+                    int hitTest = (short) (lParam & 0xFFFF);
+                    if (hitTest is OS.HTCLIENT) {
+                        HDHITTESTINFO pinfo = new HDHITTESTINFO ();
+                        int pos = OS.GetMessagePos ();
+                        POINT pt = new POINT ();
+                        pt.x = (short) (pos & 0xFFFF);
+                        pt.y = (short) (pos >> 16);
+                        OS.ScreenToClient (hwnd, pt);
+                        pinfo.x = pt.x;
+                        pinfo.y = pt.y;
+                        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                        int index = OS.SendMessage (hwndHeader, OS.HDM_HITTEST, 0, pinfo);
+                        if (0 <= index && index < columnCount && !columns [index].resizable) {
+                            if ((pinfo.flags & (OS.HHT_ONDIVIDER | OS.HHT_ONDIVOPEN)) !is 0) {
+                                OS.SetCursor (OS.LoadCursor (0, OS.IDC_ARROW));
+                                return 1;
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+        }
+        return callWindowProc (hwnd, msg, wParam, lParam);
+    }
+    return super.windowProc (hwnd, msg, wParam, lParam);
+}
+
+LRESULT WM_CHAR (int wParam, int lParam) {
+    LRESULT result = super.WM_CHAR (wParam, lParam);
+    if (result !is null) return result;
+    switch (wParam) {
+        case ' ':
+            if ((style & DWT.CHECK) !is 0) {
+                int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
+                if (index !is -1) {
+                    TableItem item = _getItem (index);
+                    item.setChecked (!item.getChecked (), true);
+                    if (!OS.IsWinCE) {
+                        OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
+                    }
+                }
+            }
+            /*
+            * NOTE: Call the window proc with WM_KEYDOWN rather than WM_CHAR
+            * so that the key that was ignored during WM_KEYDOWN is processed.
+            * This allows the application to cancel an operation that is normally
+            * performed in WM_KEYDOWN from WM_CHAR.
+            */
+            int code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
+            return new LRESULT (code);
+        case DWT.CR:
+            /*
+            * Feature in Windows.  Windows sends LVN_ITEMACTIVATE from WM_KEYDOWN
+            * instead of WM_CHAR.  This means that application code that expects
+            * to consume the key press and therefore avoid a DWT.DefaultSelection
+            * event will fail.  The fix is to ignore LVN_ITEMACTIVATE when it is
+            * caused by WM_KEYDOWN and send DWT.DefaultSelection from WM_CHAR.
+            */
+            int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
+            if (index !is -1) {
+                Event event = new Event ();
+                event.item = _getItem (index);
+                postEvent (DWT.DefaultSelection, event);
+            }
+            return LRESULT.ZERO;
+    }
+    return result;
+}
+
+LRESULT WM_CONTEXTMENU (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  For some reason, when the right
+    * mouse button is pressed over an item, Windows sends
+    * a WM_CONTEXTMENU from WM_RBUTTONDOWN, instead of from
+    * WM_RBUTTONUP.  This causes two context menus requests
+    * to be sent.  The fix is to ignore WM_CONTEXTMENU on
+    * mouse down.
+    *
+    * NOTE: This only happens when dragging is disabled.
+    * When the table is detecting drag, the WM_CONTEXTMENU
+    * is not sent WM_RBUTTONUP.
+    */
+    if (!display.runDragDrop) return LRESULT.ZERO;
+    return super.WM_CONTEXTMENU (wParam, lParam);
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    if (findImageControl () !is null) return LRESULT.ONE;
+    if (OS.COMCTL32_MAJOR < 6) {
+        if ((style & DWT.DOUBLE_BUFFERED) !is 0) {
+            int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+            if ((bits & OS.LVS_EX_DOUBLEBUFFER) is 0) return LRESULT.ONE;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_GETOBJECT (int wParam, int lParam) {
+    /*
+    * Ensure that there is an accessible object created for this
+    * control because support for checked item accessibility is
+    * temporarily implemented in the accessibility package.
+    */
+    if ((style & DWT.CHECK) !is 0) {
+        if (accessible is null) accessible = new_Accessible (this);
+    }
+    return super.WM_GETOBJECT (wParam, lParam);
+}
+
+LRESULT WM_KEYDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_KEYDOWN (wParam, lParam);
+    if (result !is null) return result;
+    switch (wParam) {
+        case OS.VK_SPACE:
+            /*
+            * Ensure that the window proc does not process VK_SPACE
+            * so that it can be handled in WM_CHAR.  This allows the
+            * application to cancel an operation that is normally
+            * performed in WM_KEYDOWN from WM_CHAR.
+            */
+            return LRESULT.ZERO;
+        case OS.VK_ADD:
+            if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
+                int index = 0;
+                while (index < columnCount) {
+                    if (!columns [index].getResizable ()) break;
+                    index++;
+                }
+                if (index !is columnCount || hooks (DWT.MeasureItem)) {
+                    TableColumn [] newColumns = new TableColumn [columnCount];
+                    System.arraycopy (columns, 0, newColumns, 0, columnCount);
+                    for (int i=0; i<newColumns.length; i++) {
+                        TableColumn column = newColumns [i];
+                        if (!column.isDisposed () && column.getResizable ()) {
+                            column.pack ();
+                        }
+                    }
+                    return LRESULT.ZERO;
+                }
+            }
+            break;
+        case OS.VK_PRIOR:
+        case OS.VK_NEXT:
+        case OS.VK_HOME:
+        case OS.VK_END:
+            /*
+            * When there are many columns in a table, scrolling performance
+            * can be improved by temporarily unsubclassing the window proc
+            * so that internal messages are dispatched directly to the table.
+            * If the application expects to see a paint event, the window
+            * proc cannot be unsubclassed or the event will not be seen.
+            *
+            * NOTE: The header tooltip can subclass the header proc so the
+            * current proc must be restored or header tooltips stop working.
+            */
+            int oldHeaderProc = 0, oldTableProc = 0;
+            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+            bool fixSubclass = !hasChildren () && !hooks (DWT.Paint) && !filters (DWT.Paint);
+            if (fixSubclass) {
+                oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, TableProc);
+                oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
+            }
+            int code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
+            result = code is 0 ? LRESULT.ZERO : new LRESULT (code);
+            if (fixSubclass) {
+                OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
+                OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
+            }
+            //FALL THROUGH
+        case OS.VK_UP:
+        case OS.VK_DOWN:
+            OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
+            break;
+    }
+    return result;
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
+    /*
+    * Bug in Windows.  When focus is lost, Windows does not
+    * redraw the selection properly, leaving the image and
+    * check box appearing selected.  The fix is to redraw
+    * the table.
+    */
+    if (imageList !is null || (style & DWT.CHECK) !is 0) {
+        OS.InvalidateRect (handle, null, false);
+    }
+    return result;
+}
+
+LRESULT WM_LBUTTONDBLCLK (int wParam, int lParam) {
+
+    /*
+    * Feature in Windows.  When the user selects outside of
+    * a table item, Windows deselects all the items, even
+    * when the table is multi-select.  While not strictly
+    * wrong, this is unexpected.  The fix is to detect the
+    * case and avoid calling the window proc.
+    */
+    LVHITTESTINFO pinfo = new LVHITTESTINFO ();
+    pinfo.x = (short) (lParam & 0xFFFF);
+    pinfo.y = (short) (lParam >> 16);
+    int index = OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
+    Display display = this.display;
+    display.captureChanged = false;
+    sendMouseEvent (DWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam);
+    if (!sendMouseEvent (DWT.MouseDoubleClick, 1, handle, OS.WM_LBUTTONDBLCLK, wParam, lParam)) {
+        if (!display.captureChanged && !isDisposed ()) {
+            if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+        }
+        return LRESULT.ZERO;
+    }
+    if (pinfo.iItem !is -1) callWindowProc (handle, OS.WM_LBUTTONDBLCLK, wParam, lParam);
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+    }
+
+    /* Look for check/uncheck */
+    if ((style & DWT.CHECK) !is 0) {
+        /*
+        * Note that when the table has LVS_EX_FULLROWSELECT and the
+        * user clicks anywhere on a row except on the check box, all
+        * of the bits are set.  The hit test flags are LVHT_ONITEM.
+        * This means that a bit test for LVHT_ONITEMSTATEICON is not
+        * the correct way to determine that the user has selected
+        * the check box, equality is needed.
+        */
+        if (index !is -1 && pinfo.flags is OS.LVHT_ONITEMSTATEICON) {
+            TableItem item = _getItem (index);
+            item.setChecked (!item.getChecked (), true);
+            if (!OS.IsWinCE) {
+                OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
+            }
+        }
+    }
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  For some reason, capturing
+    * the mouse after processing the mouse event for the
+    * widget interferes with the normal mouse processing
+    * for the widget.  The fix is to avoid the automatic
+    * mouse capture.
+    */
+    LRESULT result = sendMouseDownEvent (DWT.MouseDown, 1, OS.WM_LBUTTONDOWN, wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+
+    /* Look for check/uncheck */
+    if ((style & DWT.CHECK) !is 0) {
+        LVHITTESTINFO pinfo = new LVHITTESTINFO ();
+        pinfo.x = (short) (lParam & 0xFFFF);
+        pinfo.y = (short) (lParam >> 16);
+        /*
+        * Note that when the table has LVS_EX_FULLROWSELECT and the
+        * user clicks anywhere on a row except on the check box, all
+        * of the bits are set.  The hit test flags are LVHT_ONITEM.
+        * This means that a bit test for LVHT_ONITEMSTATEICON is not
+        * the correct way to determine that the user has selected
+        * the check box, equality is needed.
+        */
+        int index = OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
+        if (index !is -1 && pinfo.flags is OS.LVHT_ONITEMSTATEICON) {
+            TableItem item = _getItem (index);
+            item.setChecked (!item.getChecked (), true);
+            if (!OS.IsWinCE) {
+                OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_MOUSEHOVER (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  Despite the fact that hot
+    * tracking is not enabled, the hot tracking code
+    * in WM_MOUSEHOVER is executed causing the item
+    * under the cursor to be selected.  The fix is to
+    * avoid calling the window proc.
+    */
+    LRESULT result = super.WM_MOUSEHOVER (wParam, lParam);
+    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+    int mask = OS.LVS_EX_ONECLICKACTIVATE | OS.LVS_EX_TRACKSELECT | OS.LVS_EX_TWOCLICKACTIVATE;
+    if ((bits & mask) !is 0) return result;
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_PAINT (int wParam, int lParam) {
+    if (!ignoreShrink) {
+        /* Resize the item array to match the item count */
+        int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+        if (items.length > 4 && items.length - count > 3) {
+            int length = Math.max (4, (count + 3) / 4 * 4);
+            TableItem [] newItems = new TableItem [length];
+            System.arraycopy (items, 0, newItems, 0, count);
+            items = newItems;
+        }
+    }
+    if (fixScrollWidth) setScrollWidth (null, true);
+    if (OS.COMCTL32_MAJOR < 6) {
+        if ((style & DWT.DOUBLE_BUFFERED) !is 0 || findImageControl () !is null) {
+            int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+            if ((bits & OS.LVS_EX_DOUBLEBUFFER) is 0) {
+                GC gc = null;
+                int paintDC = 0;
+                PAINTSTRUCT ps = new PAINTSTRUCT ();
+                bool hooksPaint = hooks (DWT.Paint);
+                if (hooksPaint) {
+                    GCData data = new GCData ();
+                    data.ps = ps;
+                    data.hwnd = handle;
+                    gc = GC.win32_new (this, data);
+                    paintDC = gc.handle;
+                } else {
+                    paintDC = OS.BeginPaint (handle, ps);
+                }
+                int width = ps.right - ps.left;
+                int height = ps.bottom - ps.top;
+                if (width !is 0 && height !is 0) {
+                    int hDC = OS.CreateCompatibleDC (paintDC);
+                    POINT lpPoint1 = new POINT (), lpPoint2 = new POINT ();
+                    OS.SetWindowOrgEx (hDC, ps.left, ps.top, lpPoint1);
+                    OS.SetBrushOrgEx (hDC, ps.left, ps.top, lpPoint2);
+                    int hBitmap = OS.CreateCompatibleBitmap (paintDC, width, height);
+                    int hOldBitmap = OS.SelectObject (hDC, hBitmap);
+                    if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) !is OS.CLR_NONE) {
+                        RECT rect = new RECT ();
+                        OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+                        drawBackground (hDC, rect);
+                    }
+                    callWindowProc (handle, OS.WM_PAINT, hDC, 0);
+                    OS.SetWindowOrgEx (hDC, lpPoint1.x, lpPoint1.y, null);
+                    OS.SetBrushOrgEx (hDC, lpPoint2.x, lpPoint2.y, null);
+                    OS.BitBlt (paintDC, ps.left, ps.top, width, height, hDC, 0, 0, OS.SRCCOPY);
+                    OS.SelectObject (hDC, hOldBitmap);
+                    OS.DeleteObject (hBitmap);
+                    OS.DeleteObject (hDC);
+                    if (hooksPaint) {
+                        Event event = new Event ();
+                        event.gc = gc;
+                        event.x = ps.left;
+                        event.y = ps.top;
+                        event.width = ps.right - ps.left;
+                        event.height = ps.bottom - ps.top;
+                        sendEvent (DWT.Paint, event);
+                        // widget could be disposed at this point
+                        event.gc = null;
+                    }
+                }
+                if (hooksPaint) {
+                    gc.dispose ();
+                } else {
+                    OS.EndPaint (handle, ps);
+                }
+                return LRESULT.ZERO;
+            }
+        }
+    }
+    return super.WM_PAINT (wParam, lParam);
+}
+
+LRESULT WM_RBUTTONDBLCLK (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When the user selects outside of
+    * a table item, Windows deselects all the items, even
+    * when the table is multi-select.  While not strictly
+    * wrong, this is unexpected.  The fix is to detect the
+    * case and avoid calling the window proc.
+    */
+    LVHITTESTINFO pinfo = new LVHITTESTINFO ();
+    pinfo.x = (short) (lParam & 0xFFFF);
+    pinfo.y = (short) (lParam >> 16);
+    OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
+    Display display = this.display;
+    display.captureChanged = false;
+    sendMouseEvent (DWT.MouseDown, 3, handle, OS.WM_RBUTTONDOWN, wParam, lParam);
+    if (sendMouseEvent (DWT.MouseDoubleClick, 3, handle, OS.WM_RBUTTONDBLCLK, wParam, lParam)) {
+        if (pinfo.iItem !is -1) callWindowProc (handle, OS.WM_RBUTTONDBLCLK, wParam, lParam);
+    }
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+    }
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_RBUTTONDOWN (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  For some reason, capturing
+    * the mouse after processing the mouse event for the
+    * widget interferes with the normal mouse processing
+    * for the widget.  The fix is to avoid the automatic
+    * mouse capture.
+    */
+    return sendMouseDownEvent (DWT.MouseDown, 3, OS.WM_RBUTTONDOWN, wParam, lParam);
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_SETFOCUS (wParam, lParam);
+    /*
+    * Bug in Windows.  When focus is gained after the
+    * selection has been changed using LVM_SETITEMSTATE,
+    * Windows redraws the selected text but does not
+    * redraw the image or the check box, leaving them
+    * appearing unselected.  The fix is to redraw
+    * the table.
+    */
+    if (imageList !is null || (style & DWT.CHECK) !is 0) {
+        OS.InvalidateRect (handle, null, false);
+    }
+
+    /*
+    * Bug in Windows.  For some reason, the table does
+    * not set the default focus rectangle to be the first
+    * item in the table when it gets focus and there is
+    * no selected item.  The fix to make the first item
+    * be the focus item.
+    */
+    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
+    if (count is 0) return result;
+    int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
+    if (index is -1) {
+        LVITEM lvItem = new LVITEM ();
+        lvItem.state = OS.LVIS_FOCUSED;
+        lvItem.stateMask = OS.LVIS_FOCUSED;
+        ignoreSelect = true;
+        OS.SendMessage (handle, OS.LVM_SETITEMSTATE, 0, lvItem);
+        ignoreSelect = false;
+    }
+    return result;
+}
+
+LRESULT WM_SETFONT (int wParam, int lParam) {
+    LRESULT result = super.WM_SETFONT (wParam, lParam);
+    if (result !is null) return result;
+
+    /*
+    * Bug in Windows.  When a header has a sort indicator
+    * triangle, Windows resizes the indicator based on the
+    * size of the n-1th font.  The fix is to always make
+    * the n-1th font be the default.  This makes the sort
+    * indicator always be the default size.
+    *
+    * NOTE: The table window proc sets the actual font in
+    * the header so that all that is necessary here is to
+    * set the default first.
+    */
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    OS.SendMessage (hwndHeader, OS.WM_SETFONT, 0, lParam);
+
+    if (headerToolTipHandle !is 0) {
+        OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam);
+    }
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    if (ignoreResize) return null;
+    if (hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) {
+        OS.InvalidateRect (handle, null, true);
+    }
+    if (resizeCount !is 0) {
+        wasResized = true;
+        return null;
+    }
+    return super.WM_SIZE (wParam, lParam);
+}
+
+LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) {
+    LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
+    if (result !is null) return result;
+    if (findBackgroundControl () is null) {
+        setBackgroundPixel (defaultBackground ());
+    } else {
+        if ((style & DWT.CHECK) !is 0) {
+            fixCheckboxImageListColor (true);
+        }
+    }
+    return result;
+}
+
+LRESULT WM_HSCROLL (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  When a table that is drawing grid lines
+    * is slowly scrolled horizontally to the left, the table does
+    * not redraw the newly exposed vertical grid lines.  The fix
+    * is to save the old scroll position, call the window proc,
+    * get the new scroll position and redraw the new area.
+    */
+    int oldPos = 0;
+    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+    if ((bits & OS.LVS_EX_GRIDLINES) !is 0) {
+        SCROLLINFO info = new SCROLLINFO ();
+        info.cbSize = SCROLLINFO.sizeof;
+        info.fMask = OS.SIF_POS;
+        OS.GetScrollInfo (handle, OS.SB_HORZ, info);
+        oldPos = info.nPos;
+    }
+
+    /*
+    * When there are many columns in a table, scrolling performance
+    * can be improved by temporarily unsubclassing the window proc
+    * so that internal messages are dispatched directly to the table.
+    * If the application expects to see a paint event or has a child
+    * whose font, foreground or background color might be needed,
+    * the window proc cannot be unsubclassed
+    *
+    * NOTE: The header tooltip can subclass the header proc so the
+    * current proc must be restored or header tooltips stop working.
+    */
+    int oldHeaderProc = 0, oldTableProc = 0;
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    bool fixSubclass = !hasChildren () && !hooks (DWT.Paint) && !filters (DWT.Paint);
+    if (fixSubclass) {
+        oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, TableProc);
+        oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
+    }
+    LRESULT result = super.WM_HSCROLL (wParam, lParam);
+    if (fixSubclass) {
+        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
+        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
+    }
+
+    /*
+    * Bug in Windows.  When a table that is drawing grid lines
+    * is slowly scrolled horizontally to the left, the table does
+    * not redraw the newly exposed vertical grid lines.  The fix
+    * is to save the old scroll position, call the window proc,
+    * get the new scroll position and redraw the new area.
+    */
+    if ((bits & OS.LVS_EX_GRIDLINES) !is 0) {
+        SCROLLINFO info = new SCROLLINFO ();
+        info.cbSize = SCROLLINFO.sizeof;
+        info.fMask = OS.SIF_POS;
+        OS.GetScrollInfo (handle, OS.SB_HORZ, info);
+        int newPos = info.nPos;
+        if (newPos < oldPos) {
+            RECT rect = new RECT ();
+            OS.GetClientRect (handle, rect);
+            rect.right = oldPos - newPos + GRID_WIDTH;
+            OS.InvalidateRect (handle, rect, true);
+        }
+    }
+    return result;
+}
+
+LRESULT WM_VSCROLL (int wParam, int lParam) {
+    /*
+    * When there are many columns in a table, scrolling performance
+    * can be improved by temporarily unsubclassing the window proc
+    * so that internal messages are dispatched directly to the table.
+    * If the application expects to see a paint event or has a child
+    * whose font, foreground or background color might be needed,
+    * the window proc cannot be unsubclassed.
+    *
+    * NOTE: The header tooltip can subclass the header proc so the
+    * current proc must be restored or header tooltips stop working.
+    */
+    int oldHeaderProc = 0, oldTableProc = 0;
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    bool fixSubclass = !hasChildren () && !hooks (DWT.Paint) && !filters (DWT.Paint);
+    if (fixSubclass) {
+        oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, TableProc);
+        oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
+    }
+    LRESULT result = super.WM_VSCROLL (wParam, lParam);
+    if (fixSubclass) {
+        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
+        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
+    }
+
+    /*
+    * Bug in Windows.  When a table is drawing grid lines and the
+    * user scrolls vertically up or down by a line or a page, the
+    * table does not redraw the grid lines for newly exposed items.
+    * The fix is to invalidate the items.
+    */
+    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+    if ((bits & OS.LVS_EX_GRIDLINES) !is 0) {
+        int code = wParam & 0xFFFF;
+        switch (code) {
+            case OS.SB_ENDSCROLL:
+            case OS.SB_THUMBPOSITION:
+            case OS.SB_THUMBTRACK:
+            case OS.SB_TOP:
+            case OS.SB_BOTTOM:
+                break;
+            case OS.SB_LINEDOWN:
+            case OS.SB_LINEUP:
+                RECT rect = new RECT ();
+                OS.GetWindowRect (hwndHeader, rect);
+                int headerHeight = rect.bottom - rect.top;
+                RECT clientRect = new RECT ();
+                OS.GetClientRect (handle, clientRect);
+                clientRect.top += headerHeight;
+                int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+                int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+                int itemHeight = (oneItem >> 16) - (empty >> 16);
+                if (code is OS.SB_LINEDOWN) {
+                    clientRect.top = clientRect.bottom - itemHeight - GRID_WIDTH;
+                } else {
+                    clientRect.bottom = clientRect.top + itemHeight + GRID_WIDTH;
+                }
+                OS.InvalidateRect (handle, clientRect, true);
+                break;
+            case OS.SB_PAGEDOWN:
+            case OS.SB_PAGEUP:
+                OS.InvalidateRect (handle, null, true);
+                break;
+        }
+    }
+    return result;
+}
+
+LRESULT wmMeasureChild (int wParam, int lParam) {
+    MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT ();
+    OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof);
+    if (itemHeight is -1) {
+        int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
+        int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
+        struct.itemHeight = (oneItem >> 16) - (empty >> 16);
+    } else {
+        struct.itemHeight = itemHeight;
+    }
+    OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof);
+    return null;
+}
+
+LRESULT wmNotify (NMHDR hdr, int wParam, int lParam) {
+    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+    if (hdr.hwndFrom is hwndHeader) {
+        /*
+        * Feature in Windows.  On NT, the automatically created
+        * header control is created as a UNICODE window, not an
+        * ANSI window despite the fact that the parent is created
+        * as an ANSI window.  This means that it sends UNICODE
+        * notification messages to the parent window on NT for
+        * no good reason.  The data and size in the NMHEADER and
+        * HDITEM structs is identical between the platforms so no
+        * different message is actually necessary.  Despite this,
+        * Windows sends different messages.  The fix is to look
+        * for both messages, despite the platform.  This works
+        * because only one will be sent on either platform, never
+        * both.
+        */
+        switch (hdr.code) {
+            case OS.HDN_BEGINTRACKW:
+            case OS.HDN_BEGINTRACKA:
+            case OS.HDN_DIVIDERDBLCLICKW:
+            case OS.HDN_DIVIDERDBLCLICKA: {
+                if (columnCount is 0) return LRESULT.ONE;
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                TableColumn column = columns [phdn.iItem];
+                if (column !is null && !column.getResizable ()) {
+                    return LRESULT.ONE;
+                }
+                ignoreColumnMove = true;
+                switch (hdr.code) {
+                    case OS.HDN_DIVIDERDBLCLICKW:
+                    case OS.HDN_DIVIDERDBLCLICKA:
+                        /*
+                        * Bug in Windows.  When the first column of a table does not
+                        * have an image and the user double clicks on the divider,
+                        * Windows packs the column but does not take into account
+                        * the empty space left for the image.  The fix is to measure
+                        * each items ourselves rather than letting Windows do it.
+                        */
+                        bool fixPack = phdn.iItem is 0 && !firstColumnImage;
+                        if (column !is null && (fixPack || hooks (DWT.MeasureItem))) {
+                            column.pack ();
+                            return LRESULT.ONE;
+                        }
+                }
+                break;
+            }
+            case OS.NM_RELEASEDCAPTURE: {
+                if (!ignoreColumnMove) {
+                    for (int i=0; i<columnCount; i++) {
+                        TableColumn column = columns [i];
+                        column.updateToolTip (i);
+                    }
+                }
+                ignoreColumnMove = false;
+                break;
+            }
+            case OS.HDN_BEGINDRAG: {
+                if (ignoreColumnMove) return LRESULT.ONE;
+                int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+                if ((bits & OS.LVS_EX_HEADERDRAGDROP) is 0) break;
+                if (columnCount is 0) return LRESULT.ONE;
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                if (phdn.iItem !is -1) {
+                    TableColumn column = columns [phdn.iItem];
+                    if (column !is null && !column.getMoveable ()) {
+                        ignoreColumnMove = true;
+                        return LRESULT.ONE;
+                    }
+                }
+                break;
+            }
+            case OS.HDN_ENDDRAG: {
+                int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+                if ((bits & OS.LVS_EX_HEADERDRAGDROP) is 0) break;
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                if (phdn.iItem !is -1 && phdn.pitem !is 0) {
+                    HDITEM pitem = new HDITEM ();
+                    OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
+                    if ((pitem.mask & OS.HDI_ORDER) !is 0 && pitem.iOrder !is -1) {
+                        if (columnCount is 0) break;
+                        int [] order = new int [columnCount];
+                        OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
+                        int index = 0;
+                        while (index < order.length) {
+                            if (order [index] is phdn.iItem) break;
+                            index++;
+                        }
+                        if (index is order.length) index = 0;
+                        if (index is pitem.iOrder) break;
+                        int start = Math.min (index, pitem.iOrder);
+                        int end = Math.max (index, pitem.iOrder);
+                        ignoreColumnMove = false;
+                        for (int i=start; i<=end; i++) {
+                            TableColumn column = columns [order [i]];
+                            if (!column.isDisposed ()) {
+                                column.postEvent (DWT.Move);
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+            case OS.HDN_ITEMCHANGEDW:
+            case OS.HDN_ITEMCHANGEDA: {
+                /*
+                * Bug in Windows.  When a table has the LVS_EX_GRIDLINES extended
+                * style and the user drags any column over the first column in the
+                * table, making the size become zero, when the user drags a column
+                * such that the size of the first column becomes non-zero, the grid
+                * lines are not redrawn.  The fix is to detect the case and force
+                * a redraw of the first column.
+                */
+                int width = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
+                if (lastWidth is 0 && width > 0) {
+                    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+                    if ((bits & OS.LVS_EX_GRIDLINES) !is 0) {
+                        RECT rect = new RECT ();
+                        OS.GetClientRect (handle, rect);
+                        rect.right = rect.left + width;
+                        OS.InvalidateRect (handle, rect, true);
+                    }
+                }
+                lastWidth = width;
+                if (!ignoreColumnResize) {
+                    NMHEADER phdn = new NMHEADER ();
+                    OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                    if (phdn.pitem !is 0) {
+                        HDITEM pitem = new HDITEM ();
+                        OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
+                        if ((pitem.mask & OS.HDI_WIDTH) !is 0) {
+                            TableColumn column = columns [phdn.iItem];
+                            if (column !is null) {
+                                column.updateToolTip (phdn.iItem);
+                                column.sendEvent (DWT.Resize);
+                                if (isDisposed ()) return LRESULT.ZERO;
+                                /*
+                                * It is possible (but unlikely), that application
+                                * code could have disposed the column in the move
+                                * event.  If this happens, process the move event
+                                * for those columns that have not been destroyed.
+                                */
+                                TableColumn [] newColumns = new TableColumn [columnCount];
+                                System.arraycopy (columns, 0, newColumns, 0, columnCount);
+                                int [] order = new int [columnCount];
+                                OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
+                                bool moved = false;
+                                for (int i=0; i<columnCount; i++) {
+                                    TableColumn nextColumn = newColumns [order [i]];
+                                    if (moved && !nextColumn.isDisposed ()) {
+                                        nextColumn.updateToolTip (order [i]);
+                                        nextColumn.sendEvent (DWT.Move);
+                                    }
+                                    if (nextColumn is column) moved = true;
+                                }
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+            case OS.HDN_ITEMDBLCLICKW:
+            case OS.HDN_ITEMDBLCLICKA: {
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                TableColumn column = columns [phdn.iItem];
+                if (column !is null) {
+                    column.postEvent (DWT.DefaultSelection);
+                }
+                break;
+            }
+        }
+    }
+    LRESULT result = super.wmNotify (hdr, wParam, lParam);
+    if (result !is null) return result;
+    switch (hdr.code) {
+        case OS.TTN_GETDISPINFOA:
+        case OS.TTN_GETDISPINFOW: {
+            tipRequested = true;
+            int code = callWindowProc (handle, OS.WM_NOTIFY, wParam, lParam);
+            tipRequested = false;
+            return new LRESULT (code);
+        }
+    }
+    return result;
+}
+
+LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
+    switch (hdr.code) {
+        case OS.LVN_ODFINDITEMA:
+        case OS.LVN_ODFINDITEMW: {
+            if ((style & DWT.VIRTUAL) !is 0) return new LRESULT (-1);
+            break;
+        }
+        case OS.LVN_ODSTATECHANGED: {
+            if ((style & DWT.VIRTUAL) !is 0) {
+                if (!ignoreSelect) {
+                    NMLVODSTATECHANGE lpStateChange  = new NMLVODSTATECHANGE ();
+                    OS.MoveMemory (lpStateChange, lParam, NMLVODSTATECHANGE.sizeof);
+                    bool oldSelected = (lpStateChange.uOldState & OS.LVIS_SELECTED) !is 0;
+                    bool newSelected = (lpStateChange.uNewState & OS.LVIS_SELECTED) !is 0;
+                    if (oldSelected !is newSelected) wasSelected = true;
+                }
+            }
+            break;
+        }
+        case OS.LVN_GETDISPINFOA:
+        case OS.LVN_GETDISPINFOW: {
+//          if (drawCount !is 0 || !OS.IsWindowVisible (handle)) break;
+            NMLVDISPINFO plvfi = new NMLVDISPINFO ();
+            OS.MoveMemory (plvfi, lParam, NMLVDISPINFO.sizeof);
+
+            /*
+            * When an item is being deleted from a virtual table, do not
+            * allow the application to provide data for a new item that
+            * becomes visible until the item has been removed from the
+            * items array.  Because arbitrary application code can run
+            * during the callback, the items array might be accessed
+            * in an inconsistent state.  Rather than answering the data
+            * right away, queue a redraw for later.
+            */
+            if ((style & DWT.VIRTUAL) !is 0) {
+                if (ignoreShrink) {
+                    OS.SendMessage (handle, OS.LVM_REDRAWITEMS, plvfi.iItem, plvfi.iItem);
+                    break;
+                }
+            }
+
+            /*
+            * Feature in Windows.  When a new table item is inserted
+            * using LVM_INSERTITEM in a table that is transparent
+            * (ie. LVM_SETBKCOLOR has been called with CLR_NONE),
+            * TVM_INSERTITEM calls LVN_GETDISPINFO before the item
+            * has been added to the array.  The fix is to check for
+            * null.
+            */
+            TableItem item = _getItem (plvfi.iItem);
+            if (item is null) break;
+
+            /*
+            * The cached flag is used by both virtual and non-virtual
+            * tables to indicate that Windows has asked at least once
+            * for a table item.
+            */
+            if (!item.cached) {
+                if ((style & DWT.VIRTUAL) !is 0) {
+                    lastIndexOf = plvfi.iItem;
+                    if (!checkData (item, lastIndexOf, false)) break;
+                    TableItem newItem = fixScrollWidth ? null : item;
+                    if (setScrollWidth (newItem, true)) {
+                        OS.InvalidateRect (handle, null, true);
+                    }
+                }
+                item.cached = true;
+            }
+            if ((plvfi.mask & OS.LVIF_TEXT) !is 0) {
+                String string = null;
+                if (plvfi.iSubItem is 0) {
+                    string = item.text;
+                } else {
+                    String [] strings  = item.strings;
+                    if (strings !is null) string = strings [plvfi.iSubItem];
+                }
+                if (string !is null) {
+                    /*
+                    * Bug in Windows.  When pszText points to a zero length
+                    * NULL terminated string, Windows correctly draws the
+                    * empty string but the cache of the bounds for the item
+                    * is not reset.  This means that when the text for the
+                    * item is set and then reset to an empty string, the
+                    * selection draws using the bounds of the previous text.
+                    * The fix is to use a space rather than an empty string
+                    * when anything but a tool tip is requested (to avoid
+                    * a tool tip that is a single space).
+                    *
+                    * NOTE: This is only a problem for items in the first
+                    * column.  Assigning NULL to other columns stops Windows
+                    * from drawing the selection when LVS_EX_FULLROWSELECT
+                    * is set.
+                    */
+                    int length = Math.min (string.length (), plvfi.cchTextMax - 1);
+                    if (!tipRequested && plvfi.iSubItem is 0 && length is 0) {
+                        string = " "; //$NON-NLS-1$
+                        length = 1;
+                    }
+                    char [] buffer = display.tableBuffer;
+                    if (buffer is null || plvfi.cchTextMax > buffer.length) {
+                        buffer = display.tableBuffer = new char [plvfi.cchTextMax];
+                    }
+                    string.getChars (0, length, buffer, 0);
+                    buffer [length++] = 0;
+                    if (OS.IsUnicode) {
+                        OS.MoveMemory (plvfi.pszText, buffer, length * 2);
+                    } else {
+                        OS.WideCharToMultiByte (getCodePage (), 0, buffer, length, plvfi.pszText, plvfi.cchTextMax, null, null);
+                        OS.MoveMemory (plvfi.pszText + plvfi.cchTextMax - 1, new byte [1], 1);
+                    }
+                }
+            }
+            bool move = false;
+            if ((plvfi.mask & OS.LVIF_IMAGE) !is 0) {
+                Image image = null;
+                if (plvfi.iSubItem is 0) {
+                    image = item.image;
+                } else {
+                    Image [] images = item.images;
+                    if (images !is null) image = images [plvfi.iSubItem];
+                }
+                if (image !is null) {
+                    plvfi.iImage = imageIndex (image, plvfi.iSubItem);
+                    move = true;
+                }
+            }
+            if ((plvfi.mask & OS.LVIF_STATE) !is 0) {
+                if (plvfi.iSubItem is 0) {
+                    int state = 1;
+                    if (item.checked) state++;
+                    if (item.grayed) state +=2;
+                    plvfi.state = state << 12;
+                    plvfi.stateMask = OS.LVIS_STATEIMAGEMASK;
+                    move = true;
+                }
+            }
+            if ((plvfi.mask & OS.LVIF_INDENT) !is 0) {
+                if (plvfi.iSubItem is 0) {
+                    plvfi.iIndent = item.imageIndent;
+                    move = true;
+                }
+            }
+            if (move) OS.MoveMemory (lParam, plvfi, NMLVDISPINFO.sizeof);
+            break;
+        }
+        case OS.NM_CUSTOMDRAW: {
+            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+            if (hdr.hwndFrom is hwndHeader) break;
+            if (!customDraw && findImageControl () is null) {
+                /*
+                * Feature in Windows.  When the table is disabled, it draws
+                * with a gray background but does not gray the text.  The fix
+                * is to explicitly gray the text using Custom Draw.
+                */
+                if (OS.IsWindowEnabled (handle)) {
+                    /*
+                    * Feature in Windows.  On Vista using the explorer theme,
+                    * Windows draws a vertical line to separate columns.  When
+                    * there is only a single column, the line looks strange.
+                    * The fix is to draw the background using custom draw.
+                    */
+                    if (!explorerTheme || columnCount !is 0) break;
+                }
+            }
+            NMLVCUSTOMDRAW nmcd = new NMLVCUSTOMDRAW ();
+            OS.MoveMemory (nmcd, lParam, NMLVCUSTOMDRAW.sizeof);
+            switch (nmcd.dwDrawStage) {
+                case OS.CDDS_PREPAINT: return CDDS_PREPAINT (nmcd, wParam, lParam);
+                case OS.CDDS_ITEMPREPAINT: return CDDS_ITEMPREPAINT (nmcd, wParam, lParam);
+                case OS.CDDS_ITEMPOSTPAINT: return CDDS_ITEMPOSTPAINT (nmcd, wParam, lParam);
+                case OS.CDDS_SUBITEMPREPAINT: return CDDS_SUBITEMPREPAINT (nmcd, wParam, lParam);
+                case OS.CDDS_SUBITEMPOSTPAINT: return CDDS_SUBITEMPOSTPAINT (nmcd, wParam, lParam);
+                case OS.CDDS_POSTPAINT: return CDDS_POSTPAINT (nmcd, wParam, lParam);
+            }
+            break;
+        }
+        case OS.LVN_MARQUEEBEGIN: {
+            if ((style & DWT.SINGLE) !is 0) return LRESULT.ONE;
+            if (hooks (DWT.MouseDown) || hooks (DWT.MouseUp)) {
+                return LRESULT.ONE;
+            }
+            break;
+        }
+        case OS.LVN_BEGINDRAG:
+        case OS.LVN_BEGINRDRAG: {
+            dragStarted = true;
+            if (hdr.code is OS.LVN_BEGINDRAG) {
+                int pos = OS.GetMessagePos ();
+                POINT pt = new POINT ();
+                pt.x = (short) (pos & 0xFFFF);
+                pt.y = (short) (pos >> 16);
+                OS.ScreenToClient (handle, pt);
+                sendDragEvent (1, pt.x, pt.y);
+            }
+            break;
+        }
+        case OS.LVN_COLUMNCLICK: {
+            NMLISTVIEW pnmlv = new NMLISTVIEW ();
+            OS.MoveMemory(pnmlv, lParam, NMLISTVIEW.sizeof);
+            TableColumn column = columns [pnmlv.iSubItem];
+            if (column !is null) {
+                column.postEvent (DWT.Selection);
+            }
+            break;
+        }
+        case OS.LVN_ITEMACTIVATE: {
+            if (ignoreActivate) break;
+            NMLISTVIEW pnmlv = new NMLISTVIEW ();
+            OS.MoveMemory(pnmlv, lParam, NMLISTVIEW.sizeof);
+            if (pnmlv.iItem !is -1) {
+                Event event = new Event ();
+                event.item = _getItem (pnmlv.iItem);
+                postEvent (DWT.DefaultSelection, event);
+            }
+            break;
+        }
+        case OS.LVN_ITEMCHANGED: {
+            if (!ignoreSelect) {
+                NMLISTVIEW pnmlv = new NMLISTVIEW ();
+                OS.MoveMemory (pnmlv, lParam, NMLISTVIEW.sizeof);
+                if ((pnmlv.uChanged & OS.LVIF_STATE) !is 0) {
+                    if (pnmlv.iItem is -1) {
+                        wasSelected = true;
+                    } else {
+                        bool oldSelected = (pnmlv.uOldState & OS.LVIS_SELECTED) !is 0;
+                        bool newSelected = (pnmlv.uNewState & OS.LVIS_SELECTED) !is 0;
+                        if (oldSelected !is newSelected) wasSelected = true;
+                    }
+                }
+            }
+            if (hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) {
+                int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
+                int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                if (count !is 0) {
+                    forceResize ();
+                    RECT rect = new RECT ();
+                    OS.GetClientRect (handle, rect);
+                    NMLISTVIEW pnmlv = new NMLISTVIEW ();
+                    OS.MoveMemory (pnmlv, lParam, NMLISTVIEW.sizeof);
+                    if (pnmlv.iItem !is -1) {
+                        RECT itemRect = new RECT ();
+                        itemRect.left = OS.LVIR_BOUNDS;
+                        ignoreCustomDraw = true;
+                        OS.SendMessage (handle, OS. LVM_GETITEMRECT, pnmlv.iItem, itemRect);
+                        ignoreCustomDraw = false;
+                        RECT headerRect = new RECT ();
+                        int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0);
+                        OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+                        OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
+                        rect.left = headerRect.right;
+                        rect.top = itemRect.top;
+                        rect.bottom = itemRect.bottom;
+                        OS.InvalidateRect (handle, rect, true);
+                    }
+                }
+            }
+            break;
+        }
+        case OS.NM_RECOGNIZEGESTURE:
+            /*
+            * Feature on Pocket PC.  The tree and table controls detect the tap
+            * and hold gesture by default. They send a GN_CONTEXTMENU message to show
+            * the popup menu.  This default behaviour is unwanted on Pocket PC 2002
+            * when no menu has been set, as it still draws a red circle.  The fix
+            * is to disable this default behaviour when no menu is set by returning
+            * TRUE when receiving the Pocket PC 2002 specific NM_RECOGNIZEGESTURE
+            * message.
+            */
+            if (OS.IsPPC) {
+                bool hasMenu = menu !is null && !menu.isDisposed ();
+                if (!hasMenu && !hooks (DWT.MenuDetect)) return LRESULT.ONE;
+            }
+            break;
+        case OS.GN_CONTEXTMENU:
+            if (OS.IsPPC) {
+                bool hasMenu = menu !is null && !menu.isDisposed ();
+                if (hasMenu || hooks (DWT.MenuDetect)) {
+                    NMRGINFO nmrg = new NMRGINFO ();
+                    OS.MoveMemory (nmrg, lParam, NMRGINFO.sizeof);
+                    showMenu (nmrg.x, nmrg.y);
+                    return LRESULT.ONE;
+                }
+            }
+            break;
+    }
+    return super.wmNotifyChild (hdr, wParam, lParam);
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/TableColumn.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,878 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.TableColumn;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+class TableColumn : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+
+/++
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.ControlListener;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Image;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.HDITEM;
+import dwt.internal.win32.LVCOLUMN;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TOOLINFO;
+
+/**
+ * Instances of this class represent a column in a table widget.
+ * <p><dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>LEFT, RIGHT, CENTER</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd> Move, Resize, Selection</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles LEFT, RIGHT and CENTER may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+public class TableColumn extends Item {
+    Table parent;
+    bool resizable, moveable;
+    String toolTipText;
+    int id;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Table</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#LEFT
+ * @see DWT#RIGHT
+ * @see DWT#CENTER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TableColumn (Table parent, int style) {
+    super (parent, checkStyle (style));
+    resizable = true;
+    this.parent = parent;
+    parent.createItem (this, parent.getColumnCount ());
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Table</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#LEFT
+ * @see DWT#RIGHT
+ * @see DWT#CENTER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TableColumn (Table parent, int style, int index) {
+    super (parent, checkStyle (style));
+    resizable = true;
+    this.parent = parent;
+    parent.createItem (this, index);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is moved or resized, by sending
+ * it one of the messages defined in the <code>ControlListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #removeControlListener
+ */
+public void addControlListener(ControlListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Resize,typedListener);
+    addListener (DWT.Move,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the column header is selected.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the control is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.LEFT, DWT.CENTER, DWT.RIGHT, 0, 0, 0);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+void destroyWidget () {
+    parent.destroyItem (this);
+    releaseHandle ();
+}
+
+/**
+ * Returns a value which describes the position of the
+ * text or image in the receiver. The value will be one of
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>.
+ *
+ * @return the alignment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getAlignment () {
+    checkWidget ();
+    if ((style & DWT.LEFT) !is 0) return DWT.LEFT;
+    if ((style & DWT.CENTER) !is 0) return DWT.CENTER;
+    if ((style & DWT.RIGHT) !is 0) return DWT.RIGHT;
+    return DWT.LEFT;
+}
+
+String getNameText () {
+    return getText ();
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Table</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Table getParent () {
+    checkWidget ();
+    return parent;
+}
+
+/**
+ * Gets the moveable attribute. A column that is
+ * not moveable cannot be reordered by the user
+ * by dragging the header but may be reordered
+ * by the programmer.
+ *
+ * @return the moveable attribute
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#getColumnOrder()
+ * @see Table#setColumnOrder(int[])
+ * @see TableColumn#setMoveable(bool)
+ * @see DWT#Move
+ *
+ * @since 3.1
+ */
+public bool getMoveable () {
+    checkWidget ();
+    return moveable;
+}
+
+/**
+ * Gets the resizable attribute. A column that is
+ * not resizable cannot be dragged by the user but
+ * may be resized by the programmer.
+ *
+ * @return the resizable attribute
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getResizable () {
+    checkWidget ();
+    return resizable;
+}
+
+/**
+ * Returns the receiver's tool tip text, or null if it has
+ * not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public String getToolTipText () {
+    checkWidget();
+    return toolTipText;
+}
+
+/**
+ * Gets the width of the receiver.
+ *
+ * @return the width
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getWidth () {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return 0;
+    int hwnd = parent.handle;
+    return OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0);
+}
+
+/**
+ * Causes the receiver to be resized to its preferred size.
+ * For a composite, this involves computing the preferred size
+ * from its layout, if there is one.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ */
+public void pack () {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    int hwnd = parent.handle;
+    int oldWidth = OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0);
+    TCHAR buffer = new TCHAR (parent.getCodePage (), text, true);
+    int headerWidth = OS.SendMessage (hwnd, OS.LVM_GETSTRINGWIDTH, 0, buffer) + Table.HEADER_MARGIN;
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) headerWidth += Table.HEADER_EXTRA;
+    bool hasHeaderImage = false;
+    if (image !is null || parent.sortColumn is this) {
+        hasHeaderImage = true;
+        Image headerImage = null;
+        if (parent.sortColumn is this && parent.sortDirection !is DWT.NONE) {
+            if (OS.COMCTL32_MAJOR < 6) {
+                headerImage = display.getSortImage (parent.sortDirection);
+            } else {
+                headerWidth += Table.SORT_WIDTH;
+            }
+        } else {
+            headerImage = image;
+        }
+        if (headerImage !is null) {
+            Rectangle bounds = headerImage.getBounds ();
+            headerWidth += bounds.width;
+        }
+        int margin = 0;
+        if (OS.COMCTL32_VERSION >= OS.VERSION (5, 80)) {
+            int hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0);
+            margin = OS.SendMessage (hwndHeader, OS.HDM_GETBITMAPMARGIN, 0, 0);
+        } else {
+            margin = OS.GetSystemMetrics (OS.SM_CXEDGE) * 3;
+        }
+        headerWidth += margin * 4;
+    }
+    parent.ignoreColumnResize = true;
+    int columnWidth = 0;
+    /*
+    * Bug in Windows.  When the first column of a table does not
+    * have an image and the user double clicks on the divider,
+    * Windows packs the column but does not take into account
+    * the empty space left for the image.  The fix is to measure
+    * each items ourselves rather than letting Windows do it.
+    */
+    if ((index is 0 && !parent.firstColumnImage) || parent.hooks (DWT.MeasureItem)) {
+        RECT headerRect = new RECT ();
+        int hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0);
+        OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+        OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2);
+        int hDC = OS.GetDC (hwnd);
+        int oldFont = 0, newFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+        int count = OS.SendMessage (hwnd, OS.LVM_GETITEMCOUNT, 0, 0);
+        for (int i=0; i<count; i++) {
+            TableItem item = parent.items [i];
+            if (item !is null) {
+                int hFont = item.cellFont !is null ? item.cellFont [index] : -1;
+                if (hFont is -1) hFont = item.font;
+                if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+                Event event = parent.sendMeasureItemEvent (item, i, index, hDC);
+                if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+                if (isDisposed () || parent.isDisposed ()) break;
+                columnWidth = Math.max (columnWidth, event.x + event.width - headerRect.left);
+            }
+        }
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (hwnd, hDC);
+        OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, columnWidth);
+    } else {
+        OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, OS.LVSCW_AUTOSIZE);
+        columnWidth = OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0);
+        if (index is 0) {
+            /*
+            * Bug in Windows.  When LVM_SETCOLUMNWIDTH is used with LVSCW_AUTOSIZE
+            * where each item has I_IMAGECALLBACK but there are no images in the
+            * table, the size computed by LVM_SETCOLUMNWIDTH is too small for the
+            * first column, causing long items to be clipped with '...'.  The fix
+            * is to increase the column width by a small amount.
+            */
+            if (parent.imageList is null) columnWidth += 2;
+            /*
+            * Bug in Windows.  When LVM_SETCOLUMNWIDTH is used with LVSCW_AUTOSIZE
+            * for a table with a state image list, the column is width does not
+            * include space for the state icon.  The fix is to increase the column
+            * width by the width of the image list.
+            */
+            if ((parent.style & DWT.CHECK) !is 0) {
+                int hStateList = OS.SendMessage (hwnd, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
+                if (hStateList !is 0) {
+                    int [] cx = new int [1], cy = new int [1];
+                    OS.ImageList_GetIconSize (hStateList, cx, cy);
+                    columnWidth += cx [0];
+                }
+            }
+        }
+    }
+    if (headerWidth > columnWidth) {
+        if (!hasHeaderImage) {
+            /*
+            * Feature in Windows.  When LVSCW_AUTOSIZE_USEHEADER is used
+            * with LVM_SETCOLUMNWIDTH to resize the last column, the last
+            * column is expanded to fill the client area.  The fix is to
+            * resize the table to be small, set the column width and then
+            * restore the table to its original size.
+            */
+            RECT rect = null;
+            bool fixWidth = index is parent.getColumnCount () - 1;
+            if (fixWidth) {
+                rect = new RECT ();
+                OS.GetWindowRect (hwnd, rect);
+                OS.UpdateWindow (hwnd);
+                int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER;
+                SetWindowPos (hwnd, 0, 0, 0, 0, rect.bottom - rect.top, flags);
+            }
+            OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, OS.LVSCW_AUTOSIZE_USEHEADER);
+            if (fixWidth) {
+                int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOZORDER;
+                SetWindowPos (hwnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, flags);
+            }
+        } else {
+            OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, headerWidth);
+        }
+    } else {
+        if (index is 0) {
+            OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, columnWidth);
+        }
+    }
+    parent.ignoreColumnResize = false;
+    int newWidth = OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0);
+    if (oldWidth !is newWidth) {
+        updateToolTip (index);
+        sendEvent (DWT.Resize);
+        if (isDisposed ()) return;
+        bool moved = false;
+        int [] order = parent.getColumnOrder ();
+        TableColumn [] columns = parent.getColumns ();
+        for (int i=0; i<order.length; i++) {
+            TableColumn column = columns [order [i]];
+            if (moved && !column.isDisposed ()) {
+                column.updateToolTip (order [i]);
+                column.sendEvent (DWT.Move);
+            }
+            if (column is this) moved = true;
+        }
+    }
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+}
+
+void releaseParent () {
+    super.releaseParent ();
+    if (parent.sortColumn is this) {
+        parent.sortColumn = null;
+    }
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is moved or resized.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #addControlListener
+ */
+public void removeControlListener (ControlListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Move, listener);
+    eventTable.unhook (DWT.Resize, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Controls how text and images will be displayed in the receiver.
+ * The argument should be one of <code>LEFT</code>, <code>RIGHT</code>
+ * or <code>CENTER</code>.
+ *
+ * @param alignment the new alignment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setAlignment (int alignment) {
+    checkWidget ();
+    if ((alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER)) is 0) return;
+    int index = parent.indexOf (this);
+    if (index is -1 || index is 0) return;
+    style &= ~(DWT.LEFT | DWT.RIGHT | DWT.CENTER);
+    style |= alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER);
+    int hwnd = parent.handle;
+    LVCOLUMN lvColumn = new LVCOLUMN ();
+    lvColumn.mask = OS.LVCF_FMT | OS.LVCF_IMAGE;
+    OS.SendMessage (hwnd, OS.LVM_GETCOLUMN, index, lvColumn);
+    lvColumn.fmt &= ~OS.LVCFMT_JUSTIFYMASK;
+    int fmt = 0;
+    if ((style & DWT.LEFT) is DWT.LEFT) fmt = OS.LVCFMT_LEFT;
+    if ((style & DWT.CENTER) is DWT.CENTER) fmt = OS.LVCFMT_CENTER;
+    if ((style & DWT.RIGHT) is DWT.RIGHT) fmt = OS.LVCFMT_RIGHT;
+    lvColumn.fmt |= fmt;
+    OS.SendMessage (hwnd, OS.LVM_SETCOLUMN, index, lvColumn);
+    /*
+    * Bug in Windows.  When LVM_SETCOLUMN is used to change
+    * the alignment of a column, the column is not redrawn
+    * to show the new alignment.  The fix is to compute the
+    * visible rectangle for the column and redraw it.
+    */
+    if (index !is 0) {
+        parent.forceResize ();
+        RECT rect = new RECT (), headerRect = new RECT ();
+        OS.GetClientRect (hwnd, rect);
+        int hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0);
+        OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+        OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2);
+        rect.left = headerRect.left;
+        rect.right = headerRect.right;
+        OS.InvalidateRect (hwnd, rect, true);
+    }
+}
+
+public void setImage (Image image) {
+    checkWidget();
+    if (image !is null && image.isDisposed ()) {
+        error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    super.setImage (image);
+    if (parent.sortColumn !is this || parent.sortDirection !is DWT.NONE) {
+        setImage (image, false, false);
+    }
+}
+
+void setImage (Image image, bool sort, bool right) {
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    int hwnd = parent.handle;
+    if (OS.COMCTL32_MAJOR < 6) {
+        int hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0);
+        HDITEM hdItem = new HDITEM ();
+        hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE | OS.HDI_BITMAP;
+        OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+        hdItem.fmt &= ~OS.HDF_BITMAP_ON_RIGHT;
+        if (image !is null) {
+            if (sort) {
+                hdItem.mask &= ~OS.HDI_IMAGE;
+                hdItem.fmt &= ~OS.HDF_IMAGE;
+                hdItem.fmt |= OS.HDF_BITMAP;
+                hdItem.hbm = image.handle;
+            } else {
+                hdItem.mask &= ~OS.HDI_BITMAP;
+                hdItem.fmt &= ~OS.HDF_BITMAP;
+                hdItem.fmt |= OS.HDF_IMAGE;
+                hdItem.iImage = parent.imageIndexHeader (image);
+            }
+            if (right) hdItem.fmt |= OS.HDF_BITMAP_ON_RIGHT;
+        } else {
+            hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_BITMAP);
+        }
+        OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
+    } else {
+        LVCOLUMN lvColumn = new LVCOLUMN ();
+        lvColumn.mask = OS.LVCF_FMT | OS.LVCF_IMAGE;
+        OS.SendMessage (hwnd, OS.LVM_GETCOLUMN, index, lvColumn);
+        if (image !is null) {
+            lvColumn.fmt |= OS.LVCFMT_IMAGE;
+            lvColumn.iImage = parent.imageIndexHeader (image);
+            if (right) lvColumn.fmt |= OS.LVCFMT_BITMAP_ON_RIGHT;
+        } else {
+            lvColumn.mask &= ~OS.LVCF_IMAGE;
+            lvColumn.fmt &= ~(OS.LVCFMT_IMAGE | OS.LVCFMT_BITMAP_ON_RIGHT);
+        }
+        OS.SendMessage (hwnd, OS.LVM_SETCOLUMN, index, lvColumn);
+    }
+}
+
+/**
+ * Sets the moveable attribute.  A column that is
+ * moveable can be reordered by the user by dragging
+ * the header. A column that is not moveable cannot be
+ * dragged by the user but may be reordered
+ * by the programmer.
+ *
+ * @param moveable the moveable attribute
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#setColumnOrder(int[])
+ * @see Table#getColumnOrder()
+ * @see TableColumn#getMoveable()
+ * @see DWT#Move
+ *
+ * @since 3.1
+ */
+public void setMoveable (bool moveable) {
+    checkWidget ();
+    this.moveable = moveable;
+    parent.updateMoveable ();
+}
+
+/**
+ * Sets the resizable attribute.  A column that is
+ * resizable can be resized by the user dragging the
+ * edge of the header.  A column that is not resizable
+ * cannot be dragged by the user but may be resized
+ * by the programmer.
+ *
+ * @param resizable the resize attribute
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setResizable (bool resizable) {
+    checkWidget ();
+    this.resizable = resizable;
+}
+
+void setSortDirection (int direction) {
+    if (OS.COMCTL32_MAJOR >= 6) {
+        int index = parent.indexOf (this);
+        if (index is -1) return;
+        int hwnd = parent.handle;
+        int hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0);
+        HDITEM hdItem = new HDITEM ();
+        hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE;
+        OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+        switch (direction) {
+            case DWT.UP:
+                hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_SORTDOWN);
+                hdItem.fmt |= OS.HDF_SORTUP;
+                break;
+            case DWT.DOWN:
+                hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_SORTUP);
+                hdItem.fmt |= OS.HDF_SORTDOWN;
+                break;
+            case DWT.NONE:
+                hdItem.fmt &= ~(OS.HDF_SORTUP | OS.HDF_SORTDOWN);
+                if (image !is null) {
+                    hdItem.fmt |= OS.HDF_IMAGE;
+                    hdItem.iImage = parent.imageIndexHeader (image);
+                } else {
+                    hdItem.fmt &= ~OS.HDF_IMAGE;
+                    hdItem.mask &= ~OS.HDI_IMAGE;
+                }
+                break;
+        }
+        OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
+        /*
+        * Bug in Windows.  When LVM_SETSELECTEDCOLUMN is used to
+        * specify a selected column, Windows does not redraw either
+        * the new or the previous selected column.  The fix is to
+        * force a redraw of both.
+        *
+        * Feature in Windows.  When LVM_SETBKCOLOR is used with
+        * CLR_NONE and LVM_SETSELECTEDCOLUMN is used to select
+        * a column, Windows fills the column with the selection
+        * color, drawing on top of the background image and any
+        * other custom drawing.  The fix is to avoid setting the
+        * selected column.
+        */
+        parent.forceResize ();
+        RECT rect = new RECT ();
+        OS.GetClientRect (hwnd, rect);
+        if (OS.SendMessage (hwnd, OS.LVM_GETBKCOLOR, 0, 0) !is OS.CLR_NONE) {
+            int oldColumn = OS.SendMessage (hwnd, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
+            int newColumn = direction is DWT.NONE ? -1 : index;
+            OS.SendMessage (hwnd, OS.LVM_SETSELECTEDCOLUMN, newColumn, 0);
+            RECT headerRect = new RECT ();
+            if (oldColumn !is -1) {
+                if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, oldColumn, headerRect) !is 0) {
+                    OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2);
+                    rect.left = headerRect.left;
+                    rect.right = headerRect.right;
+                    OS.InvalidateRect (hwnd, rect, true);
+                }
+            }
+        }
+        RECT headerRect = new RECT ();
+        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) !is 0) {
+            OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2);
+            rect.left = headerRect.left;
+            rect.right = headerRect.right;
+            OS.InvalidateRect (hwnd, rect, true);
+        }
+    } else {
+        switch (direction) {
+            case DWT.UP:
+            case DWT.DOWN:
+                setImage (display.getSortImage (direction), true, true);
+                break;
+            case DWT.NONE:
+                setImage (image, false, false);
+                break;
+        }
+    }
+}
+
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (string.equals (text)) return;
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    super.setText (string);
+
+    /*
+    * Bug in Windows.  For some reason, when the title
+    * of a column is changed after the column has been
+    * created, the alignment must also be reset or the
+    * text does not draw.  The fix is to query and then
+    * set the alignment.
+    */
+    int hwnd = parent.handle;
+    LVCOLUMN lvColumn = new LVCOLUMN ();
+    lvColumn.mask = OS.LVCF_FMT;
+    OS.SendMessage (hwnd, OS.LVM_GETCOLUMN, index, lvColumn);
+
+    /*
+    * Bug in Windows.  When a column header contains a
+    * mnemonic character, Windows does not measure the
+    * text properly.  This causes '...' to always appear
+    * at the end of the text.  The fix is to remove
+    * mnemonic characters and replace doubled mnemonics
+    * with spaces.
+    */
+    int hHeap = OS.GetProcessHeap ();
+    TCHAR buffer = new TCHAR (parent.getCodePage (), fixMnemonic (string), true);
+    int byteCount = buffer.length () * TCHAR.sizeof;
+    int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+    OS.MoveMemory (pszText, buffer, byteCount);
+    lvColumn.mask |= OS.LVCF_TEXT;
+    lvColumn.pszText = pszText;
+    int result = OS.SendMessage (hwnd, OS.LVM_SETCOLUMN, index, lvColumn);
+    if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+    if (result is 0) error (DWT.ERROR_CANNOT_SET_TEXT);
+}
+
+/**
+ * Sets the receiver's tool tip text to the argument, which
+ * may be null indicating that no tool tip text should be shown.
+ *
+ * @param string the new tool tip text (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setToolTipText (String string) {
+    checkWidget();
+    toolTipText = string;
+    int hwndHeaderToolTip = parent.headerToolTipHandle;
+    if (hwndHeaderToolTip is 0) {
+        parent.createHeaderToolTips ();
+        parent.updateHeaderToolTips ();
+    }
+}
+
+/**
+ * Sets the width of the receiver.
+ *
+ * @param width the new width
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setWidth (int width) {
+    checkWidget ();
+    if (width < 0) return;
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    int hwnd = parent.handle;
+    OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, width);
+}
+
+void updateToolTip (int index) {
+    int hwndHeaderToolTip = parent.headerToolTipHandle;
+    if (hwndHeaderToolTip !is 0) {
+        int hwnd = parent.handle;
+        int hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0);
+        RECT rect = new RECT ();
+        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) !is 0) {
+            TOOLINFO lpti = new TOOLINFO ();
+            lpti.cbSize = TOOLINFO.sizeof;
+            lpti.hwnd = hwndHeader;
+            lpti.uId = id;
+            lpti.left = rect.left;
+            lpti.top = rect.top;
+            lpti.right = rect.right;
+            lpti.bottom = rect.bottom;
+            OS.SendMessage (hwndHeaderToolTip, OS.TTM_NEWTOOLRECT, 0, lpti);
+        }
+    }
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/TableItem.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1244 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.TableItem;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+class TableItem : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.LVITEM;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class represent a selectable user interface object
+ * that represents an item in a table.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class TableItem extends Item {
+    Table parent;
+    String [] strings;
+    Image [] images;
+    bool checked, grayed, cached;
+    int imageIndent, background = -1, foreground = -1, font = -1;
+    int [] cellBackground, cellForeground, cellFont;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Table</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TableItem (Table parent, int style) {
+    this (parent, style, checkNull (parent).getItemCount (), true);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Table</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TableItem (Table parent, int style, int index) {
+    this (parent, style, index, true);
+}
+
+TableItem (Table parent, int style, int index, bool create) {
+    super (parent, style);
+    this.parent = parent;
+    if (create) parent.createItem (this, index);
+}
+
+static Table checkNull (Table control) {
+    if (control is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    return control;
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+void clear () {
+    text = "";
+    image = null;
+    strings = null;
+    images = null;
+    imageIndent = 0;
+    checked = grayed = false;
+    background = foreground = font = -1;
+    cellBackground = cellForeground = cellFont = null;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = false;
+}
+
+void destroyWidget () {
+    parent.destroyItem (this);
+    releaseHandle ();
+}
+
+/**
+ * Returns the receiver's background color.
+ *
+ * @return the background color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public Color getBackground () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (background is -1) return parent.getBackground ();
+    return Color.win32_new (display, background);
+}
+
+/**
+ * Returns the background color at the given column index in the receiver.
+ *
+ * @param index the column index
+ * @return the background color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Color getBackground (int index) {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return getBackground ();
+    int pixel = cellBackground !is null ? cellBackground [index] : -1;
+    return pixel is -1 ? getBackground () : Color.win32_new (display, pixel);
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent.
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public Rectangle getBounds () {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int itemIndex = parent.indexOf (this);
+    if (itemIndex is -1) return new Rectangle (0, 0, 0, 0);
+    RECT rect = getBounds (itemIndex, 0, true, false, false);
+    int width = rect.right - rect.left, height = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent at a column in the table.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding column rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int itemIndex = parent.indexOf (this);
+    if (itemIndex is -1) return new Rectangle (0, 0, 0, 0);
+    RECT rect = getBounds (itemIndex, index, true, true, true);
+    int width = rect.right - rect.left, height = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+RECT getBounds (int row, int column, bool getText, bool getImage, bool fullText) {
+    return getBounds (row, column, getText, getImage, fullText, false, 0);
+}
+
+RECT getBounds (int row, int column, bool getText, bool getImage, bool fullText, bool fullImage, int hDC) {
+    if (!getText && !getImage) return new RECT ();
+    int columnCount = parent.getColumnCount ();
+    if (!(0 <= column && column < Math.max (1, columnCount))) {
+        return new RECT ();
+    }
+    if (parent.fixScrollWidth) parent.setScrollWidth (null, true);
+    RECT rect = new RECT ();
+    int hwnd = parent.handle;
+    int bits = OS.SendMessage (hwnd, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+    if (column is 0 && (bits & OS.LVS_EX_FULLROWSELECT) is 0) {
+        if (parent.explorerTheme) {
+            rect.left = OS.LVIR_ICON;
+            parent.ignoreCustomDraw = true;
+            int code = OS.SendMessage (hwnd, OS. LVM_GETITEMRECT, row, rect);
+            parent.ignoreCustomDraw = false;
+            if (code is 0) return new RECT ();
+            if (getText) {
+                int width = 0;
+                int hFont = cellFont !is null ? cellFont [column] : -1;
+                if (hFont is -1) hFont = font;
+                if (hFont is -1 && hDC is 0) {
+                    TCHAR buffer = new TCHAR (parent.getCodePage (), text, true);
+                    width = OS.SendMessage (hwnd, OS.LVM_GETSTRINGWIDTH, 0, buffer);
+                } else {
+                    TCHAR buffer = new TCHAR (parent.getCodePage (), text, false);
+                    int textDC = hDC !is 0 ? hDC : OS.GetDC (hwnd), oldFont = -1;
+                    if (hDC is 0) {
+                        if (hFont is -1) hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0);
+                        oldFont = OS.SelectObject (textDC, hFont);
+                    }
+                    RECT textRect = new RECT ();
+                    int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_CALCRECT;
+                    OS.DrawText (textDC, buffer, buffer.length (), textRect, flags);
+                    width = textRect.right - textRect.left;
+                    if (hDC is 0) {
+                        if (oldFont !is -1) OS.SelectObject (textDC, oldFont);
+                        OS.ReleaseDC (hwnd, textDC);
+                    }
+                }
+                if (!getImage) rect.left = rect.right;
+                rect.right += width + Table.INSET * 2;
+            }
+        } else {
+            if (getText) {
+                rect.left = OS.LVIR_SELECTBOUNDS;
+                parent.ignoreCustomDraw = true;
+                int code = OS.SendMessage (hwnd, OS.LVM_GETITEMRECT, row, rect);
+                parent.ignoreCustomDraw = false;
+                if (code is 0) return new RECT ();
+                if (!getImage) {
+                    RECT iconRect = new RECT ();
+                    iconRect.left = OS.LVIR_ICON;
+                    parent.ignoreCustomDraw = true;
+                    code = OS.SendMessage (hwnd, OS. LVM_GETITEMRECT, row, iconRect);
+                    parent.ignoreCustomDraw = false;
+                    if (code !is 0) rect.left = iconRect.right;
+                }
+            } else {
+                rect.left = OS.LVIR_ICON;
+                parent.ignoreCustomDraw = true;
+                int code = OS.SendMessage (hwnd, OS.LVM_GETITEMRECT, row, rect);
+                parent.ignoreCustomDraw = false;
+                if (code is 0) return new RECT ();
+            }
+        }
+        if (fullText || fullImage) {
+            RECT headerRect = new RECT ();
+            int hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0);
+            OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, 0, headerRect);
+            OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2);
+            if (getText && fullText) rect.right = headerRect.right;
+            if (getImage && fullImage) rect.left = headerRect.left;
+        }
+    } else {
+        /*
+        * Feature in Windows.  LVM_GETSUBITEMRECT returns an image width
+        * even when the subitem does not contain an image.  The fix is to
+        * test for this case and adjust the rectangle to represent the area
+        * the table is actually drawing.
+        */
+        bool hasImage = (column is 0 && image !is null) || (images !is null && images [column] !is null);
+        rect.top = column;
+        if (fullText || fullImage || hDC is 0) {
+            /*
+            * Bug in Windows.  Despite the fact that the documentation states
+            * that LVIR_BOUNDS and LVIR_LABEL are identical when used with
+            * LVM_GETSUBITEMRECT, LVIR_BOUNDS can return a zero height.  The
+            * fix is to use LVIR_LABEL.
+            */
+            rect.left = getText ? OS.LVIR_LABEL : OS.LVIR_ICON;
+            parent.ignoreCustomDraw = true;
+            int code = OS.SendMessage (hwnd, OS. LVM_GETSUBITEMRECT, row, rect);
+            parent.ignoreCustomDraw = false;
+            if (code is 0) return new RECT ();
+            /*
+            * Feature in Windows.  Calling LVM_GETSUBITEMRECT with LVIR_LABEL
+            * and zero for the column number gives the bounds of the first item
+            * without including the bounds of the icon.  This is undocumented.
+            * When called with values greater than zero, the icon bounds are
+            * included and this behavior is documented.  If the icon is needed
+            * in the bounds of the first item, the fix is to adjust the item
+            * bounds using the icon bounds.
+            */
+            if (column is 0 && getText && getImage) {
+                RECT iconRect = new RECT ();
+                iconRect.left = OS.LVIR_ICON;
+                parent.ignoreCustomDraw = true;
+                code = OS.SendMessage (hwnd, OS. LVM_GETSUBITEMRECT, row, iconRect);
+                parent.ignoreCustomDraw = false;
+                if (code !is 0) rect.left = iconRect.left;
+            }
+            if (hasImage) {
+                if (column !is 0 && getText && !getImage) {
+                    RECT iconRect = new RECT ();
+                    iconRect.top = column;
+                    iconRect.left = OS.LVIR_ICON;
+                    if (OS.SendMessage (hwnd, OS. LVM_GETSUBITEMRECT, row, iconRect) !is 0) {
+                        rect.left = iconRect.right + Table.INSET / 2;
+                    }
+                }
+            } else {
+                if (getImage && !getText) rect.right = rect.left;
+            }
+            if (column is 0 && fullImage) {
+                RECT headerRect = new RECT ();
+                int hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0);
+                OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, 0, headerRect);
+                OS.MapWindowPoints (hwndHeader, hwnd, headerRect, 2);
+                rect.left = headerRect.left;
+            }
+        } else {
+            rect.left = OS.LVIR_ICON;
+            parent.ignoreCustomDraw = true;
+            int code = OS.SendMessage (hwnd, OS. LVM_GETSUBITEMRECT, row, rect);
+            parent.ignoreCustomDraw = false;
+            if (code is 0) return new RECT ();
+            if (!hasImage) rect.right = rect.left;
+            if (getText) {
+                String string = column is 0 ? text : strings !is null ? strings [column] : null;
+                if (string !is null) {
+                    RECT textRect = new RECT ();
+                    TCHAR buffer = new TCHAR (parent.getCodePage (), string, false);
+                    int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_CALCRECT;
+                    OS.DrawText (hDC, buffer, buffer.length (), textRect, flags);
+                    rect.right += textRect.right - textRect.left + Table.INSET * 3 + 1;
+                }
+            }
+        }
+    }
+    /*
+    * Bug in Windows.  In version 5.80 of COMCTL32.DLL, the top
+    * of the rectangle returned by LVM_GETSUBITEMRECT is off by
+    * the grid width when the grid is visible.  The fix is to
+    * move the top of the rectangle up by the grid width.
+    */
+    int gridWidth = parent.getLinesVisible () ? Table.GRID_WIDTH : 0;
+    if (OS.COMCTL32_VERSION >= OS.VERSION (5, 80)) rect.top -= gridWidth;
+    if (column !is 0) rect.left += gridWidth;
+    rect.right = Math.max (rect.right, rect.left);
+    rect.top += gridWidth;
+    rect.bottom = Math.max (rect.bottom - gridWidth, rect.top);
+    return rect;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is checked,
+ * and false otherwise.  When the parent does not have
+ * the <code>CHECK</code> style, return false.
+ *
+ * @return the checked state of the checkbox
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getChecked () {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if ((parent.style & DWT.CHECK) is 0) return false;
+    return checked;
+}
+
+/**
+ * Returns the font that the receiver will use to paint textual information for this item.
+ *
+ * @return the receiver's font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Font getFont () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    return font is -1 ? parent.getFont () : Font.win32_new (display, font);
+}
+
+/**
+ * Returns the font that the receiver will use to paint textual information
+ * for the specified cell in this item.
+ *
+ * @param index the column index
+ * @return the receiver's font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Font getFont (int index) {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count -1) return getFont ();
+    int hFont = (cellFont !is null) ? cellFont [index] : font;
+    return hFont is -1 ? getFont () : Font.win32_new (display, hFont);
+}
+
+/**
+ * Returns the foreground color that the receiver will use to draw.
+ *
+ * @return the receiver's foreground color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public Color getForeground () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (foreground is -1) return parent.getForeground ();
+    return Color.win32_new (display, foreground);
+}
+
+/**
+ *
+ * Returns the foreground color at the given column index in the receiver.
+ *
+ * @param index the column index
+ * @return the foreground color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Color getForeground (int index) {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count -1) return getForeground ();
+    int pixel = cellForeground !is null ? cellForeground [index] : -1;
+    return pixel is -1 ? getForeground () : Color.win32_new (display, pixel);
+}
+
+/**
+ * Returns <code>true</code> if the receiver is grayed,
+ * and false otherwise. When the parent does not have
+ * the <code>CHECK</code> style, return false.
+ *
+ * @return the grayed state of the checkbox
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getGrayed () {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if ((parent.style & DWT.CHECK) is 0) return false;
+    return grayed;
+}
+
+public Image getImage () {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    return super.getImage ();
+}
+
+/**
+ * Returns the image stored at the given column index in the receiver,
+ * or null if the image has not been set or if the column does not exist.
+ *
+ * @param index the column index
+ * @return the image stored at the given column index in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getImage (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (index is 0) return getImage ();
+    if (images !is null) {
+        if (0 <= index && index < images.length) return images [index];
+    }
+    return null;
+}
+
+/**
+ * Returns a rectangle describing the size and location
+ * relative to its parent of an image at a column in the
+ * table.  An empty rectangle is returned if index exceeds
+ * the index of the table's last column.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding image rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getImageBounds (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int itemIndex = parent.indexOf (this);
+    if (itemIndex is -1) return new Rectangle (0, 0, 0, 0);
+    RECT rect = getBounds (itemIndex, index, false, true, false);
+    int width = rect.right - rect.left, height = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+/**
+ * Gets the image indent.
+ *
+ * @return the indent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getImageIndent () {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    return imageIndent;
+}
+
+String getNameText () {
+    if ((parent.style & DWT.VIRTUAL) !is 0) {
+        if (!cached) return "*virtual*"; //$NON-NLS-1$
+    }
+    return super.getNameText ();
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Table</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Table getParent () {
+    checkWidget();
+    return parent;
+}
+
+public String getText () {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    return super.getText ();
+}
+
+/**
+ * Returns the text stored at the given column index in the receiver,
+ * or empty string if the text has not been set.
+ *
+ * @param index the column index
+ * @return the text stored at the given column index in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (index is 0) return getText ();
+    if (strings !is null) {
+        if (0 <= index && index < strings.length) {
+            String string = strings [index];
+            return string !is null ? string : "";
+        }
+    }
+    return "";
+}
+
+/**
+ * Returns a rectangle describing the size and location
+ * relative to its parent of the text at a column in the
+ * table.  An empty rectangle is returned if index exceeds
+ * the index of the table's last column.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding text rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public Rectangle getTextBounds (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int itemIndex = parent.indexOf (this);
+    if (itemIndex is -1) return new Rectangle (0, 0, 0, 0);
+    RECT rect = getBounds (itemIndex, index, true, false, true);
+    rect.left += 2;
+    if (index !is 0) rect.left += Table.INSET;
+    rect.left = Math.min (rect.left, rect.right);
+    rect.right = rect.right - Table.INSET;
+    int width = Math.max (0, rect.right - rect.left);
+    int height = Math.max (0, rect.bottom - rect.top);
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+void redraw () {
+    if (parent.currentItem is this || parent.drawCount !is 0) return;
+    int hwnd = parent.handle;
+    if (!OS.IsWindowVisible (hwnd)) return;
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    OS.SendMessage (hwnd, OS.LVM_REDRAWITEMS, index, index);
+}
+
+void redraw (int column, bool drawText, bool drawImage) {
+    if (parent.currentItem is this || parent.drawCount !is 0) return;
+    int hwnd = parent.handle;
+    if (!OS.IsWindowVisible (hwnd)) return;
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    RECT rect = getBounds (index, column, drawText, drawImage, true);
+    OS.InvalidateRect (hwnd, rect, true);
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    strings = null;
+    images = null;
+    cellBackground = cellForeground = cellFont = null;
+}
+
+/**
+ * Sets the receiver's background color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public void setBackground (Color color) {
+    checkWidget ();
+    if (color !is null && color.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int pixel = -1;
+    if (color !is null) {
+        parent.setCustomDraw (true);
+        pixel = color.handle;
+    }
+    if (background is pixel) return;
+    background = pixel;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    redraw ();
+}
+
+/**
+ * Sets the background color at the given column index in the receiver
+ * to the color specified by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param index the column index
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setBackground (int index, Color color) {
+    checkWidget ();
+    if (color !is null && color.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    int pixel = -1;
+    if (color !is null) {
+        parent.setCustomDraw (true);
+        pixel = color.handle;
+    }
+    if (cellBackground is null) {
+        cellBackground = new int [count];
+        for (int i = 0; i < count; i++) {
+            cellBackground [i] = -1;
+        }
+    }
+    if (cellBackground [index] is pixel) return;
+    cellBackground [index] = pixel;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    redraw (index, true, true);
+}
+
+/**
+ * Sets the checked state of the checkbox for this item.  This state change
+ * only applies if the Table was created with the DWT.CHECK style.
+ *
+ * @param checked the new checked state of the checkbox
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setChecked (bool checked) {
+    checkWidget();
+    if ((parent.style & DWT.CHECK) is 0) return;
+    if (this.checked is checked) return;
+    setChecked (checked, false);
+}
+
+void setChecked (bool checked, bool notify) {
+    this.checked = checked;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    if (notify) {
+        Event event = new Event();
+        event.item = this;
+        event.detail = DWT.CHECK;
+        parent.postEvent (DWT.Selection, event);
+    }
+    redraw ();
+}
+
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * for this item to the font specified by the argument, or to the default font
+ * for that kind of control if the argument is null.
+ *
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setFont (Font font){
+    checkWidget ();
+    if (font !is null && font.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int hFont = -1;
+    if (font !is null) {
+        parent.setCustomDraw (true);
+        hFont = font.handle;
+    }
+    if (this.font is hFont) return;
+    this.font = hFont;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    /*
+    * Bug in Windows.  Despite the fact that every item in the
+    * table always has LPSTR_TEXTCALLBACK, Windows caches the
+    * bounds for the selected items.  This means that
+    * when you change the string to be something else, Windows
+    * correctly asks you for the new string but when the item
+    * is selected, the selection draws using the bounds of the
+    * previous item.  The fix is to reset LPSTR_TEXTCALLBACK
+    * even though it has not changed, causing Windows to flush
+    * cached bounds.
+    */
+    if ((parent.style & DWT.VIRTUAL) is 0 && cached) {
+        int itemIndex = parent.indexOf (this);
+        if (itemIndex !is -1) {
+            int hwnd = parent.handle;
+            LVITEM lvItem = new LVITEM ();
+            lvItem.mask = OS.LVIF_TEXT;
+            lvItem.iItem = itemIndex;
+            lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+            OS.SendMessage (hwnd, OS.LVM_SETITEM, 0, lvItem);
+            cached = false;
+        }
+    }
+    parent.setScrollWidth (this, false);
+    redraw ();
+}
+
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * for the specified cell in this item to the font specified by the
+ * argument, or to the default font for that kind of control if the
+ * argument is null.
+ *
+ * @param index the column index
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setFont (int index, Font font) {
+    checkWidget ();
+    if (font !is null && font.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    int hFont = -1;
+    if (font !is null) {
+        parent.setCustomDraw (true);
+        hFont = font.handle;
+    }
+    if (cellFont is null) {
+        cellFont = new int [count];
+        for (int i = 0; i < count; i++) {
+            cellFont [i] = -1;
+        }
+    }
+    if (cellFont [index] is hFont) return;
+    cellFont [index] = hFont;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    if (index is 0) {
+        /*
+        * Bug in Windows.  Despite the fact that every item in the
+        * table always has LPSTR_TEXTCALLBACK, Windows caches the
+        * bounds for the selected items.  This means that
+        * when you change the string to be something else, Windows
+        * correctly asks you for the new string but when the item
+        * is selected, the selection draws using the bounds of the
+        * previous item.  The fix is to reset LPSTR_TEXTCALLBACK
+        * even though it has not changed, causing Windows to flush
+        * cached bounds.
+        */
+        if ((parent.style & DWT.VIRTUAL) is 0 && cached) {
+            int itemIndex = parent.indexOf (this);
+            if (itemIndex !is -1) {
+                int hwnd = parent.handle;
+                LVITEM lvItem = new LVITEM ();
+                lvItem.mask = OS.LVIF_TEXT;
+                lvItem.iItem = itemIndex;
+                lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+                OS.SendMessage (hwnd, OS.LVM_SETITEM, 0, lvItem);
+                cached = false;
+            }
+        }
+        parent.setScrollWidth (this, false);
+    }
+    redraw (index, true, false);
+}
+
+/**
+ * Sets the receiver's foreground color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public void setForeground (Color color){
+    checkWidget ();
+    if (color !is null && color.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int pixel = -1;
+    if (color !is null) {
+        parent.setCustomDraw (true);
+        pixel = color.handle;
+    }
+    if (foreground is pixel) return;
+    foreground = pixel;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    redraw ();
+}
+
+/**
+ * Sets the foreground color at the given column index in the receiver
+ * to the color specified by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param index the column index
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setForeground (int index, Color color){
+    checkWidget ();
+    if (color !is null && color.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    int pixel = -1;
+    if (color !is null) {
+        parent.setCustomDraw (true);
+        pixel = color.handle;
+    }
+    if (cellForeground is null) {
+        cellForeground = new int [count];
+        for (int i = 0; i < count; i++) {
+            cellForeground [i] = -1;
+        }
+    }
+    if (cellForeground [index] is pixel) return;
+    cellForeground [index] = pixel;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    redraw (index, true, false);
+}
+
+/**
+ * Sets the grayed state of the checkbox for this item.  This state change
+ * only applies if the Table was created with the DWT.CHECK style.
+ *
+ * @param grayed the new grayed state of the checkbox;
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setGrayed (bool grayed) {
+    checkWidget();
+    if ((parent.style & DWT.CHECK) is 0) return;
+    if (this.grayed is grayed) return;
+    this.grayed = grayed;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    redraw ();
+}
+
+/**
+ * Sets the image for multiple columns in the table.
+ *
+ * @param images the array of new images
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of images is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if one of the images has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (Image [] images) {
+    checkWidget();
+    if (images is null) error (DWT.ERROR_NULL_ARGUMENT);
+    for (int i=0; i<images.length; i++) {
+        setImage (i, images [i]);
+    }
+}
+
+/**
+ * Sets the receiver's image at a column.
+ *
+ * @param index the column index
+ * @param image the new image
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (int index, Image image) {
+    checkWidget();
+    if (image !is null && image.isDisposed ()) {
+        error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    Image oldImage = null;
+    if (index is 0) {
+        if (image !is null && image.type is DWT.ICON) {
+            if (image.equals (this.image)) return;
+        }
+        oldImage = this.image;
+        super.setImage (image);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    if (images is null && index !is 0) {
+        images = new Image [count];
+        images [0] = image;
+    }
+    if (images !is null) {
+        if (image !is null && image.type is DWT.ICON) {
+            if (image.equals (images [index])) return;
+        }
+        oldImage = images [index];
+        images [index] = image;
+    }
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+
+    /* Ensure that the image list is created */
+    parent.imageIndex (image, index);
+
+    if (index is 0) parent.setScrollWidth (this, false);
+    bool drawText = (image is null && oldImage !is null) || (image !is null && oldImage is null);
+    redraw (index, drawText, true);
+}
+
+public void setImage (Image image) {
+    checkWidget ();
+    setImage (0, image);
+}
+
+/**
+ * Sets the indent of the first column's image, expressed in terms of the image's width.
+ *
+ * @param indent the new indent
+ *
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @deprecated this functionality is not supported on most platforms
+ */
+public void setImageIndent (int indent) {
+    checkWidget();
+    if (indent < 0) return;
+    if (imageIndent is indent) return;
+    imageIndent = indent;
+    if ((parent.style & DWT.VIRTUAL) !is 0) {
+        cached = true;
+    } else {
+        int index = parent.indexOf (this);
+        if (index !is -1) {
+            int hwnd = parent.handle;
+            LVITEM lvItem = new LVITEM ();
+            lvItem.mask = OS.LVIF_INDENT;
+            lvItem.iItem = index;
+            lvItem.iIndent = indent;
+            OS.SendMessage (hwnd, OS.LVM_SETITEM, 0, lvItem);
+        }
+    }
+    parent.setScrollWidth (this, false);
+    redraw ();
+}
+
+/**
+ * Sets the text for multiple columns in the table.
+ *
+ * @param strings the array of new strings
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String [] strings) {
+    checkWidget();
+    if (strings is null) error (DWT.ERROR_NULL_ARGUMENT);
+    for (int i=0; i<strings.length; i++) {
+        String string = strings [i];
+        if (string !is null) setText (i, string);
+    }
+}
+
+/**
+ * Sets the receiver's text at a column
+ *
+ * @param index the column index
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (int index, String string) {
+    checkWidget();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (index is 0) {
+        if (string.equals (text)) return;
+        super.setText (string);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    if (strings is null && index !is 0)  {
+        strings = new String [count];
+        strings [0] = text;
+    }
+    if (strings !is null) {
+        if (string.equals (strings [index])) return;
+        strings [index] = string;
+    }
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    if (index is 0) {
+        /*
+        * Bug in Windows.  Despite the fact that every item in the
+        * table always has LPSTR_TEXTCALLBACK, Windows caches the
+        * bounds for the selected items.  This means that
+        * when you change the string to be something else, Windows
+        * correctly asks you for the new string but when the item
+        * is selected, the selection draws using the bounds of the
+        * previous item.  The fix is to reset LPSTR_TEXTCALLBACK
+        * even though it has not changed, causing Windows to flush
+        * cached bounds.
+        */
+        if ((parent.style & DWT.VIRTUAL) is 0 && cached) {
+            int itemIndex = parent.indexOf (this);
+            if (itemIndex !is -1) {
+                int hwnd = parent.handle;
+                LVITEM lvItem = new LVITEM ();
+                lvItem.mask = OS.LVIF_TEXT;
+                lvItem.iItem = itemIndex;
+                lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+                OS.SendMessage (hwnd, OS.LVM_SETITEM, 0, lvItem);
+                cached = false;
+            }
+        }
+        parent.setScrollWidth (this, false);
+    }
+    redraw (index, true, false);
+}
+
+public void setText (String string) {
+    checkWidget();
+    setText (0, string);
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Text.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,2404 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Text;
+
+import dwt.widgets.Scrollable;
+class Text : Scrollable {
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.ModifyListener;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.events.VerifyListener;
+import dwt.graphics.Font;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.GUITHREADINFO;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MSG;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SHRGINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class are selectable user interface
+ * objects that allow the user to enter and modify text.
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>CANCEL, CENTER, LEFT, MULTI, PASSWORD, SEARCH, SINGLE, RIGHT, READ_ONLY, WRAP</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>DefaultSelection, Modify, Verify</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles MULTI and SINGLE may be specified,
+ * and only one of the styles LEFT, CENTER, and RIGHT may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+public class Text extends Scrollable {
+    int tabs, oldStart, oldEnd;
+    bool doubleClick, ignoreModify, ignoreVerify, ignoreCharacter;
+    String message;
+
+    /**
+    * The maximum number of characters that can be entered
+    * into a text widget.
+    * <p>
+    * Note that this value is platform dependent, based upon
+    * the native widget implementation.
+    * </p>
+    */
+    public static final int LIMIT;
+
+    /**
+    * The delimiter used by multi-line text widgets.  When text
+    * is queried and from the widget, it will be delimited using
+    * this delimiter.
+    */
+    public static final String DELIMITER;
+
+    /*
+    * This code is intentionally commented.
+    */
+//  static final char PASSWORD;
+
+    /*
+    * These values can be different on different platforms.
+    * Therefore they are not initialized in the declaration
+    * to stop the compiler from inlining.
+    */
+    static {
+        LIMIT = OS.IsWinNT ? 0x7FFFFFFF : 0x7FFF;
+        DELIMITER = "\r\n";
+    }
+
+    static final int EditProc;
+    static final TCHAR EditClass = new TCHAR (0, "EDIT", true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, EditClass, lpWndClass);
+        EditProc = lpWndClass.lpfnWndProc;
+        /*
+        * This code is intentionally commented.
+        */
+//      int hwndText = OS.CreateWindowEx (0,
+//          EditClass,
+//          null,
+//          OS.WS_OVERLAPPED | OS.ES_PASSWORD,
+//          0, 0, 0, 0,
+//          0,
+//          0,
+//          OS.GetModuleHandle (null),
+//          null);
+//      char echo = (char) OS.SendMessage (hwndText, OS.EM_GETPASSWORDCHAR, 0, 0);
+//      OS.DestroyWindow (hwndText);
+//      PASSWORD = echo !is 0 ? echo : '*';
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#SINGLE
+ * @see DWT#MULTI
+ * @see DWT#READ_ONLY
+ * @see DWT#WRAP
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Text (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    return OS.CallWindowProc (EditProc, hwnd, msg, wParam, lParam);
+}
+
+void createHandle () {
+    super.createHandle ();
+    OS.SendMessage (handle, OS.EM_LIMITTEXT, 0, 0);
+    if ((style & DWT.READ_ONLY) !is 0) {
+        if ((style & (DWT.BORDER | DWT.H_SCROLL | DWT.V_SCROLL)) is 0) {
+            state |= THEME_BACKGROUND;
+        }
+    }
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver's text is modified, by sending
+ * it one of the messages defined in the <code>ModifyListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ModifyListener
+ * @see #removeModifyListener
+ */
+public void addModifyListener (ModifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Modify, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is not called for texts.
+ * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed in a single-line text,
+ * or when ENTER is pressed in a search text. If the receiver has the <code>DWT.SEARCH | DWT.CANCEL</code> style
+ * and the user cancels the search, the event object detail field contains the value <code>DWT.CANCEL</code>.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the control is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver's text is verified, by sending
+ * it one of the messages defined in the <code>VerifyListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see VerifyListener
+ * @see #removeVerifyListener
+ */
+public void addVerifyListener (VerifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Verify, typedListener);
+}
+
+/**
+ * Appends a string.
+ * <p>
+ * The new text is appended to the text at
+ * the end of the widget.
+ * </p>
+ *
+ * @param string the string to be appended
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void append (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    string = Display.withCrLf (string);
+    int length = OS.GetWindowTextLength (handle);
+    if (hooks (DWT.Verify) || filters (DWT.Verify)) {
+        string = verifyText (string, length, length, null);
+        if (string is null) return;
+    }
+    OS.SendMessage (handle, OS.EM_SETSEL, length, length);
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    /*
+    * Feature in Windows.  When an edit control with ES_MULTILINE
+    * style that does not have the WS_VSCROLL style is full (i.e.
+    * there is no space at the end to draw any more characters),
+    * EM_REPLACESEL sends a WM_CHAR with a backspace character
+    * to remove any further text that is added.  This is an
+    * implementation detail of the edit control that is unexpected
+    * and can cause endless recursion when EM_REPLACESEL is sent
+    * from a WM_CHAR handler.  The fix is to ignore calling the
+    * handler from WM_CHAR.
+    */
+    ignoreCharacter = true;
+    OS.SendMessage (handle, OS.EM_REPLACESEL, 0, buffer);
+    ignoreCharacter = false;
+    OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
+}
+
+static int checkStyle (int style) {
+    if ((style & DWT.SEARCH) !is 0) {
+        style |= DWT.SINGLE | DWT.BORDER;
+        style &= ~DWT.PASSWORD;
+    }
+    if (OS.COMCTL32_MAJOR < 6) style &= ~DWT.SEARCH;
+    if ((style & DWT.SINGLE) !is 0 && (style & DWT.MULTI) !is 0) {
+        style &= ~DWT.MULTI;
+    }
+    style = checkBits (style, DWT.LEFT, DWT.CENTER, DWT.RIGHT, 0, 0, 0);
+    if ((style & DWT.SINGLE) !is 0) style &= ~(DWT.H_SCROLL | DWT.V_SCROLL | DWT.WRAP);
+    if ((style & DWT.WRAP) !is 0) {
+        style |= DWT.MULTI;
+        style &= ~DWT.H_SCROLL;
+    }
+    if ((style & DWT.MULTI) !is 0) style &= ~DWT.PASSWORD;
+    if ((style & (DWT.SINGLE | DWT.MULTI)) !is 0) return style;
+    if ((style & (DWT.H_SCROLL | DWT.V_SCROLL)) !is 0) return style | DWT.MULTI;
+    return style | DWT.SINGLE;
+}
+
+/**
+ * Clears the selection.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void clearSelection () {
+    checkWidget ();
+    if (OS.IsWinCE) {
+        /*
+        * Bug in WinCE.  Calling EM_SETSEL with -1 and 0 is equivalent
+        * to calling EM_SETSEL with 0 and -1.  It causes the entire
+        * text to be selected instead of clearing the selection.  The
+        * fix is to set the start of the selection to the  end of the
+        * current selection.
+        */
+        int [] end = new int [1];
+        OS.SendMessage (handle, OS.EM_GETSEL, (int []) null, end);
+        OS.SendMessage (handle, OS.EM_SETSEL, end [0], end [0]);
+    } else {
+        OS.SendMessage (handle, OS.EM_SETSEL, -1, 0);
+    }
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int height = 0, width = 0;
+    if (wHint is DWT.DEFAULT || hHint is DWT.DEFAULT) {
+        int newFont, oldFont = 0;
+        int hDC = OS.GetDC (handle);
+        newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+        TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+        OS.GetTextMetrics (hDC, tm);
+        int count = (style & DWT.SINGLE) !is 0 ? 1 : OS.SendMessage (handle, OS.EM_GETLINECOUNT, 0, 0);
+        height = count * tm.tmHeight;
+        RECT rect = new RECT ();
+        int flags = OS.DT_CALCRECT | OS.DT_EDITCONTROL | OS.DT_NOPREFIX;
+        bool wrap = (style & DWT.MULTI) !is 0 && (style & DWT.WRAP) !is 0;
+        if (wrap && wHint !is DWT.DEFAULT) {
+            flags |= OS.DT_WORDBREAK;
+            rect.right = wHint;
+        }
+        int length = OS.GetWindowTextLength (handle);
+        if (length !is 0) {
+            TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+            OS.GetWindowText (handle, buffer, length + 1);
+            OS.DrawText (hDC, buffer, length, rect, flags);
+            width = rect.right - rect.left;
+        }
+        //This code is intentionally commented
+//      if (OS.COMCTL32_MAJOR >= 6) {
+//          if ((style & DWT.SEARCH) !is 0) {
+//              length = message.length ();
+//              if (length !is 0) {
+//                  char [] buffer = new char [length + 1];
+//                  message.getChars (0, length, buffer, 0);
+//                  SIZE size = new SIZE ();
+//                  OS.GetTextExtentPoint32W (hDC, buffer, length, size);
+//                  width = Math.max (width, size.cx);
+//              }
+//          }
+//      }
+        if (wrap && hHint is DWT.DEFAULT) {
+            int newHeight = rect.bottom - rect.top;
+            if (newHeight !is 0) height = newHeight;
+        }
+        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+        OS.ReleaseDC (handle, hDC);
+    }
+    if (width is 0) width = DEFAULT_WIDTH;
+    if (height is 0) height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    Rectangle trim = computeTrim (0, 0, width, height);
+    return new Point (trim.width, trim.height);
+}
+
+public Rectangle computeTrim (int x, int y, int width, int height) {
+    checkWidget ();
+    Rectangle rect = super.computeTrim (x, y, width, height);
+    /*
+    * The preferred height of a single-line text widget
+    * has been hand-crafted to be the same height as
+    * the single-line text widget in an editable combo
+    * box.
+    */
+    int margins = OS.SendMessage(handle, OS.EM_GETMARGINS, 0, 0);
+    rect.x -= margins & 0xFFFF;
+    rect.width += (margins & 0xFFFF) + ((margins >> 16) & 0xFFFF);
+    if ((style & DWT.H_SCROLL) !is 0) rect.width++;
+    if ((style & DWT.BORDER) !is 0) {
+        rect.x -= 1;
+        rect.y -= 1;
+        rect.width += 2;
+        rect.height += 2;
+    }
+    return rect;
+}
+
+/**
+ * Copies the selected text.
+ * <p>
+ * The current selection is copied to the clipboard.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void copy () {
+    checkWidget ();
+    OS.SendMessage (handle, OS.WM_COPY, 0, 0);
+}
+
+void createWidget () {
+    super.createWidget ();
+    message = "";
+    doubleClick = true;
+    setTabStops (tabs = 8);
+    fixAlignment ();
+}
+
+/**
+ * Cuts the selected text.
+ * <p>
+ * The current selection is first copied to the
+ * clipboard and then deleted from the widget.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void cut () {
+    checkWidget ();
+    if ((style & DWT.READ_ONLY) !is 0) return;
+    OS.SendMessage (handle, OS.WM_CUT, 0, 0);
+}
+
+int defaultBackground () {
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    return OS.GetSysColor ((bits & OS.ES_READONLY) !is 0 ? OS.COLOR_3DFACE : OS.COLOR_WINDOW);
+}
+
+bool dragDetect (int hwnd, int x, int y, bool filter, bool [] detect, bool [] consume) {
+    if (filter) {
+        int [] start = new int [1], end = new int [1];
+        OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+        if (start [0] !is end [0]) {
+            int lParam = (x & 0xFFFF) | ((y << 16) & 0xFFFF0000);
+            int position = OS.SendMessage (handle, OS.EM_CHARFROMPOS, 0, lParam) & 0xFFFF;
+            if (start [0] <= position && position < end [0]) {
+                if (super.dragDetect (hwnd, x, y, filter, detect, consume)) {
+                    if (consume !is null) consume [0] = true;
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    return super.dragDetect (hwnd, x, y, filter, detect, consume);
+}
+
+void fixAlignment () {
+    /*
+    * Feature in Windows.  When the edit control is not
+    * mirrored, it uses WS_EX_RIGHT, WS_EX_RTLREADING and
+    * WS_EX_LEFTSCROLLBAR to give the control a right to
+    * left appearance.  This causes the control to be lead
+    * aligned no matter what alignment was specified by
+    * the programmer.  For example, setting ES_RIGHT and
+    * WS_EX_LAYOUTRTL should cause the contents of the
+    * control to be left (trail) aligned in a mirrored world.
+    * When the orientation is changed by the user or
+    * specified by the programmer, WS_EX_RIGHT conflicts
+    * with the mirrored alignment.  The fix is to clear
+    * or set WS_EX_RIGHT to achieve the correct alignment
+    * according to the orientation and mirroring.
+    */
+    if ((style & DWT.MIRRORED) !is 0) return;
+    int bits1 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+    int bits2 = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((style & DWT.LEFT_TO_RIGHT) !is 0) {
+        /*
+        * Bug in Windows 98. When the edit control is created
+        * with the style ES_RIGHT it automatically sets the
+        * WS_EX_LEFTSCROLLBAR bit.  The fix is to clear the
+        * bit when the orientation of the control is left
+        * to right.
+        */
+        bits1 &= ~OS.WS_EX_LEFTSCROLLBAR;
+        if ((style & DWT.RIGHT) !is 0) {
+            bits1 |= OS.WS_EX_RIGHT;
+            bits2 |= OS.ES_RIGHT;
+        }
+        if ((style & DWT.LEFT) !is 0) {
+            bits1 &= ~OS.WS_EX_RIGHT;
+            bits2 &= ~OS.ES_RIGHT;
+        }
+    } else {
+        if ((style & DWT.RIGHT) !is 0) {
+            bits1 &= ~OS.WS_EX_RIGHT;
+            bits2 &= ~OS.ES_RIGHT;
+        }
+        if ((style & DWT.LEFT) !is 0) {
+            bits1 |= OS.WS_EX_RIGHT;
+            bits2 |= OS.ES_RIGHT;
+        }
+    }
+    if ((style & DWT.CENTER) !is 0) {
+        bits2 |= OS.ES_CENTER;
+    }
+    OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits1);
+    OS.SetWindowLong (handle, OS.GWL_STYLE, bits2);
+}
+
+public int getBorderWidth () {
+    checkWidget ();
+    /*
+    * Feature in Windows 2000 and XP.  Despite the fact that WS_BORDER
+    * is set when the edit control is created, the style bit is cleared.
+    * The fix is to avoid the check for WS_BORDER and use the DWT widget
+    * style bits instead.
+    */
+//  if ((style & DWT.BORDER) !is 0 && (style & DWT.FLAT) !is 0) {
+//      return OS.GetSystemMetrics (OS.SM_CXBORDER);
+//  }
+    return super.getBorderWidth ();
+}
+
+/**
+ * Returns the line number of the caret.
+ * <p>
+ * The line number of the caret is returned.
+ * </p>
+ *
+ * @return the line number
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getCaretLineNumber () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.EM_LINEFROMCHAR, -1, 0);
+}
+
+/**
+ * Returns a point describing the receiver's location relative
+ * to its parent (or its display if its parent is null).
+ * <p>
+ * The location of the caret is returned.
+ * </p>
+ *
+ * @return a point, the location of the caret
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getCaretLocation () {
+    checkWidget ();
+    /*
+    * Bug in Windows.  For some reason, Windows is unable
+    * to return the pixel coordinates of the last character
+    * in the widget.  The fix is to temporarily insert a
+    * space, query the coordinates and delete the space.
+    * The selection is always an i-beam in this case because
+    * this is the only time the start of the selection can
+    * be equal to the last character position in the widget.
+    * If EM_POSFROMCHAR fails for any other reason, return
+    * pixel coordinates (0,0).
+    */
+    int position = getCaretPosition ();
+    int caretPos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, position, 0);
+    if (caretPos is -1) {
+        caretPos = 0;
+        if (position >= OS.GetWindowTextLength (handle)) {
+            int cp = getCodePage ();
+            int [] start = new int [1], end = new int [1];
+            OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+            OS.SendMessage (handle, OS.EM_SETSEL, position, position);
+            /*
+            * Feature in Windows.  When an edit control with ES_MULTILINE
+            * style that does not have the WS_VSCROLL style is full (i.e.
+            * there is no space at the end to draw any more characters),
+            * EM_REPLACESEL sends a WM_CHAR with a backspace character
+            * to remove any further text that is added.  This is an
+            * implementation detail of the edit control that is unexpected
+            * and can cause endless recursion when EM_REPLACESEL is sent
+            * from a WM_CHAR handler.  The fix is to ignore calling the
+            * handler from WM_CHAR.
+            */
+            ignoreCharacter = ignoreModify = true;
+            OS.SendMessage (handle, OS.EM_REPLACESEL, 0, new TCHAR (cp, " ", true));
+            caretPos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, position, 0);
+            OS.SendMessage (handle, OS.EM_SETSEL, position, position + 1);
+            OS.SendMessage (handle, OS.EM_REPLACESEL, 0, new TCHAR (cp, "", true));
+            ignoreCharacter = ignoreModify = false;
+            OS.SendMessage (handle, OS.EM_SETSEL, start [0], start [0]);
+            OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]);
+        }
+    }
+    return new Point ((short) (caretPos & 0xFFFF), (short) (caretPos >> 16));
+}
+
+/**
+ * Returns the character position of the caret.
+ * <p>
+ * Indexing is zero based.
+ * </p>
+ *
+ * @return the position of the caret
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getCaretPosition () {
+    checkWidget ();
+    int [] start = new int [1], end = new int [1];
+    OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+    /*
+    * In Windows, there is no API to get the position of the caret
+    * when the selection is not an i-beam.  The best that can be done
+    * is to query the pixel position of the current caret and compare
+    * it to the pixel position of the start and end of the selection.
+    *
+    * NOTE:  This does not work when the i-beam belongs to another
+    * control.  In this case, guess that the i-beam is at the start
+    * of the selection.
+    */
+    int caret = start [0];
+    if (start [0] !is end [0]) {
+        int startLine = OS.SendMessage (handle, OS.EM_LINEFROMCHAR, start [0], 0);
+        int endLine = OS.SendMessage (handle, OS.EM_LINEFROMCHAR, end [0], 0);
+        if (startLine is endLine) {
+            if (!OS.IsWinCE) {
+                int idThread = OS.GetWindowThreadProcessId (handle, null);
+                GUITHREADINFO lpgui = new GUITHREADINFO ();
+                lpgui.cbSize = GUITHREADINFO.sizeof;
+                if (OS.GetGUIThreadInfo (idThread, lpgui)) {
+                    if (lpgui.hwndCaret is handle || lpgui.hwndCaret is 0) {
+                        POINT ptCurrentPos = new POINT ();
+                        if (OS.GetCaretPos (ptCurrentPos)) {
+                            int endPos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, end [0], 0);
+                            if (endPos is -1) {
+                                int startPos = OS.SendMessage (handle, OS.EM_POSFROMCHAR, start [0], 0);
+                                int startX = (short) (startPos & 0xFFFF);
+                                if (ptCurrentPos.x > startX) caret = end [0];
+                            } else {
+                                int endX = (short) (endPos & 0xFFFF);
+                                if (ptCurrentPos.x >= endX) caret = end [0];
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+            int caretPos = OS.SendMessage (handle, OS.EM_LINEINDEX, -1, 0);
+            int caretLine = OS.SendMessage (handle, OS.EM_LINEFROMCHAR, caretPos, 0);
+            if (caretLine is endLine) caret = end [0];
+        }
+    }
+    if (!OS.IsUnicode && OS.IsDBLocale) caret = mbcsToWcsPos (caret);
+    return caret;
+}
+
+/**
+ * Returns the number of characters.
+ *
+ * @return number of characters in the widget
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getCharCount () {
+    checkWidget ();
+    int length = OS.GetWindowTextLength (handle);
+    if (!OS.IsUnicode && OS.IsDBLocale) length = mbcsToWcsPos (length);
+    return length;
+}
+
+/**
+ * Returns the double click enabled flag.
+ * <p>
+ * The double click flag enables or disables the
+ * default action of the text widget when the user
+ * double clicks.
+ * </p>
+ *
+ * @return whether or not double click is enabled
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getDoubleClickEnabled () {
+    checkWidget ();
+    return doubleClick;
+}
+
+/**
+ * Returns the echo character.
+ * <p>
+ * The echo character is the character that is
+ * displayed when the user enters text or the
+ * text is changed by the programmer.
+ * </p>
+ *
+ * @return the echo character
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setEchoChar
+ */
+public char getEchoChar () {
+    checkWidget ();
+    char echo = (char) OS.SendMessage (handle, OS.EM_GETPASSWORDCHAR, 0, 0);
+    if (echo !is 0 && (echo = Display.mbcsToWcs (echo, getCodePage ())) is 0) echo = '*';
+    return echo;
+}
+
+/**
+ * Returns the editable state.
+ *
+ * @return whether or not the receiver is editable
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getEditable () {
+    checkWidget ();
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    return (bits & OS.ES_READONLY) is 0;
+}
+
+/**
+ * Returns the number of lines.
+ *
+ * @return the number of lines in the widget
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getLineCount () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.EM_GETLINECOUNT, 0, 0);
+}
+
+/**
+ * Returns the line delimiter.
+ *
+ * @return a string that is the line delimiter
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #DELIMITER
+ */
+public String getLineDelimiter () {
+    checkWidget ();
+    return DELIMITER;
+}
+
+/**
+ * Returns the height of a line.
+ *
+ * @return the height of a row of text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getLineHeight () {
+    checkWidget ();
+    int newFont, oldFont = 0;
+    int hDC = OS.GetDC (handle);
+    newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+    OS.GetTextMetrics (hDC, tm);
+    if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+    OS.ReleaseDC (handle, hDC);
+    return tm.tmHeight;
+}
+
+/**
+ * Returns the orientation of the receiver, which will be one of the
+ * constants <code>DWT.LEFT_TO_RIGHT</code> or <code>DWT.RIGHT_TO_LEFT</code>.
+ *
+ * @return the orientation style
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public int getOrientation () {
+    checkWidget();
+    return style & (DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT);
+}
+
+/**
+ * Returns the widget message. When the widget is created
+ * with the style <code>DWT.SEARCH</code>, the message text
+ * is displayed as a hint for the user, indicating the
+ * purpose of the field.
+ * <p>
+ * Note: This operation is a <em>HINT</em> and is not
+ * supported on platforms that do not have this concept.
+ * </p>
+ *
+ * @return the widget message
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public String getMessage () {
+    checkWidget ();
+    return message;
+}
+
+/**
+ * Returns the character position at the given point in the receiver
+ * or -1 if no such position exists. The point is in the coordinate
+ * system of the receiver.
+ * <p>
+ * Indexing is zero based.
+ * </p>
+ *
+ * @return the position of the caret
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+//TODO - Javadoc
+/*public*/ int getPosition (Point point) {
+    checkWidget();
+    if (point is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int lParam = (point.x & 0xFFFF) | ((point.y << 16) & 0xFFFF0000);
+    int position = OS.SendMessage (handle, OS.EM_CHARFROMPOS, 0, lParam) & 0xFFFF;
+    if (!OS.IsUnicode && OS.IsDBLocale) position = mbcsToWcsPos (position);
+    return position;
+}
+
+/**
+ * Returns a <code>Point</code> whose x coordinate is the
+ * character position representing the start of the selected
+ * text, and whose y coordinate is the character position
+ * representing the end of the selection. An "empty" selection
+ * is indicated by the x and y coordinates having the same value.
+ * <p>
+ * Indexing is zero based.  The range of a selection is from
+ * 0..N where N is the number of characters in the widget.
+ * </p>
+ *
+ * @return a point representing the selection start and end
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Point getSelection () {
+    checkWidget ();
+    int [] start = new int [1], end = new int [1];
+    OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+    if (!OS.IsUnicode && OS.IsDBLocale) {
+        start [0] = mbcsToWcsPos (start [0]);
+        end [0] = mbcsToWcsPos (end [0]);
+    }
+    return new Point (start [0], end [0]);
+}
+
+/**
+ * Returns the number of selected characters.
+ *
+ * @return the number of selected characters.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionCount () {
+    checkWidget ();
+    Point selection = getSelection ();
+    return selection.y - selection.x;
+}
+
+/**
+ * Gets the selected text, or an empty string if there is no current selection.
+ *
+ * @return the selected text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getSelectionText () {
+    checkWidget ();
+    int length = OS.GetWindowTextLength (handle);
+    if (length is 0) return "";
+    int [] start = new int [1], end = new int [1];
+    OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+    if (start [0] is end [0]) return "";
+    TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+    OS.GetWindowText (handle, buffer, length + 1);
+    return buffer.toString (start [0], end [0] - start [0]);
+}
+
+/**
+ * Returns the number of tabs.
+ * <p>
+ * Tab stop spacing is specified in terms of the
+ * space (' ') character.  The width of a single
+ * tab stop is the pixel width of the spaces.
+ * </p>
+ *
+ * @return the number of tab characters
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTabs () {
+    checkWidget ();
+    return tabs;
+}
+
+int getTabWidth (int tabs) {
+    int oldFont = 0;
+    RECT rect = new RECT ();
+    int hDC = OS.GetDC (handle);
+    int newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
+    TCHAR SPACE = new TCHAR (getCodePage (), " ", false);
+    OS.DrawText (hDC, SPACE, SPACE.length (), rect, flags);
+    if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+    OS.ReleaseDC (handle, hDC);
+    return (rect.right - rect.left) * tabs;
+}
+
+/**
+ * Returns the widget text.
+ * <p>
+ * The text for a text widget is the characters in the widget, or
+ * an empty string if this has never been set.
+ * </p>
+ *
+ * @return the widget text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText () {
+    checkWidget ();
+    int length = OS.GetWindowTextLength (handle);
+    if (length is 0) return "";
+    TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+    OS.GetWindowText (handle, buffer, length + 1);
+    return buffer.toString (0, length);
+}
+
+/**
+ * Returns a range of text.  Returns an empty string if the
+ * start of the range is greater than the end.
+ * <p>
+ * Indexing is zero based.  The range of
+ * a selection is from 0..N-1 where N is
+ * the number of characters in the widget.
+ * </p>
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ * @return the range of text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText (int start, int end) {
+    checkWidget ();
+    if (!(start <= end && 0 <= end)) return "";
+    int length = OS.GetWindowTextLength (handle);
+    if (!OS.IsUnicode && OS.IsDBLocale) length = mbcsToWcsPos (length);
+    start = Math.max (0, start);
+    end = Math.min (end, length - 1);
+    /*
+    * NOTE: The current implementation uses substring ()
+    * which can reference a potentially large character
+    * array.
+    */
+    return getText ().substring (start, end + 1);
+}
+
+/**
+ * Returns the maximum number of characters that the receiver is capable of holding.
+ * <p>
+ * If this has not been changed by <code>setTextLimit()</code>,
+ * it will be the constant <code>Text.LIMIT</code>.
+ * </p>
+ *
+ * @return the text limit
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #LIMIT
+ */
+public int getTextLimit () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF;
+}
+
+/**
+ * Returns the zero-relative index of the line which is currently
+ * at the top of the receiver.
+ * <p>
+ * This index can change when lines are scrolled or new lines are added or removed.
+ * </p>
+ *
+ * @return the index of the top line
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTopIndex () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) return 0;
+    return OS.SendMessage (handle, OS.EM_GETFIRSTVISIBLELINE, 0, 0);
+}
+
+/**
+ * Returns the top pixel.
+ * <p>
+ * The top pixel is the pixel position of the line
+ * that is currently at the top of the widget.  On
+ * some platforms, a text widget can be scrolled by
+ * pixels instead of lines so that a partial line
+ * is displayed at the top of the widget.
+ * </p><p>
+ * The top pixel changes when the widget is scrolled.
+ * The top pixel does not include the widget trimming.
+ * </p>
+ *
+ * @return the pixel position of the top line
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTopPixel () {
+    checkWidget ();
+    /*
+    * Note, EM_GETSCROLLPOS is implemented in Rich Edit 3.0
+    * and greater.  The plain text widget and previous versions
+    * of Rich Edit return zero.
+    */
+    int [] buffer = new int [2];
+    int code = OS.SendMessage (handle, OS.EM_GETSCROLLPOS, 0, buffer);
+    if (code is 1) return buffer [1];
+    return getTopIndex () * getLineHeight ();
+}
+
+/**
+ * Inserts a string.
+ * <p>
+ * The old selection is replaced with the new text.
+ * </p>
+ *
+ * @param string the string
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is <code>null</code></li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void insert (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    string = Display.withCrLf (string);
+    if (hooks (DWT.Verify) || filters (DWT.Verify)) {
+        int [] start = new int [1], end = new int [1];
+        OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+        string = verifyText (string, start [0], end [0], null);
+        if (string is null) return;
+    }
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    /*
+    * Feature in Windows.  When an edit control with ES_MULTILINE
+    * style that does not have the WS_VSCROLL style is full (i.e.
+    * there is no space at the end to draw any more characters),
+    * EM_REPLACESEL sends a WM_CHAR with a backspace character
+    * to remove any further text that is added.  This is an
+    * implementation detail of the edit control that is unexpected
+    * and can cause endless recursion when EM_REPLACESEL is sent
+    * from a WM_CHAR handler.  The fix is to ignore calling the
+    * handler from WM_CHAR.
+    */
+    ignoreCharacter = true;
+    OS.SendMessage (handle, OS.EM_REPLACESEL, 0, buffer);
+    ignoreCharacter = false;
+}
+
+int mbcsToWcsPos (int mbcsPos) {
+    if (mbcsPos <= 0) return 0;
+    if (OS.IsUnicode) return mbcsPos;
+    int cp = getCodePage ();
+    int wcsTotal = 0, mbcsTotal = 0;
+    byte [] buffer = new byte [128];
+    String delimiter = getLineDelimiter();
+    int delimiterSize = delimiter.length ();
+    int count = OS.SendMessageA (handle, OS.EM_GETLINECOUNT, 0, 0);
+    for (int line=0; line<count; line++) {
+        int wcsSize = 0;
+        int linePos = OS.SendMessageA (handle, OS.EM_LINEINDEX, line, 0);
+        int mbcsSize = OS.SendMessageA (handle, OS.EM_LINELENGTH, linePos, 0);
+        if (mbcsSize !is 0) {
+            if (mbcsSize + delimiterSize > buffer.length) {
+                buffer = new byte [mbcsSize + delimiterSize];
+            }
+            //ENDIAN
+            buffer [0] = (byte) (mbcsSize & 0xFF);
+            buffer [1] = (byte) (mbcsSize >> 8);
+            mbcsSize = OS.SendMessageA (handle, OS.EM_GETLINE, line, buffer);
+            wcsSize = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, mbcsSize, null, 0);
+        }
+        if (line - 1 !is count) {
+            for (int i=0; i<delimiterSize; i++) {
+                buffer [mbcsSize++] = (byte) delimiter.charAt (i);
+            }
+            wcsSize += delimiterSize;
+        }
+        if ((mbcsTotal + mbcsSize) >= mbcsPos) {
+            int bufferSize = mbcsPos - mbcsTotal;
+            wcsSize = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, bufferSize, null, 0);
+            return wcsTotal + wcsSize;
+        }
+        wcsTotal += wcsSize;
+        mbcsTotal += mbcsSize;
+    }
+    return wcsTotal;
+}
+
+/**
+ * Pastes text from clipboard.
+ * <p>
+ * The selected text is deleted from the widget
+ * and new text inserted from the clipboard.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void paste () {
+    checkWidget ();
+    if ((style & DWT.READ_ONLY) !is 0) return;
+    OS.SendMessage (handle, OS.WM_PASTE, 0, 0);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    message = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the receiver's text is modified.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ModifyListener
+ * @see #addModifyListener
+ */
+public void removeModifyListener (ModifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Modify, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is verified.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see VerifyListener
+ * @see #addVerifyListener
+ */
+public void removeVerifyListener (VerifyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Verify, listener);
+}
+
+/**
+ * Selects all the text in the receiver.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void selectAll () {
+    checkWidget ();
+    OS.SendMessage (handle, OS.EM_SETSEL, 0, -1);
+}
+
+bool sendKeyEvent (int type, int msg, int wParam, int lParam, Event event) {
+    if (!super.sendKeyEvent (type, msg, wParam, lParam, event)) {
+        return false;
+    }
+    if ((style & DWT.READ_ONLY) !is 0) return true;
+    if (ignoreVerify) return true;
+    if (type !is DWT.KeyDown) return true;
+    if (msg !is OS.WM_CHAR && msg !is OS.WM_KEYDOWN && msg !is OS.WM_IME_CHAR) {
+        return true;
+    }
+    if (event.character is 0) return true;
+    if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return true;
+    char key = event.character;
+    int stateMask = event.stateMask;
+
+    /*
+    * Disable all magic keys that could modify the text
+    * and don't send events when Alt, Shift or Ctrl is
+    * pressed.
+    */
+    switch (msg) {
+        case OS.WM_CHAR:
+            if (key !is 0x08 && key !is 0x7F && key !is '\r' && key !is '\t' && key !is '\n') break;
+            // FALL THROUGH
+        case OS.WM_KEYDOWN:
+            if ((stateMask & (DWT.ALT | DWT.SHIFT | DWT.CONTROL)) !is 0) return false;
+            break;
+    }
+
+    /*
+    * Feature in Windows.  If the left button is down in
+    * the text widget, it refuses the character.  The fix
+    * is to detect this case and avoid sending a verify
+    * event.
+    */
+    if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
+        if (handle is OS.GetCapture()) return true;
+    }
+
+    /* Verify the character */
+    String oldText = "";
+    int [] start = new int [1], end = new int [1];
+    OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+    switch (key) {
+        case 0x08:  /* Bs */
+            if (start [0] is end [0]) {
+                if (start [0] is 0) return true;
+                int lineStart = OS.SendMessage (handle, OS.EM_LINEINDEX, -1, 0);
+                if (start [0] is lineStart) {
+                    start [0] = start [0] - DELIMITER.length ();
+                } else {
+                    start [0] = start [0] - 1;
+                    if (!OS.IsUnicode && OS.IsDBLocale) {
+                        int [] newStart = new int [1], newEnd = new int [1];
+                        OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]);
+                        OS.SendMessage (handle, OS.EM_GETSEL, newStart, newEnd);
+                        if (start [0] !is newStart [0]) start [0] = start [0] - 1;
+                    }
+                }
+                start [0] = Math.max (start [0], 0);
+            }
+            break;
+        case 0x7F:  /* Del */
+            if (start [0] is end [0]) {
+                int length = OS.GetWindowTextLength (handle);
+                if (start [0] is length) return true;
+                int line = OS.SendMessage (handle, OS.EM_LINEFROMCHAR, end [0], 0);
+                int lineStart = OS.SendMessage (handle, OS.EM_LINEINDEX, line + 1, 0);
+                if (end [0] is lineStart - DELIMITER.length ()) {
+                    end [0] = end [0] + DELIMITER.length ();
+                } else {
+                    end [0] = end [0] + 1;
+                    if (!OS.IsUnicode && OS.IsDBLocale) {
+                        int [] newStart = new int [1], newEnd = new int [1];
+                        OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]);
+                        OS.SendMessage (handle, OS.EM_GETSEL, newStart, newEnd);
+                        if (end [0] !is newEnd [0]) end [0] = end [0] + 1;
+                    }
+                }
+                end [0] = Math.min (end [0], length);
+            }
+            break;
+        case '\r':  /* Return */
+            if ((style & DWT.SINGLE) !is 0) return true;
+            oldText = DELIMITER;
+            break;
+        default:    /* Tab and other characters */
+            if (key !is '\t' && key < 0x20) return true;
+            oldText = new String (new char [] {key});
+            break;
+    }
+    String newText = verifyText (oldText, start [0], end [0], event);
+    if (newText is null) return false;
+    if (newText is oldText) return true;
+    newText = Display.withCrLf (newText);
+    TCHAR buffer = new TCHAR (getCodePage (), newText, true);
+    OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]);
+    /*
+    * Feature in Windows.  When an edit control with ES_MULTILINE
+    * style that does not have the WS_VSCROLL style is full (i.e.
+    * there is no space at the end to draw any more characters),
+    * EM_REPLACESEL sends a WM_CHAR with a backspace character
+    * to remove any further text that is added.  This is an
+    * implementation detail of the edit control that is unexpected
+    * and can cause endless recursion when EM_REPLACESEL is sent
+    * from a WM_CHAR handler.  The fix is to ignore calling the
+    * handler from WM_CHAR.
+    */
+    ignoreCharacter = true;
+    OS.SendMessage (handle, OS.EM_REPLACESEL, 0, buffer);
+    ignoreCharacter = false;
+    return false;
+}
+
+void setBounds (int x, int y, int width, int height, int flags) {
+    /*
+    * Feature in Windows.  When the caret is moved,
+    * the text widget scrolls to show the new location.
+    * This means that the text widget may be scrolled
+    * to the right in order to show the caret when the
+    * widget is not large enough to show both the caret
+    * location and all the text.  Unfortunately, when
+    * the text widget is resized such that all the text
+    * and the caret could be visible, Windows does not
+    * scroll the widget back.  The fix is to resize the
+    * text widget, set the selection to the start of the
+    * text and then restore the selection.  This will
+    * cause the text widget compute the correct scroll
+    * position.
+    */
+    if ((flags & OS.SWP_NOSIZE) is 0 && width !is 0) {
+        RECT rect = new RECT ();
+        OS.GetWindowRect (handle, rect);
+        int margins = OS.SendMessage (handle, OS.EM_GETMARGINS, 0, 0);
+        int marginWidth = (margins & 0xFFFF) + ((margins >> 16) & 0xFFFF);
+        if (rect.right - rect.left <= marginWidth) {
+            int [] start = new int [1], end = new int [1];
+            OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+            if (start [0] !is 0 || end [0] !is 0) {
+                SetWindowPos (handle, 0, x, y, width, height, flags);
+                OS.SendMessage (handle, OS.EM_SETSEL, 0, 0);
+                OS.SendMessage (handle, OS.EM_SETSEL, start [0], end [0]);
+                return;
+            }
+        }
+    }
+    super.setBounds (x, y, width, height, flags);
+}
+
+void setDefaultFont () {
+    super.setDefaultFont ();
+    setMargins ();
+}
+
+/**
+ * Sets the double click enabled flag.
+ * <p>
+ * The double click flag enables or disables the
+ * default action of the text widget when the user
+ * double clicks.
+ * </p><p>
+ * Note: This operation is a hint and is not supported on
+ * platforms that do not have this concept.
+ * </p>
+ *
+ * @param doubleClick the new double click flag
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setDoubleClickEnabled (bool doubleClick) {
+    checkWidget ();
+    this.doubleClick = doubleClick;
+}
+
+/**
+ * Sets the echo character.
+ * <p>
+ * The echo character is the character that is
+ * displayed when the user enters text or the
+ * text is changed by the programmer. Setting
+ * the echo character to '\0' clears the echo
+ * character and redraws the original text.
+ * If for any reason the echo character is invalid,
+ * or if the platform does not allow modification
+ * of the echo character, the default echo character
+ * for the platform is used.
+ * </p>
+ *
+ * @param echo the new echo character
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setEchoChar (char echo) {
+    checkWidget ();
+    if ((style & DWT.MULTI) !is 0) return;
+    if (echo !is 0) {
+        if ((echo = (char) Display.wcsToMbcs (echo, getCodePage ())) is 0) echo = '*';
+    }
+    OS.SendMessage (handle, OS.EM_SETPASSWORDCHAR, echo, 0);
+    /*
+    * Bug in Windows.  When the password character is changed,
+    * Windows does not redraw to show the new password character.
+    * The fix is to force a redraw when the character is set.
+    */
+    OS.InvalidateRect (handle, null, true);
+}
+
+/**
+ * Sets the editable state.
+ *
+ * @param editable the new editable state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setEditable (bool editable) {
+    checkWidget ();
+    style &= ~DWT.READ_ONLY;
+    if (!editable) style |= DWT.READ_ONLY;
+    OS.SendMessage (handle, OS.EM_SETREADONLY, editable ? 0 : 1, 0);
+}
+
+public void setFont (Font font) {
+    checkWidget ();
+    super.setFont (font);
+    setTabStops (tabs);
+    setMargins ();
+}
+
+void setMargins () {
+    /*
+    * Bug in Windows.  When EM_SETCUEBANNER is used to set the
+    * banner text, the control does not take into account the
+    * margins, causing the first character to be clipped.  The
+    * fix is to set the margins to zero.
+    */
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if ((style & DWT.SEARCH) !is 0) {
+            OS.SendMessage (handle, OS.EM_SETMARGINS, OS.EC_LEFTMARGIN | OS.EC_RIGHTMARGIN, 0);
+        }
+    }
+}
+
+/**
+ * Sets the widget message. When the widget is created
+ * with the style <code>DWT.SEARCH</code>, the message text
+ * is displayed as a hint for the user, indicating the
+ * purpose of the field.
+ * <p>
+ * Note: This operation is a <em>HINT</em> and is not
+ * supported on platforms that do not have this concept.
+ * </p>
+ *
+ * @param message the new message
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the message is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public void setMessage (String message) {
+    checkWidget ();
+    if (message is null) error (DWT.ERROR_NULL_ARGUMENT);
+    this.message = message;
+    if (OS.COMCTL32_MAJOR >= 6) {
+        if ((style & DWT.SEARCH) !is 0) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            if ((bits & OS.ES_MULTILINE) is 0) {
+                int length = message.length ();
+                char [] chars = new char [length + 1];
+                message.getChars(0, length, chars, 0);
+                OS.SendMessage (handle, OS.EM_SETCUEBANNER, 0, chars);
+            }
+        }
+    }
+}
+
+/**
+ * Sets the orientation of the receiver, which must be one
+ * of the constants <code>DWT.LEFT_TO_RIGHT</code> or <code>DWT.RIGHT_TO_LEFT</code>.
+ * <p>
+ * Note: This operation is a hint and is not supported on
+ * platforms that do not have this concept.
+ * </p>
+ *
+ * @param orientation new orientation style
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1.2
+ */
+public void setOrientation (int orientation) {
+    checkWidget();
+    if (OS.IsWinCE) return;
+    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return;
+    int flags = DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT;
+    if ((orientation & flags) is 0 || (orientation & flags) is flags) return;
+    style &= ~flags;
+    style |= orientation & flags;
+    int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) {
+        bits |= OS.WS_EX_RTLREADING | OS.WS_EX_LEFTSCROLLBAR;
+    } else {
+        bits &= ~(OS.WS_EX_RTLREADING | OS.WS_EX_LEFTSCROLLBAR);
+    }
+    OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits);
+    fixAlignment ();
+}
+
+/**
+ * Sets the selection.
+ * <p>
+ * Indexing is zero based.  The range of
+ * a selection is from 0..N where N is
+ * the number of characters in the widget.
+ * </p><p>
+ * Text selections are specified in terms of
+ * caret positions.  In a text widget that
+ * contains N characters, there are N+1 caret
+ * positions, ranging from 0..N.  This differs
+ * from other functions that address character
+ * position such as getText () that use the
+ * regular array indexing rules.
+ * </p>
+ *
+ * @param start new caret position
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (int start) {
+    checkWidget ();
+    if (!OS.IsUnicode && OS.IsDBLocale) start = wcsToMbcsPos (start);
+    OS.SendMessage (handle, OS.EM_SETSEL, start, start);
+    OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
+}
+
+/**
+ * Sets the selection to the range specified
+ * by the given start and end indices.
+ * <p>
+ * Indexing is zero based.  The range of
+ * a selection is from 0..N where N is
+ * the number of characters in the widget.
+ * </p><p>
+ * Text selections are specified in terms of
+ * caret positions.  In a text widget that
+ * contains N characters, there are N+1 caret
+ * positions, ranging from 0..N.  This differs
+ * from other functions that address character
+ * position such as getText () that use the
+ * usual array indexing rules.
+ * </p>
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (int start, int end) {
+    checkWidget ();
+    if (!OS.IsUnicode && OS.IsDBLocale) {
+        start = wcsToMbcsPos (start);
+        end = wcsToMbcsPos (end);
+    }
+    OS.SendMessage (handle, OS.EM_SETSEL, start, end);
+    OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
+}
+
+public void setRedraw (bool redraw) {
+    checkWidget ();
+    super.setRedraw (redraw);
+    /*
+    * Feature in Windows.  When WM_SETREDRAW is used to turn
+    * redraw off, the edit control is not scrolled to show the
+    * i-beam.  The fix is to detect that the i-beam has moved
+    * while redraw is turned off and force it to be visible
+    * when redraw is restored.
+    */
+    if (drawCount !is 0) return;
+    int [] start = new int [1], end = new int [1];
+    OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+    if (!redraw) {
+        oldStart = start [0];  oldEnd = end [0];
+    } else {
+        if (oldStart is start [0] && oldEnd is end [0]) return;
+        OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
+    }
+}
+
+/**
+ * Sets the selection to the range specified
+ * by the given point, where the x coordinate
+ * represents the start index and the y coordinate
+ * represents the end index.
+ * <p>
+ * Indexing is zero based.  The range of
+ * a selection is from 0..N where N is
+ * the number of characters in the widget.
+ * </p><p>
+ * Text selections are specified in terms of
+ * caret positions.  In a text widget that
+ * contains N characters, there are N+1 caret
+ * positions, ranging from 0..N.  This differs
+ * from other functions that address character
+ * position such as getText () that use the
+ * usual array indexing rules.
+ * </p>
+ *
+ * @param selection the point
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (Point selection) {
+    checkWidget ();
+    if (selection is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setSelection (selection.x, selection.y);
+}
+
+/**
+ * Sets the number of tabs.
+ * <p>
+ * Tab stop spacing is specified in terms of the
+ * space (' ') character.  The width of a single
+ * tab stop is the pixel width of the spaces.
+ * </p>
+ *
+ * @param tabs the number of tabs
+ *
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setTabs (int tabs) {
+    checkWidget ();
+    if (tabs < 0) return;
+    setTabStops (this.tabs = tabs);
+}
+
+void setTabStops (int tabs) {
+    /*
+    * Feature in Windows.  Windows expects the tab spacing in
+    * dialog units so we must convert from space widths.  Due
+    * to round off error, the tab spacing may not be the exact
+    * number of space widths, depending on the font.
+    */
+    int width = (getTabWidth (tabs) * 4) / (OS.GetDialogBaseUnits () & 0xFFFF);
+    OS.SendMessage (handle, OS.EM_SETTABSTOPS, 1, new int [] {width});
+}
+
+/**
+ * Sets the contents of the receiver to the given string. If the receiver has style
+ * SINGLE and the argument contains multiple lines of text, the result of this
+ * operation is undefined and may vary from platform to platform.
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    string = Display.withCrLf (string);
+    if (hooks (DWT.Verify) || filters (DWT.Verify)) {
+        int length = OS.GetWindowTextLength (handle);
+        string = verifyText (string, 0, length, null);
+        if (string is null) return;
+    }
+    int limit = OS.SendMessage (handle, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF;
+    if (string.length () > limit) string = string.substring (0, limit);
+    TCHAR buffer = new TCHAR (getCodePage (), string, true);
+    OS.SetWindowText (handle, buffer);
+    /*
+    * Bug in Windows.  When the widget is multi line
+    * text widget, it does not send a WM_COMMAND with
+    * control code EN_CHANGE from SetWindowText () to
+    * notify the application that the text has changed.
+    * The fix is to send the event.
+    */
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.ES_MULTILINE) !is 0) {
+        sendEvent (DWT.Modify);
+        // widget could be disposed at this point
+    }
+}
+
+/**
+ * Sets the maximum number of characters that the receiver
+ * is capable of holding to be the argument.
+ * <p>
+ * Instead of trying to set the text limit to zero, consider
+ * creating a read-only text widget.
+ * </p><p>
+ * To reset this value to the default, use <code>setTextLimit(Text.LIMIT)</code>.
+ * Specifying a limit value larger than <code>Text.LIMIT</code> sets the
+ * receiver's limit to <code>Text.LIMIT</code>.
+ * </p>
+ *
+ * @param limit new text limit
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #LIMIT
+ */
+public void setTextLimit (int limit) {
+    checkWidget ();
+    if (limit is 0) error (DWT.ERROR_CANNOT_BE_ZERO);
+    OS.SendMessage (handle, OS.EM_SETLIMITTEXT, limit, 0);
+}
+
+/**
+ * Sets the zero-relative index of the line which is currently
+ * at the top of the receiver. This index can change when lines
+ * are scrolled or new lines are added and removed.
+ *
+ * @param index the index of the top item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setTopIndex (int index) {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) return;
+    int count = OS.SendMessage (handle, OS.EM_GETLINECOUNT, 0, 0);
+    index = Math.min (Math.max (index, 0), count - 1);
+    int topIndex = OS.SendMessage (handle, OS.EM_GETFIRSTVISIBLELINE, 0, 0);
+    OS.SendMessage (handle, OS.EM_LINESCROLL, 0, index - topIndex);
+}
+
+/**
+ * Shows the selection.
+ * <p>
+ * If the selection is already showing
+ * in the receiver, this method simply returns.  Otherwise,
+ * lines are scrolled until the selection is visible.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void showSelection () {
+    checkWidget ();
+    OS.SendMessage (handle, OS.EM_SCROLLCARET, 0, 0);
+}
+
+String verifyText (String string, int start, int end, Event keyEvent) {
+    if (ignoreVerify) return string;
+    Event event = new Event ();
+    event.text = string;
+    event.start = start;
+    event.end = end;
+    if (keyEvent !is null) {
+        event.character = keyEvent.character;
+        event.keyCode = keyEvent.keyCode;
+        event.stateMask = keyEvent.stateMask;
+    }
+    if (!OS.IsUnicode && OS.IsDBLocale) {
+        event.start = mbcsToWcsPos (start);
+        event.end = mbcsToWcsPos (end);
+    }
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in the verify
+    * event.  If this happens, answer null to cancel
+    * the operation.
+    */
+    sendEvent (DWT.Verify, event);
+    if (!event.doit || isDisposed ()) return null;
+    return event.text;
+}
+
+int wcsToMbcsPos (int wcsPos) {
+    if (wcsPos <= 0) return 0;
+    if (OS.IsUnicode) return wcsPos;
+    int cp = getCodePage ();
+    int wcsTotal = 0, mbcsTotal = 0;
+    byte [] buffer = new byte [128];
+    String delimiter = getLineDelimiter ();
+    int delimiterSize = delimiter.length ();
+    int count = OS.SendMessageA (handle, OS.EM_GETLINECOUNT, 0, 0);
+    for (int line=0; line<count; line++) {
+        int wcsSize = 0;
+        int linePos = OS.SendMessageA (handle, OS.EM_LINEINDEX, line, 0);
+        int mbcsSize = OS.SendMessageA (handle, OS.EM_LINELENGTH, linePos, 0);
+        if (mbcsSize !is 0) {
+            if (mbcsSize + delimiterSize > buffer.length) {
+                buffer = new byte [mbcsSize + delimiterSize];
+            }
+            //ENDIAN
+            buffer [0] = (byte) (mbcsSize & 0xFF);
+            buffer [1] = (byte) (mbcsSize >> 8);
+            mbcsSize = OS.SendMessageA (handle, OS.EM_GETLINE, line, buffer);
+            wcsSize = OS.MultiByteToWideChar (cp, OS.MB_PRECOMPOSED, buffer, mbcsSize, null, 0);
+        }
+        if (line - 1 !is count) {
+            for (int i=0; i<delimiterSize; i++) {
+                buffer [mbcsSize++] = (byte) delimiter.charAt (i);
+            }
+            wcsSize += delimiterSize;
+        }
+        if ((wcsTotal + wcsSize) >= wcsPos) {
+            wcsSize = 0;
+            int index = 0;
+            while (index < mbcsSize) {
+                if ((wcsTotal + wcsSize) is wcsPos) {
+                    return mbcsTotal + index;
+                }
+                if (OS.IsDBCSLeadByte (buffer [index++])) index++;
+                wcsSize++;
+            }
+            return mbcsTotal + mbcsSize;
+        }
+        wcsTotal += wcsSize;
+        mbcsTotal += mbcsSize;
+    }
+    return mbcsTotal;
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.ES_AUTOHSCROLL;
+    if ((style & DWT.PASSWORD) !is 0) bits |= OS.ES_PASSWORD;
+    if ((style & DWT.CENTER) !is 0) bits |= OS.ES_CENTER;
+    if ((style & DWT.RIGHT) !is 0) bits |= OS.ES_RIGHT;
+    if ((style & DWT.READ_ONLY) !is 0) bits |= OS.ES_READONLY;
+    if ((style & DWT.SINGLE) !is 0) {
+        /*
+        * Feature in Windows.  When a text control is read-only,
+        * uses COLOR_3DFACE for the background .  If the text
+        * controls single-line and is within a tab folder or
+        * some other themed control, using WM_ERASEBKGND and
+        * WM_CTRCOLOR to draw the theme background results in
+        * pixel corruption.  The fix is to use an ES_MULTILINE
+        * text control instead.
+        */
+        if ((style & DWT.READ_ONLY) !is 0) {
+            if ((style & (DWT.BORDER | DWT.H_SCROLL | DWT.V_SCROLL)) is 0) {
+                if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                    bits |= OS.ES_MULTILINE;
+                }
+            }
+        }
+        return bits;
+    }
+    bits |= OS.ES_MULTILINE | OS.ES_NOHIDESEL | OS.ES_AUTOVSCROLL;
+    if ((style & DWT.WRAP) !is 0) bits &= ~(OS.WS_HSCROLL | OS.ES_AUTOHSCROLL);
+    return bits;
+}
+
+TCHAR windowClass () {
+    return EditClass;
+}
+
+int windowProc () {
+    return EditProc;
+}
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (msg is OS.EM_UNDO) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if ((bits & OS.ES_MULTILINE) is 0) {
+            LRESULT result = wmClipboard (OS.EM_UNDO, wParam, lParam);
+            if (result !is null) return result.value;
+            return callWindowProc (hwnd, OS.EM_UNDO, wParam, lParam);
+        }
+    }
+    if (msg is Display.SWT_RESTORECARET) {
+        callWindowProc (hwnd, OS.WM_KILLFOCUS, 0, 0);
+        callWindowProc (hwnd, OS.WM_SETFOCUS, 0, 0);
+        return 1;
+    }
+    return super.windowProc (hwnd, msg, wParam, lParam);
+}
+
+LRESULT WM_CHAR (int wParam, int lParam) {
+    if (ignoreCharacter) return null;
+    LRESULT result = super.WM_CHAR (wParam, lParam);
+    if (result !is null) return result;
+
+    /*
+    * Bug in Windows.  When the user types CTRL and BS
+    * in an edit control, a DEL character is generated.
+    * Rather than deleting the text, the DEL character
+    * is inserted into the control.  The fix is to detect
+    * this case and not call the window proc.
+    */
+    switch (wParam) {
+        case DWT.DEL:
+            if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
+                return LRESULT.ZERO;
+            }
+    }
+
+    /*
+    * Feature in Windows.  For some reason, when the
+    * widget is a single line text widget, when the
+    * user presses tab, return or escape, Windows beeps.
+    * The fix is to look for these keys and not call
+    * the window proc.
+    */
+    if ((style & DWT.SINGLE) !is 0) {
+        switch (wParam) {
+            case DWT.CR:
+                postEvent (DWT.DefaultSelection);
+                // FALL THROUGH
+            case DWT.TAB:
+            case DWT.ESC: return LRESULT.ZERO;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_CLEAR (int wParam, int lParam) {
+    LRESULT result = super.WM_CLEAR (wParam, lParam);
+    if (result !is null) return result;
+    return wmClipboard (OS.WM_CLEAR, wParam, lParam);
+}
+
+LRESULT WM_CUT (int wParam, int lParam) {
+    LRESULT result = super.WM_CUT (wParam, lParam);
+    if (result !is null) return result;
+    return wmClipboard (OS.WM_CUT, wParam, lParam);
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    if ((style & DWT.READ_ONLY) !is 0) {
+        if ((style & (DWT.BORDER | DWT.H_SCROLL | DWT.V_SCROLL)) is 0) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            if ((bits & OS.ES_MULTILINE) !is 0) {
+                Control control = findBackgroundControl ();
+                if (control is null && background is -1) {
+                    if ((state & THEME_BACKGROUND) !is 0) {
+                        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                            control = findThemeControl ();
+                            if (control !is null) {
+                                RECT rect = new RECT ();
+                                OS.GetClientRect (handle, rect);
+                                fillThemeBackground (wParam, control, rect);
+                                return LRESULT.ONE;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
+    if (result !is null) return result;
+
+    /*
+    * Bug in WinCE PPC.  For some reason, sending WM_GETDLGCODE
+    * to a multi-line edit control causes it to ignore return and
+    * tab keys.  The fix is to return the value which is normally
+    * returned by the text window proc on other versions of Windows.
+    */
+    if (OS.IsPPC) {
+        if ((style & DWT.MULTI) !is 0 && (style & DWT.READ_ONLY) is 0 && lParam is 0) {
+            return new LRESULT (OS.DLGC_HASSETSEL | OS.DLGC_WANTALLKEYS | OS.DLGC_WANTCHARS);
+        }
+    }
+
+    /*
+    * Feature in Windows.  Despite the fact that the
+    * edit control is read only, it still returns a
+    * dialog code indicating that it wants all keys.
+    * The fix is to detect this case and clear the bits.
+    *
+    * NOTE: A read only edit control processes arrow keys
+    * so DLGC_WANTARROWS should not be cleared.
+    */
+    if ((style & DWT.READ_ONLY) !is 0) {
+        int code = callWindowProc (handle, OS.WM_GETDLGCODE, wParam, lParam);
+        code &= ~(OS.DLGC_WANTALLKEYS | OS.DLGC_WANTTAB);
+        return new LRESULT (code);
+    }
+    return null;
+}
+
+LRESULT WM_IME_CHAR (int wParam, int lParam) {
+
+    /* Process a DBCS character */
+    Display display = this.display;
+    display.lastKey = 0;
+    display.lastAscii = wParam;
+    display.lastVirtual = display.lastNull = display.lastDead = false;
+    if (!sendKeyEvent (DWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) {
+        return LRESULT.ZERO;
+    }
+
+    /*
+    * Feature in Windows.  The Windows text widget uses
+    * two 2 WM_CHAR's to process a DBCS key instead of
+    * using WM_IME_CHAR.  The fix is to allow the text
+    * widget to get the WM_CHAR's but ignore sending
+    * them to the application.
+    */
+    ignoreCharacter = true;
+    int result = callWindowProc (handle, OS.WM_IME_CHAR, wParam, lParam);
+    MSG msg = new MSG ();
+    int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE;
+    while (OS.PeekMessage (msg, handle, OS.WM_CHAR, OS.WM_CHAR, flags)) {
+        OS.TranslateMessage (msg);
+        OS.DispatchMessage (msg);
+    }
+    ignoreCharacter = false;
+
+    sendKeyEvent (DWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam);
+    // widget could be disposed at this point
+    display.lastKey = display.lastAscii = 0;
+    return new LRESULT (result);
+}
+
+LRESULT WM_LBUTTONDBLCLK (int wParam, int lParam) {
+    /*
+    * Prevent Windows from processing WM_LBUTTONDBLCLK
+    * when double clicking behavior is disabled by not
+    * calling the window proc.
+    */
+    LRESULT result = null;
+    sendMouseEvent (DWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam);
+    if (!sendMouseEvent (DWT.MouseDoubleClick, 1, handle, OS.WM_LBUTTONDBLCLK, wParam, lParam)) {
+        result = LRESULT.ZERO;
+    }
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+    }
+    if (!doubleClick) return LRESULT.ZERO;
+
+    /*
+    * Bug in Windows.  When the last line of text in the
+    * widget is double clicked and the line is empty, Windows
+    * hides the i-beam then moves it to the first line in
+    * the widget but does not scroll to show the user.
+    * If the user types without clicking the mouse, invalid
+    * characters are displayed at the end of each line of
+    * text in the widget.  The fix is to detect this case
+    * and avoid calling the window proc.
+    */
+    int [] start = new int [1], end = new int [1];
+    OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+    if (start [0] is end [0]) {
+        int length = OS.GetWindowTextLength (handle);
+        if (length is start [0]) {
+            int code = OS.SendMessage (handle, OS.EM_LINELENGTH, length, 0);
+            if (code is 0) return LRESULT.ZERO;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    if (OS.IsPPC) {
+        LRESULT result = null;
+        Display display = this.display;
+        display.captureChanged = false;
+        bool dispatch = sendMouseEvent (DWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam);
+        /*
+        * Note: On WinCE PPC, only attempt to recognize the gesture for
+        * a context menu when the control contains a valid menu or there
+        * are listeners for the MenuDetect event.
+        *
+        * Note: On WinCE PPC, the gesture that brings up a popup menu
+        * on the text widget must keep the current text selection.  As a
+        * result, the window proc is only called if the menu is not shown.
+        */
+        bool hasMenu = menu !is null && !menu.isDisposed ();
+        if (hasMenu || hooks (DWT.MenuDetect)) {
+            int x = (short) (lParam & 0xFFFF);
+            int y = (short) (lParam >> 16);
+            SHRGINFO shrg = new SHRGINFO ();
+            shrg.cbSize = SHRGINFO.sizeof;
+            shrg.hwndClient = handle;
+            shrg.ptDown_x = x;
+            shrg.ptDown_y = y;
+            shrg.dwFlags = OS.SHRG_RETURNCMD;
+            int type = OS.SHRecognizeGesture (shrg);
+            if (type is OS.GN_CONTEXTMENU) {
+                showMenu (x, y);
+                return LRESULT.ONE;
+            }
+        }
+        if (dispatch) {
+            result = new LRESULT (callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam));
+        } else {
+            result = LRESULT.ZERO;
+        }
+        if (!display.captureChanged && !isDisposed ()) {
+            if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+        }
+        return result;
+    }
+     return super.WM_LBUTTONDOWN (wParam, lParam);
+}
+
+LRESULT WM_PASTE (int wParam, int lParam) {
+    LRESULT result = super.WM_PASTE (wParam, lParam);
+    if (result !is null) return result;
+    return wmClipboard (OS.WM_PASTE, wParam, lParam);
+}
+
+LRESULT WM_UNDO (int wParam, int lParam) {
+    LRESULT result = super.WM_UNDO (wParam, lParam);
+    if (result !is null) return result;
+    return wmClipboard (OS.WM_UNDO, wParam, lParam);
+}
+
+LRESULT wmClipboard (int msg, int wParam, int lParam) {
+    if ((style & DWT.READ_ONLY) !is 0) return null;
+    if (!hooks (DWT.Verify) && !filters (DWT.Verify)) return null;
+    bool call = false;
+    int [] start = new int [1], end = new int [1];
+    String newText = null;
+    switch (msg) {
+        case OS.WM_CLEAR:
+        case OS.WM_CUT:
+            OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+            if (start [0] !is end [0]) {
+                newText = "";
+                call = true;
+            }
+            break;
+        case OS.WM_PASTE:
+            OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+            newText = getClipboardText ();
+            break;
+        case OS.EM_UNDO:
+        case OS.WM_UNDO:
+            if (OS.SendMessage (handle, OS.EM_CANUNDO, 0, 0) !is 0) {
+                ignoreModify = ignoreCharacter = true;
+                OS.SendMessage (handle, OS.EM_GETSEL, start, end);
+                callWindowProc (handle, msg, wParam, lParam);
+                int length = OS.GetWindowTextLength (handle);
+                int [] newStart = new int [1], newEnd = new int [1];
+                OS.SendMessage (handle, OS.EM_GETSEL, newStart, newEnd);
+                if (length !is 0 && newStart [0] !is newEnd [0]) {
+                    TCHAR buffer = new TCHAR (getCodePage (), length + 1);
+                    OS.GetWindowText (handle, buffer, length + 1);
+                    newText = buffer.toString (newStart [0], newEnd [0] - newStart [0]);
+                } else {
+                    newText = "";
+                }
+                callWindowProc (handle, msg, wParam, lParam);
+                ignoreModify = ignoreCharacter = false;
+            }
+            break;
+    }
+    if (newText !is null) {
+        String oldText = newText;
+        newText = verifyText (newText, start [0], end [0], null);
+        if (newText is null) return LRESULT.ZERO;
+        if (!newText.equals (oldText)) {
+            if (call) {
+                callWindowProc (handle, msg, wParam, lParam);
+            }
+            newText = Display.withCrLf (newText);
+            TCHAR buffer = new TCHAR (getCodePage (), newText, true);
+            /*
+            * Feature in Windows.  When an edit control with ES_MULTILINE
+            * style that does not have the WS_VSCROLL style is full (i.e.
+            * there is no space at the end to draw any more characters),
+            * EM_REPLACESEL sends a WM_CHAR with a backspace character
+            * to remove any further text that is added.  This is an
+            * implementation detail of the edit control that is unexpected
+            * and can cause endless recursion when EM_REPLACESEL is sent
+            * from a WM_CHAR handler.  The fix is to ignore calling the
+            * handler from WM_CHAR.
+            */
+            ignoreCharacter = true;
+            OS.SendMessage (handle, OS.EM_REPLACESEL, 0, buffer);
+            ignoreCharacter = false;
+            return LRESULT.ZERO;
+        }
+    }
+    if (msg is OS.WM_UNDO) {
+        ignoreVerify = ignoreCharacter = true;
+        callWindowProc (handle, OS.WM_UNDO, wParam, lParam);
+        ignoreVerify = ignoreCharacter = false;
+        return LRESULT.ONE;
+    }
+    return null;
+}
+
+LRESULT wmColorChild (int wParam, int lParam) {
+    if ((style & DWT.READ_ONLY) !is 0) {
+        if ((style & (DWT.BORDER | DWT.H_SCROLL | DWT.V_SCROLL)) is 0) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            if ((bits & OS.ES_MULTILINE) !is 0) {
+                Control control = findBackgroundControl ();
+                if (control is null && background is -1) {
+                    if ((state & THEME_BACKGROUND) !is 0) {
+                        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                            control = findThemeControl ();
+                            if (control !is null) {
+                                OS.SetTextColor (wParam, getForegroundPixel ());
+                                OS.SetBkColor (wParam, getBackgroundPixel ());
+                                OS.SetBkMode (wParam, OS.TRANSPARENT);
+                                return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return super.wmColorChild (wParam, lParam);
+}
+
+LRESULT wmCommandChild (int wParam, int lParam) {
+    int code = wParam >> 16;
+    switch (code) {
+        case OS.EN_CHANGE:
+            if (ignoreModify) break;
+            /*
+            * It is possible (but unlikely), that application
+            * code could have disposed the widget in the modify
+            * event.  If this happens, end the processing of the
+            * Windows message by returning zero as the result of
+            * the window proc.
+            */
+            sendEvent (DWT.Modify);
+            if (isDisposed ()) return LRESULT.ZERO;
+            break;
+        case OS.EN_ALIGN_LTR_EC:
+            style &= ~DWT.RIGHT_TO_LEFT;
+            style |= DWT.LEFT_TO_RIGHT;
+            fixAlignment ();
+            break;
+        case OS.EN_ALIGN_RTL_EC:
+            style &= ~DWT.LEFT_TO_RIGHT;
+            style |= DWT.RIGHT_TO_LEFT;
+            fixAlignment ();
+            break;
+    }
+    return super.wmCommandChild (wParam, lParam);
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/ToolBar.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1387 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.ToolBar;
+
+import dwt.widgets.Composite;
+class ToolBar : Composite {
+}
+/++
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Font;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.ImageList;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.NMCUSTOMDRAW;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.NMTBHOTITEM;
+import dwt.internal.win32.NMTOOLBAR;
+import dwt.internal.win32.NMTTDISPINFO;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TBBUTTON;
+import dwt.internal.win32.TBBUTTONINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TOOLINFO;
+import dwt.internal.win32.WINDOWPOS;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class support the layout of selectable
+ * tool bar items.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>ToolItem</code>.
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to add <code>Control</code> children to it,
+ * or set a layout on it.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>FLAT, WRAP, RIGHT, HORIZONTAL, VERTICAL, SHADOW_OUT</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+public class ToolBar extends Composite {
+    int lastFocusId;
+    ToolItem [] items;
+    bool ignoreResize, ignoreMouse;
+    ImageList imageList, disabledImageList, hotImageList;
+    static final int ToolBarProc;
+    static final TCHAR ToolBarClass = new TCHAR (0, OS.TOOLBARCLASSNAME, true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, ToolBarClass, lpWndClass);
+        ToolBarProc = lpWndClass.lpfnWndProc;
+    }
+
+    /*
+    * From the Windows SDK for TB_SETBUTTONSIZE:
+    *
+    *   "If an application does not explicitly
+    *   set the button size, the size defaults
+    *   to 24 by 22 pixels".
+    */
+    static final int DEFAULT_WIDTH = 24;
+    static final int DEFAULT_HEIGHT = 22;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#FLAT
+ * @see DWT#WRAP
+ * @see DWT#RIGHT
+ * @see DWT#HORIZONTAL
+ * @see DWT#SHADOW_OUT
+ * @see DWT#VERTICAL
+ * @see Widget#checkSubclass()
+ * @see Widget#getStyle()
+ */
+public ToolBar (Composite parent, int style) {
+    super (parent, checkStyle (style));
+    /*
+    * Ensure that either of HORIZONTAL or VERTICAL is set.
+    * NOTE: HORIZONTAL and VERTICAL have the same values
+    * as H_SCROLL and V_SCROLL so it is necessary to first
+    * clear these bits to avoid scroll bars and then reset
+    * the bits using the original style supplied by the
+    * programmer.
+    *
+    * NOTE: The CCS_VERT style cannot be applied when the
+    * widget is created because of this conflict.
+    */
+    if ((style & DWT.VERTICAL) !is 0) {
+        this.style |= DWT.VERTICAL;
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        /*
+        * Feature in Windows.  When a tool bar has the style
+        * TBSTYLE_LIST and has a drop down item, Window leaves
+        * too much padding around the button.  This affects
+        * every button in the tool bar and makes the preferred
+        * height too big.  The fix is to set the TBSTYLE_LIST
+        * when the tool bar contains both text and images.
+        *
+        * NOTE: Tool bars with CCS_VERT must have TBSTYLE_LIST
+        * set before any item is added or the tool bar does
+        * not lay out properly.  The work around does not run
+        * in this case.
+        */
+        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+            if ((style & DWT.RIGHT) !is 0) bits |= OS.TBSTYLE_LIST;
+        }
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.CCS_VERT);
+    } else {
+        this.style |= DWT.HORIZONTAL;
+    }
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    /*
+    * Bug in Windows.  For some reason, during the processing
+    * of WM_SYSCHAR, the tool bar window proc does not call the
+    * default window proc causing mnemonics for the menu bar
+    * to be ignored.  The fix is to always call the default
+    * window proc for WM_SYSCHAR.
+    */
+    if (msg is OS.WM_SYSCHAR) {
+        return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+    }
+    return OS.CallWindowProc (ToolBarProc, hwnd, msg, wParam, lParam);
+}
+
+static int checkStyle (int style) {
+    /*
+    * On Windows, only flat tool bars can be traversed.
+    */
+    if ((style & DWT.FLAT) is 0) style |= DWT.NO_FOCUS;
+
+    /*
+    * A vertical tool bar cannot wrap because TB_SETROWS
+    * fails when the toolbar has TBSTYLE_WRAPABLE.
+    */
+    if ((style & DWT.VERTICAL) !is 0) style &= ~DWT.WRAP;
+
+    /*
+    * Even though it is legal to create this widget
+    * with scroll bars, they serve no useful purpose
+    * because they do not automatically scroll the
+    * widget's client area.  The fix is to clear
+    * the DWT style.
+    */
+    return style & ~(DWT.H_SCROLL | DWT.V_SCROLL);
+}
+
+void checkBuffered () {
+    super.checkBuffered ();
+    if (OS.COMCTL32_MAJOR >= 6) style |= DWT.DOUBLE_BUFFERED;
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = 0, height = 0;
+    if ((style & DWT.VERTICAL) !is 0) {
+        RECT rect = new RECT ();
+        TBBUTTON lpButton = new TBBUTTON ();
+        int count = OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
+        for (int i=0; i<count; i++) {
+            OS.SendMessage (handle, OS.TB_GETITEMRECT, i, rect);
+            height = Math.max (height, rect.bottom);
+            OS.SendMessage (handle, OS.TB_GETBUTTON, i, lpButton);
+            if ((lpButton.fsStyle & OS.BTNS_SEP) !is 0) {
+                TBBUTTONINFO info = new TBBUTTONINFO ();
+                info.cbSize = TBBUTTONINFO.sizeof;
+                info.dwMask = OS.TBIF_SIZE;
+                OS.SendMessage (handle, OS.TB_GETBUTTONINFO, lpButton.idCommand, info);
+                width = Math.max (width, info.cx);
+            } else {
+                width = Math.max (width, rect.right);
+            }
+        }
+    } else {
+        RECT oldRect = new RECT ();
+        OS.GetWindowRect (handle, oldRect);
+        int oldWidth = oldRect.right - oldRect.left;
+        int oldHeight = oldRect.bottom - oldRect.top;
+        int border = getBorderWidth ();
+        int newWidth = wHint is DWT.DEFAULT ? 0x3FFF : wHint + border * 2;
+        int newHeight = hHint is DWT.DEFAULT ? 0x3FFF : hHint + border * 2;
+        bool redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+        ignoreResize = true;
+        if (redraw) OS.UpdateWindow (handle);
+        int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER;
+        SetWindowPos (handle, 0, 0, 0, newWidth, newHeight, flags);
+        int count = OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
+        if (count !is 0) {
+            RECT rect = new RECT ();
+            OS.SendMessage (handle, OS.TB_GETITEMRECT, count - 1, rect);
+            width = Math.max (width, rect.right);
+            height = Math.max (height, rect.bottom);
+        }
+        SetWindowPos (handle, 0, 0, 0, oldWidth, oldHeight, flags);
+        if (redraw) OS.ValidateRect (handle, null);
+        ignoreResize = false;
+    }
+
+    /*
+    * From the Windows SDK for TB_SETBUTTONSIZE:
+    *
+    *   "If an application does not explicitly
+    *   set the button size, the size defaults
+    *   to 24 by 22 pixels".
+    */
+    if (width is 0) width = DEFAULT_WIDTH;
+    if (height is 0) height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    Rectangle trim = computeTrim (0, 0, width, height);
+    width = trim.width;  height = trim.height;
+    return new Point (width, height);
+}
+
+public Rectangle computeTrim (int x, int y, int width, int height) {
+    checkWidget ();
+    Rectangle trim = super.computeTrim (x, y, width, height);
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.CCS_NODIVIDER) is 0) trim.height += 2;
+    return trim;
+}
+
+void createHandle () {
+    super.createHandle ();
+    state &= ~CANVAS;
+
+    /*
+    * Feature in Windows.  When TBSTYLE_FLAT is used to create
+    * a flat toolbar, for some reason TBSTYLE_TRANSPARENT is
+    * also set.  This causes the toolbar to flicker when it is
+    * moved or resized.  The fix is to clear TBSTYLE_TRANSPARENT.
+    *
+    * NOTE:  This work around is unnecessary on XP.  There is no
+    * flickering and clearing the TBSTYLE_TRANSPARENT interferes
+    * with the XP theme.
+    */
+    if ((style & DWT.FLAT) !is 0) {
+        if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            bits &= ~OS.TBSTYLE_TRANSPARENT;
+            OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+        }
+    }
+
+    /*
+    * Feature in Windows.  Despite the fact that the
+    * tool tip text contains \r\n, the tooltip will
+    * not honour the new line unless TTM_SETMAXTIPWIDTH
+    * is set.  The fix is to set TTM_SETMAXTIPWIDTH to
+    * a large value.
+    */
+    /*
+    * These lines are intentionally commented.  The tool
+    * bar currently sets this value to 300 so it is not
+    * necessary to set TTM_SETMAXTIPWIDTH.
+    */
+//  int hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0);
+//  OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
+
+    /*
+    * Feature in Windows.  When the control is created,
+    * it does not use the default system font.  A new HFONT
+    * is created and destroyed when the control is destroyed.
+    * This means that a program that queries the font from
+    * this control, uses the font in another control and then
+    * destroys this control will have the font unexpectedly
+    * destroyed in the other control.  The fix is to assign
+    * the font ourselves each time the control is created.
+    * The control will not destroy a font that it did not
+    * create.
+    */
+    int hFont = OS.GetStockObject (OS.SYSTEM_FONT);
+    OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
+
+    /* Set the button struct, bitmap and button sizes */
+    OS.SendMessage (handle, OS.TB_BUTTONSTRUCTSIZE, TBBUTTON.sizeof, 0);
+    OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0);
+    OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0);
+
+    /* Set the extended style bits */
+    int bits = OS.TBSTYLE_EX_DRAWDDARROWS | OS.TBSTYLE_EX_MIXEDBUTTONS | OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS;
+    if (OS.COMCTL32_MAJOR >= 6) bits |= OS.TBSTYLE_EX_DOUBLEBUFFER;
+    OS.SendMessage (handle, OS.TB_SETEXTENDEDSTYLE, 0, bits);
+}
+
+void createItem (ToolItem item, int index) {
+    int count = OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
+    if (!(0 <= index && index <= count)) error (DWT.ERROR_INVALID_RANGE);
+    int id = 0;
+    while (id < items.length && items [id] !is null) id++;
+    if (id is items.length) {
+        ToolItem [] newItems = new ToolItem [items.length + 4];
+        System.arraycopy (items, 0, newItems, 0, items.length);
+        items = newItems;
+    }
+    int bits = item.widgetStyle ();
+    TBBUTTON lpButton = new TBBUTTON ();
+    lpButton.idCommand = id;
+    lpButton.fsStyle = (byte) bits;
+    lpButton.fsState = (byte) OS.TBSTATE_ENABLED;
+
+    /*
+    * Bug in Windows.  Despite the fact that the image list
+    * index has never been set for the item, Windows always
+    * assumes that the image index for the item is valid.
+    * When an item is inserted, the image index is zero.
+    * Therefore, when the first image is inserted and is
+    * assigned image index zero, every item draws with this
+    * image.  The fix is to set the image index to none
+    * when the item is created.  This is not necessary in
+    * the case when the item has the BTNS_SEP style because
+    * separators cannot show images.
+    */
+    if ((bits & OS.BTNS_SEP) is 0) lpButton.iBitmap = OS.I_IMAGENONE;
+    if (OS.SendMessage (handle, OS.TB_INSERTBUTTON, index, lpButton) is 0) {
+        error (DWT.ERROR_ITEM_NOT_ADDED);
+    }
+    items [item.id = id] = item;
+    if ((style & DWT.VERTICAL) !is 0) setRowCount (count + 1);
+    layoutItems ();
+}
+
+void createWidget () {
+    super.createWidget ();
+    items = new ToolItem [4];
+    lastFocusId = -1;
+}
+
+int defaultBackground () {
+    if (OS.IsWinCE) return OS.GetSysColor (OS.COLOR_BTNFACE);
+    return super.defaultBackground ();
+}
+
+void destroyItem (ToolItem item) {
+    TBBUTTONINFO info = new TBBUTTONINFO ();
+    info.cbSize = TBBUTTONINFO.sizeof;
+    info.dwMask = OS.TBIF_IMAGE | OS.TBIF_STYLE;
+    int index = OS.SendMessage (handle, OS.TB_GETBUTTONINFO, item.id, info);
+    /*
+    * Feature in Windows.  For some reason, a tool item that has
+    * the style BTNS_SEP does not return I_IMAGENONE when queried
+    * for an image index, despite the fact that no attempt has been
+    * made to assign an image to the item.  As a result, operations
+    * on an image list that use the wrong index cause random results.
+    * The fix is to ensure that the tool item is not a separator
+    * before using the image index.  Since separators cannot have
+    * an image and one is never assigned, this is not a problem.
+    */
+    if ((info.fsStyle & OS.BTNS_SEP) is 0 && info.iImage !is OS.I_IMAGENONE) {
+        if (imageList !is null) imageList.put (info.iImage, null);
+        if (hotImageList !is null) hotImageList.put (info.iImage, null);
+        if (disabledImageList !is null) disabledImageList.put (info.iImage, null);
+    }
+    OS.SendMessage (handle, OS.TB_DELETEBUTTON, index, 0);
+    if (item.id is lastFocusId) lastFocusId = -1;
+    items [item.id] = null;
+    item.id = -1;
+    int count = OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
+    if (count is 0) {
+        if (imageList !is null) {
+            OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0);
+            display.releaseToolImageList (imageList);
+        }
+        if (hotImageList !is null) {
+            OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0);
+            display.releaseToolHotImageList (hotImageList);
+        }
+        if (disabledImageList !is null) {
+            OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0);
+            display.releaseToolDisabledImageList (disabledImageList);
+        }
+        imageList = hotImageList = disabledImageList = null;
+        items = new ToolItem [4];
+    }
+    if ((style & DWT.VERTICAL) !is 0) setRowCount (count - 1);
+    layoutItems ();
+}
+
+void enableWidget (bool enabled) {
+    super.enableWidget (enabled);
+    /*
+    * Bug in Windows.  When a tool item with the style
+    * BTNS_CHECK or BTNS_CHECKGROUP is selected and then
+    * disabled, the item does not draw using the disabled
+    * image.  The fix is to use the disabled image in all
+    * image lists for the item.
+    *
+    * Feature in Windows.  When a tool bar is disabled,
+    * the text draws disabled but the images do not.
+    * The fix is to use the disabled image in all image
+    * lists for all items.
+    */
+    for (int i=0; i<items.length; i++) {
+        ToolItem item = items [i];
+        if (item !is null) {
+            if ((item.style & DWT.SEPARATOR) is 0) {
+                item.updateImages (enabled && item.getEnabled ());
+            }
+        }
+    }
+}
+
+ImageList getDisabledImageList () {
+    return disabledImageList;
+}
+
+ImageList getHotImageList () {
+    return hotImageList;
+}
+
+ImageList getImageList () {
+    return imageList;
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public ToolItem getItem (int index) {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
+    if (!(0 <= index && index < count)) error (DWT.ERROR_INVALID_RANGE);
+    TBBUTTON lpButton = new TBBUTTON ();
+    int result = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton);
+    if (result is 0) error (DWT.ERROR_CANNOT_GET_ITEM);
+    return items [lpButton.idCommand];
+}
+
+/**
+ * Returns the item at the given point in the receiver
+ * or null if no such item exists. The point is in the
+ * coordinate system of the receiver.
+ *
+ * @param point the point used to locate the item
+ * @return the item at the given point
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public ToolItem getItem (Point point) {
+    checkWidget ();
+    if (point is null) error (DWT.ERROR_NULL_ARGUMENT);
+    ToolItem [] items = getItems ();
+    for (int i=0; i<items.length; i++) {
+        Rectangle rect = items [i].getBounds ();
+        if (rect.contains (point)) return items [i];
+    }
+    return null;
+}
+
+/**
+ * Returns the number of items contained in the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
+}
+
+/**
+ * Returns an array of <code>ToolItem</code>s which are the items
+ * in the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public ToolItem [] getItems () {
+    checkWidget ();
+    int count = OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
+    TBBUTTON lpButton = new TBBUTTON ();
+    ToolItem [] result = new ToolItem [count];
+    for (int i=0; i<count; i++) {
+        OS.SendMessage (handle, OS.TB_GETBUTTON, i, lpButton);
+        result [i] = items [lpButton.idCommand];
+    }
+    return result;
+}
+
+/**
+ * Returns the number of rows in the receiver. When
+ * the receiver has the <code>WRAP</code> style, the
+ * number of rows can be greater than one.  Otherwise,
+ * the number of rows is always one.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getRowCount () {
+    checkWidget ();
+    if ((style & DWT.VERTICAL) !is 0) {
+        return OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
+    }
+    return OS.SendMessage (handle, OS.TB_GETROWS, 0, 0);
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the tool item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the tool item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (ToolItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (item.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    return OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, item.id, 0);
+}
+
+void layoutItems () {
+    /*
+    * Feature in Windows.  When a tool bar has the style
+    * TBSTYLE_LIST and has a drop down item, Window leaves
+    * too much padding around the button.  This affects
+    * every button in the tool bar and makes the preferred
+    * height too big.  The fix is to set the TBSTYLE_LIST
+    * when the tool bar contains both text and images.
+    *
+    * NOTE: Tool bars with CCS_VERT must have TBSTYLE_LIST
+    * set before any item is added or the tool bar does
+    * not lay out properly.  The work around does not run
+    * in this case.
+    */
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        if ((style & DWT.RIGHT) !is 0 && (style & DWT.VERTICAL) is 0) {
+            bool hasText = false, hasImage = false;
+            for (int i=0; i<items.length; i++) {
+                ToolItem item = items [i];
+                if (item !is null) {
+                    if (!hasText) hasText = item.text.length () !is 0;
+                    if (!hasImage) hasImage = item.image !is null;
+                    if (hasText && hasImage) break;
+                }
+            }
+            int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
+            if (hasText && hasImage) {
+                newBits |= OS.TBSTYLE_LIST;
+            } else {
+                newBits &= ~OS.TBSTYLE_LIST;
+            }
+            if (newBits !is oldBits) {
+                setDropDownItems (false);
+                OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+                /*
+                * Feature in Windows.  For some reason, when the style
+                * is changed to TBSTYLE_LIST, Windows does not lay out
+                * the tool items.  The fix is to use WM_SETFONT to force
+                * the tool bar to redraw and lay out.
+                */
+                int hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+                OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
+                setDropDownItems (true);
+            }
+        }
+    }
+
+    if ((style & DWT.WRAP) !is 0) {
+        OS.SendMessage (handle, OS.TB_AUTOSIZE, 0, 0);
+    }
+    /*
+    *  When the tool bar is vertical, make the width of each button
+    *  be the width of the widest button in the tool bar.  Note that
+    *  when the tool bar contains a drop down item, it needs to take
+    *  into account extra padding.
+    */
+    if ((style & DWT.VERTICAL) !is 0) {
+        TBBUTTONINFO info = new TBBUTTONINFO ();
+        info.cbSize = TBBUTTONINFO.sizeof;
+        info.dwMask = OS.TBIF_SIZE;
+        int size = OS.SendMessage (handle, OS.TB_GETBUTTONSIZE, 0, 0);
+        info.cx = (short) (size & 0xFFFF);
+        int index = 0;
+        while (index < items.length) {
+            ToolItem item = items [index];
+            if (item !is null && (item.style & DWT.DROP_DOWN) !is 0) break;
+            index++;
+        }
+        if (index < items.length) {
+            int padding = OS.SendMessage (handle, OS.TB_GETPADDING, 0, 0);
+            info.cx += (padding & 0xFFFF) * 2;
+        }
+        for (int i=0; i<items.length; i++) {
+            ToolItem item = items [i];
+            if (item !is null && (item.style & DWT.SEPARATOR) is 0) {
+                OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info);
+            }
+        }
+    }
+    for (int i=0; i<items.length; i++) {
+        ToolItem item = items [i];
+        if (item !is null) item.resizeControl ();
+    }
+}
+
+bool mnemonicHit (char ch) {
+    int key = Display.wcsToMbcs (ch);
+    int [] id = new int [1];
+    if (OS.SendMessage (handle, OS.TB_MAPACCELERATOR, key, id) is 0) {
+        return false;
+    }
+    if ((style & DWT.FLAT) !is 0 && !setTabGroupFocus ()) return false;
+    int index = OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, id [0], 0);
+    if (index is -1) return false;
+    OS.SendMessage (handle, OS.TB_SETHOTITEM, index, 0);
+    items [id [0]].click (false);
+    return true;
+}
+
+bool mnemonicMatch (char ch) {
+    int key = Display.wcsToMbcs (ch);
+    int [] id = new int [1];
+    if (OS.SendMessage (handle, OS.TB_MAPACCELERATOR, key, id) is 0) {
+        return false;
+    }
+    /*
+    * Feature in Windows.  TB_MAPACCELERATOR matches either the mnemonic
+    * character or the first character in a tool item.  This behavior is
+    * undocumented and unwanted.  The fix is to ensure that the tool item
+    * contains a mnemonic when TB_MAPACCELERATOR returns true.
+    */
+    int index = OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, id [0], 0);
+    if (index is -1) return false;
+    return findMnemonic (items [id [0]].text) !is '\0';
+}
+
+void releaseChildren (bool destroy) {
+    if (items !is null) {
+        for (int i=0; i<items.length; i++) {
+            ToolItem item = items [i];
+            if (item !is null && !item.isDisposed ()) {
+                item.release (false);
+            }
+        }
+        items = null;
+    }
+    super.releaseChildren (destroy);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if (imageList !is null) {
+        OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0);
+        display.releaseToolImageList (imageList);
+    }
+    if (hotImageList !is null) {
+        OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0);
+        display.releaseToolHotImageList (hotImageList);
+    }
+    if (disabledImageList !is null) {
+        OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0);
+        display.releaseToolDisabledImageList (disabledImageList);
+    }
+    imageList = hotImageList = disabledImageList = null;
+}
+
+void removeControl (Control control) {
+    super.removeControl (control);
+    for (int i=0; i<items.length; i++) {
+        ToolItem item = items [i];
+        if (item !is null && item.control is control) {
+            item.setControl (null);
+        }
+    }
+}
+
+void setBackgroundImage (int hBitmap) {
+    super.setBackgroundImage (hBitmap);
+    setBackgroundTransparent (hBitmap !is 0);
+}
+
+void setBackgroundPixel (int pixel) {
+    super.setBackgroundPixel (pixel);
+    setBackgroundTransparent (pixel !is -1);
+}
+
+void setBackgroundTransparent (bool transparent) {
+    /*
+    * Feature in Windows.  When TBSTYLE_TRANSPARENT is set
+    * in a tool bar that is drawing a background, images in
+    * the image list that include transparency information
+    * do not draw correctly.  The fix is to clear and set
+    * TBSTYLE_TRANSPARENT depending on the background color.
+    *
+    * NOTE:  This work around is unnecessary on XP.  The
+    * TBSTYLE_TRANSPARENT style is never cleared on that
+    * platform.
+    */
+    if ((style & DWT.FLAT) !is 0) {
+        if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            if (!transparent && findBackgroundControl () is null) {
+                bits &= ~OS.TBSTYLE_TRANSPARENT;
+            } else {
+                bits |= OS.TBSTYLE_TRANSPARENT;
+            }
+            OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+        }
+    }
+}
+
+void setBounds (int x, int y, int width, int height, int flags) {
+    /*
+    * Feature in Windows.  For some reason, when a tool bar is
+    * repositioned more than once using DeferWindowPos () into
+    * the same HDWP, the toolbar redraws more than once, defeating
+    * the purpose of DeferWindowPos ().  The fix is to end the
+    * deferred positioning before the next tool bar is added,
+    * ensuring that only one tool bar position is deferred at
+    * any given time.
+    */
+    if (parent.lpwp !is null) {
+        if (drawCount is 0 && OS.IsWindowVisible (handle)) {
+            parent.setResizeChildren (false);
+            parent.setResizeChildren (true);
+        }
+    }
+    super.setBounds (x, y, width, height, flags);
+}
+
+void setDefaultFont () {
+    super.setDefaultFont ();
+    OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0);
+    OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0);
+}
+
+void setDropDownItems (bool set) {
+    /*
+    * Feature in Windows.  When the first button in a tool bar
+    * is a drop down item, Window leaves too much padding around
+    * the button.  This affects every button in the tool bar and
+    * makes the preferred height too big.  The fix is clear the
+    * BTNS_DROPDOWN before Windows lays out the tool bar and set
+    * the bit afterwards.
+    *
+    * NOTE:  This work around only runs when the tool bar contains
+    * both text and images.
+    */
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        bool hasText = false, hasImage = false;
+        for (int i=0; i<items.length; i++) {
+            ToolItem item = items [i];
+            if (item !is null) {
+                if (!hasText) hasText = item.text.length () !is 0;
+                if (!hasImage) hasImage = item.image !is null;
+                if (hasText && hasImage) break;
+            }
+        }
+        if (hasImage && !hasText) {
+            for (int i=0; i<items.length; i++) {
+                ToolItem item = items [i];
+                if (item !is null && (item.style & DWT.DROP_DOWN) !is 0) {
+                    TBBUTTONINFO info = new TBBUTTONINFO ();
+                    info.cbSize = TBBUTTONINFO.sizeof;
+                    info.dwMask = OS.TBIF_STYLE;
+                    OS.SendMessage (handle, OS.TB_GETBUTTONINFO, item.id, info);
+                    if (set) {
+                        info.fsStyle |= OS.BTNS_DROPDOWN;
+                    } else {
+                        info.fsStyle &= ~OS.BTNS_DROPDOWN;
+                    }
+                    OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info);
+                }
+            }
+        }
+    }
+}
+
+void setDisabledImageList (ImageList imageList) {
+    if (disabledImageList is imageList) return;
+    int hImageList = 0;
+    if ((disabledImageList = imageList) !is null) {
+        hImageList = disabledImageList.getHandle ();
+    }
+    setDropDownItems (false);
+    OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, hImageList);
+    setDropDownItems (true);
+}
+
+public void setFont (Font font) {
+    checkWidget ();
+    setDropDownItems (false);
+    super.setFont (font);
+    setDropDownItems (true);
+    /*
+    * Bug in Windows.  When WM_SETFONT is sent to a tool bar
+    * that contains only separators, causes the bitmap and button
+    * sizes to be set.  The fix is to reset these sizes after the font
+    * has been changed when the tool bar contains only separators.
+    */
+    int index = 0;
+    int mask = DWT.PUSH | DWT.CHECK | DWT.RADIO | DWT.DROP_DOWN;
+    while (index < items.length) {
+        ToolItem item = items [index];
+        if (item !is null && (item.style & mask) !is 0) break;
+        index++;
+    }
+    if (index is items.length) {
+        OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0);
+        OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0);
+    }
+    layoutItems ();
+}
+
+void setHotImageList (ImageList imageList) {
+    if (hotImageList is imageList) return;
+    int hImageList = 0;
+    if ((hotImageList = imageList) !is null) {
+        hImageList = hotImageList.getHandle ();
+    }
+    setDropDownItems (false);
+    OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, hImageList);
+    setDropDownItems (true);
+}
+
+void setImageList (ImageList imageList) {
+    if (this.imageList is imageList) return;
+    int hImageList = 0;
+    if ((this.imageList = imageList) !is null) {
+        hImageList = imageList.getHandle ();
+    }
+    setDropDownItems (false);
+    OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, hImageList);
+    setDropDownItems (true);
+}
+
+public bool setParent (Composite parent) {
+    checkWidget ();
+    if (!super.setParent (parent)) return false;
+    OS.SendMessage (handle, OS.TB_SETPARENT, parent.handle, 0);
+    return true;
+}
+
+public void setRedraw (bool redraw) {
+    checkWidget ();
+    setDropDownItems (false);
+    super.setRedraw (redraw);
+    setDropDownItems (true);
+}
+
+void setRowCount (int count) {
+    if ((style & DWT.VERTICAL) !is 0) {
+        /*
+        * Feature in Windows.  When the TB_SETROWS is used to set the
+        * number of rows in a tool bar, the tool bar is resized to show
+        * the items.  This is unexpected.  The fix is to save and restore
+        * the current size of the tool bar.
+        */
+        RECT rect = new RECT ();
+        OS.GetWindowRect (handle, rect);
+        OS.MapWindowPoints (0, parent.handle, rect, 2);
+        ignoreResize = true;
+        /*
+        * Feature in Windows.  When the last button in a tool bar has the
+        * style BTNS_SEP and TB_SETROWS is used to set the number of rows
+        * in the tool bar, depending on the number of buttons, the toolbar
+        * will wrap items with the style BTNS_CHECK, even when the fLarger
+        * flags is used to force the number of rows to be larger than the
+        * number of items.  The fix is to set the number of rows to be two
+        * larger than the actual number of rows in the tool bar.  When items
+        * are being added, as long as the number of rows is at least one
+        * item larger than the count, the tool bar is laid out properly.
+        * When items are being removed, setting the number of rows to be
+        * one more than the item count has no effect.  The number of rows
+        * is already one more causing TB_SETROWS to do nothing.  Therefore,
+        * choosing two instead of one as the row increment fixes both cases.
+        */
+        count += 2;
+        OS.SendMessage (handle, OS.TB_SETROWS, (1 << 16) | count, 0);
+        int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOZORDER;
+        SetWindowPos (handle, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, flags);
+        ignoreResize = false;
+    }
+}
+
+bool setTabItemFocus () {
+    int index = 0;
+    while (index < items.length) {
+        ToolItem item = items [index];
+        if (item !is null && (item.style & DWT.SEPARATOR) is 0) {
+            if (item.getEnabled ()) break;
+        }
+        index++;
+    }
+    if (index is items.length) return false;
+    return super.setTabItemFocus ();
+}
+
+String toolTipText (NMTTDISPINFO hdr) {
+    if ((hdr.uFlags & OS.TTF_IDISHWND) !is 0) {
+        return null;
+    }
+    /*
+    * Bug in Windows.  On Windows XP, when TB_SETHOTITEM is
+    * used to set the hot item, the tool bar control attempts
+    * to display the tool tip, even when the cursor is not in
+    * the hot item.  The fix is to detect this case and fail to
+    * provide the string, causing no tool tip to be displayed.
+    */
+    if (!hasCursor ()) return ""; //$NON-NLS-1$
+    int index = hdr.idFrom;
+    int hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0);
+    if (hwndToolTip is hdr.hwndFrom) {
+        if (toolTipText !is null) return ""; //$NON-NLS-1$
+        if (0 <= index && index < items.length) {
+            ToolItem item = items [index];
+            if (item !is null) return item.toolTipText;
+        }
+    }
+    return super.toolTipText (hdr);
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.CCS_NORESIZE | OS.TBSTYLE_TOOLTIPS | OS.TBSTYLE_CUSTOMERASE;
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) bits |= OS.TBSTYLE_TRANSPARENT;
+    if ((style & DWT.SHADOW_OUT) is 0) bits |= OS.CCS_NODIVIDER;
+    if ((style & DWT.WRAP) !is 0) bits |= OS.TBSTYLE_WRAPABLE;
+    if ((style & DWT.FLAT) !is 0) bits |= OS.TBSTYLE_FLAT;
+    /*
+    * Feature in Windows.  When a tool bar has the style
+    * TBSTYLE_LIST and has a drop down item, Window leaves
+    * too much padding around the button.  This affects
+    * every button in the tool bar and makes the preferred
+    * height too big.  The fix is to set the TBSTYLE_LIST
+    * when the tool bar contains both text and images.
+    *
+    * NOTE: Tool bars with CCS_VERT must have TBSTYLE_LIST
+    * set before any item is added or the tool bar does
+    * not lay out properly.  The work around does not run
+    * in this case.
+    */
+    if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+        if ((style & DWT.RIGHT) !is 0) bits |= OS.TBSTYLE_LIST;
+    }
+    return bits;
+}
+
+TCHAR windowClass () {
+    return ToolBarClass;
+}
+
+int windowProc () {
+    return ToolBarProc;
+}
+
+LRESULT WM_CAPTURECHANGED (int wParam, int lParam) {
+    LRESULT result = super.WM_CAPTURECHANGED (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug in Windows.  When the tool bar loses capture while an
+    * item is pressed, the item remains pressed.  The fix is
+    * unpress all items using TB_SETSTATE and TBSTATE_PRESSED.
+    */
+    for (int i=0; i<items.length; i++) {
+        ToolItem item = items [i];
+        if (item !is null) {
+            int fsState = OS.SendMessage (handle, OS.TB_GETSTATE, item.id, 0);
+            if ((fsState & OS.TBSTATE_PRESSED) !is 0) {
+                fsState &= ~OS.TBSTATE_PRESSED;
+                OS.SendMessage (handle, OS.TB_SETSTATE, item.id, fsState);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_CHAR (int wParam, int lParam) {
+    LRESULT result = super.WM_CHAR (wParam, lParam);
+    if (result !is null) return result;
+    switch (wParam) {
+        case ' ':
+            int index = OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0);
+            if (index !is -1) {
+                TBBUTTON lpButton = new TBBUTTON ();
+                int code = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton);
+                if (code !is 0) {
+                    items [lpButton.idCommand].click (false);
+                    return LRESULT.ZERO;
+                }
+            }
+    }
+    return result;
+}
+
+LRESULT WM_COMMAND (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When the toolbar window
+    * proc processes WM_COMMAND, it forwards this
+    * message to its parent.  This is done so that
+    * children of this control that send this message
+    * type to their parent will notify not only
+    * this control but also the parent of this control,
+    * which is typically the application window and
+    * the window that is looking for the message.
+    * If the control did not forward the message,
+    * applications would have to subclass the control
+    * window to see the message. Because the control
+    * window is subclassed by DWT, the message
+    * is delivered twice, once by DWT and once when
+    * the message is forwarded by the window proc.
+    * The fix is to avoid calling the window proc
+    * for this control.
+    */
+    LRESULT result = super.WM_COMMAND (wParam, lParam);
+    if (result !is null) return result;
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    /*
+    * Bug in Windows.  For some reason, NM_CUSTOMDRAW with
+    * CDDS_PREERASE and CDDS_POSTERASE is never sent for
+    * versions of Windows earlier than XP.  The fix is to
+    * draw the background in WM_ERASEBKGND;
+    */
+    if (findBackgroundControl () !is null) {
+        if (OS.COMCTL32_MAJOR < 6) {
+            drawBackground (wParam);
+            return LRESULT.ONE;
+        }
+    }
+    return result;
+}
+
+LRESULT WM_GETDLGCODE (int wParam, int lParam) {
+    LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
+    /*
+    * Return DLGC_BUTTON so that mnemonics will be
+    * processed without needing to press the ALT key
+    * when the widget has focus.
+    */
+    if (result !is null) return result;
+    return new LRESULT (OS.DLGC_BUTTON);
+}
+
+LRESULT WM_KEYDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_KEYDOWN (wParam, lParam);
+    if (result !is null) return result;
+    switch (wParam) {
+        case OS.VK_SPACE:
+            /*
+            * Ensure that the window proc does not process VK_SPACE
+            * so that it can be handled in WM_CHAR.  This allows the
+            * application the opportunity to cancel the operation.
+            */
+            return LRESULT.ZERO;
+    }
+    return result;
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    int index = OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0);
+    TBBUTTON lpButton = new TBBUTTON ();
+    int code = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton);
+    if (code !is 0) lastFocusId = lpButton.idCommand;
+    return super.WM_KILLFOCUS (wParam, lParam);
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    if (ignoreMouse) return null;
+    return super.WM_LBUTTONDOWN (wParam, lParam);
+}
+
+LRESULT WM_LBUTTONUP (int wParam, int lParam) {
+    if (ignoreMouse) return null;
+    return super.WM_LBUTTONUP (wParam, lParam);
+}
+
+LRESULT WM_MOUSELEAVE (int wParam, int lParam) {
+    LRESULT result = super.WM_MOUSELEAVE (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug in Windows.  On XP, when a tooltip is
+    * hidden due to a time out or mouse press,
+    * the tooltip remains active although no
+    * longer visible and won't show again until
+    * another tooltip becomes active.  If there
+    * is only one tooltip in the window,  it will
+    * never show again.  The fix is to remove the
+    * current tooltip and add it again every time
+    * the mouse leaves the control.
+    */
+    if (OS.COMCTL32_MAJOR >= 6) {
+        TOOLINFO lpti = new TOOLINFO ();
+        lpti.cbSize = TOOLINFO.sizeof;
+        int hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0);
+        if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) !is 0) {
+            if ((lpti.uFlags & OS.TTF_IDISHWND) is 0) {
+                OS.SendMessage (hwndToolTip, OS.TTM_DELTOOL, 0, lpti);
+                OS.SendMessage (hwndToolTip, OS.TTM_ADDTOOL, 0, lpti);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_NOTIFY (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When the toolbar window
+    * proc processes WM_NOTIFY, it forwards this
+    * message to its parent.  This is done so that
+    * children of this control that send this message
+    * type to their parent will notify not only
+    * this control but also the parent of this control,
+    * which is typically the application window and
+    * the window that is looking for the message.
+    * If the control did not forward the message,
+    * applications would have to subclass the control
+    * window to see the message. Because the control
+    * window is subclassed by DWT, the message
+    * is delivered twice, once by DWT and once when
+    * the message is forwarded by the window proc.
+    * The fix is to avoid calling the window proc
+    * for this control.
+    */
+    LRESULT result = super.WM_NOTIFY (wParam, lParam);
+    if (result !is null) return result;
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    LRESULT result = super.WM_SETFOCUS (wParam, lParam);
+    if (lastFocusId !is -1 && handle is OS.GetFocus ()) {
+        int index = OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, lastFocusId, 0);
+        OS.SendMessage (handle, OS.TB_SETHOTITEM, index, 0);
+    }
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    if (ignoreResize) {
+        int code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam);
+        if (code is 0) return LRESULT.ZERO;
+        return new LRESULT (code);
+    }
+    LRESULT result = super.WM_SIZE (wParam, lParam);
+    if (isDisposed ()) return result;
+    /*
+    * Bug in Windows.  The code in Windows that determines
+    * when tool items should wrap seems to use the window
+    * bounds rather than the client area.  Unfortunately,
+    * tool bars with the style TBSTYLE_EX_HIDECLIPPEDBUTTONS
+    * use the client area.  This means that buttons which
+    * overlap the border are hidden before they are wrapped.
+    * The fix is to compute TBSTYLE_EX_HIDECLIPPEDBUTTONS
+    * and set it each time the tool bar is resized.
+    */
+    if ((style & DWT.BORDER) !is 0 && (style & DWT.WRAP) !is 0) {
+        RECT windowRect = new RECT ();
+        OS.GetWindowRect (handle, windowRect);
+        int index = 0, border = getBorderWidth () * 2;
+        RECT rect = new RECT ();
+        int count = OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
+        while (index < count) {
+            OS.SendMessage (handle, OS.TB_GETITEMRECT, index, rect);
+            OS.MapWindowPoints (handle, 0, rect, 2);
+            if (rect.right > windowRect.right - border * 2) break;
+            index++;
+        }
+        int bits = OS.SendMessage (handle, OS.TB_GETEXTENDEDSTYLE, 0, 0);
+        if (index is count) {
+            bits |= OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS;
+        } else {
+            bits &= ~OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS;
+        }
+        OS.SendMessage (handle, OS.TB_SETEXTENDEDSTYLE, 0, bits);
+    }
+    layoutItems ();
+    return result;
+}
+
+LRESULT WM_WINDOWPOSCHANGING (int wParam, int lParam) {
+    LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam);
+    if (result !is null) return result;
+    if (ignoreResize) return result;
+    /*
+    * Bug in Windows.  When a flat tool bar is wrapped,
+    * Windows draws a horizontal separator between the
+    * rows.  The tool bar does not draw the first or
+    * the last two pixels of this separator.  When the
+    * toolbar is resized to be bigger, only the new
+    * area is drawn and the last two pixels, which are
+    * blank are drawn over by separator.  This leaves
+    * garbage on the screen.  The fix is to damage the
+    * pixels.
+    */
+    if (drawCount !is 0) return result;
+    if ((style & DWT.WRAP) is 0) return result;
+    if (!OS.IsWindowVisible (handle)) return result;
+    if (OS.SendMessage (handle, OS.TB_GETROWS, 0, 0) is 1) {
+        return result;
+    }
+    WINDOWPOS lpwp = new WINDOWPOS ();
+    OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
+    if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) !is 0) {
+        return result;
+    }
+    RECT oldRect = new RECT ();
+    OS.GetClientRect (handle, oldRect);
+    RECT newRect = new RECT ();
+    OS.SetRect (newRect, 0, 0, lpwp.cx, lpwp.cy);
+    OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, newRect);
+    int oldWidth = oldRect.right - oldRect.left;
+    int newWidth = newRect.right - newRect.left;
+    if (newWidth > oldWidth) {
+        RECT rect = new RECT ();
+        int newHeight = newRect.bottom - newRect.top;
+        OS.SetRect (rect, oldWidth - 2, 0, oldWidth, newHeight);
+        OS.InvalidateRect (handle, rect, false);
+    }
+    return result;
+}
+
+LRESULT wmCommandChild (int wParam, int lParam) {
+    ToolItem child = items [wParam & 0xFFFF];
+    if (child is null) return null;
+    return child.wmCommandChild (wParam, lParam);
+}
+
+LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
+    switch (hdr.code) {
+        case OS.TBN_DROPDOWN:
+            NMTOOLBAR lpnmtb = new NMTOOLBAR ();
+            OS.MoveMemory (lpnmtb, lParam, NMTOOLBAR.sizeof);
+            ToolItem child = items [lpnmtb.iItem];
+            if (child !is null) {
+                Event event = new Event ();
+                event.detail = DWT.ARROW;
+                int index = OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, lpnmtb.iItem, 0);
+                RECT rect = new RECT ();
+                OS.SendMessage (handle, OS.TB_GETITEMRECT, index, rect);
+                event.x = rect.left;
+                event.y = rect.bottom;
+                child.postEvent (DWT.Selection, event);
+            }
+            break;
+        case OS.NM_CUSTOMDRAW:
+            if (OS.COMCTL32_MAJOR < 6) break;
+            /*
+            * Bug in Windows.  For some reason, under the XP Silver
+            * theme, tool bars continue to draw using the gray color
+            * from the default Blue theme.  The fix is to draw the
+            * background.
+            */
+            NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW ();
+            OS.MoveMemory (nmcd, lParam, NMCUSTOMDRAW.sizeof);
+//          if (drawCount !is 0 || !OS.IsWindowVisible (handle)) {
+//              if (!OS.IsWinCE && OS.WindowFromDC (nmcd.hdc) is handle) break;
+//          }
+            switch (nmcd.dwDrawStage) {
+                case OS.CDDS_PREERASE: {
+                    /*
+                    * Bug in Windows.  When a tool bar does not have the style
+                    * TBSTYLE_FLAT, the rectangle to be erased in CDDS_PREERASE
+                    * is empty.  The fix is to draw the whole client area.
+                    */
+                    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+                    if ((bits & OS.TBSTYLE_FLAT) is 0) {
+                        drawBackground (nmcd.hdc);
+                    } else {
+                        RECT rect = new RECT ();
+                        OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+                        drawBackground (nmcd.hdc, rect);
+                    }
+                    return new LRESULT (OS.CDRF_SKIPDEFAULT);
+                }
+            }
+            break;
+        case OS.TBN_HOTITEMCHANGE:
+            if (!OS.IsWinCE) {
+                NMTBHOTITEM lpnmhi = new NMTBHOTITEM ();
+                OS.MoveMemory (lpnmhi, lParam, NMTBHOTITEM.sizeof);
+                switch (lpnmhi.dwFlags) {
+                    case OS.HICF_ARROWKEYS:
+                        RECT client = new RECT ();
+                        OS.GetClientRect (handle, client);
+                        int index = OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, lpnmhi.idNew, 0);
+                        RECT rect = new RECT ();
+                        OS.SendMessage (handle, OS.TB_GETITEMRECT, index, rect);
+                        if (rect.right > client.right || rect.bottom > client.bottom) {
+                            return LRESULT.ONE;
+                        }
+                        break;
+                }
+            }
+            break;
+    }
+    return super.wmNotifyChild (hdr, wParam, lParam);
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/ToolItem.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,972 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.ToolItem;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+class ToolItem : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Image;
+import dwt.graphics.Rectangle;
+import dwt.internal.ImageList;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TBBUTTONINFO;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class represent a selectable user interface object
+ * that represents a button in a tool bar.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>PUSH, CHECK, RADIO, SEPARATOR, DROP_DOWN</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * <p>
+ * Note: Only one of the styles CHECK, PUSH, RADIO, SEPARATOR and DROP_DOWN
+ * may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+public class ToolItem extends Item {
+    ToolBar parent;
+    Control control;
+    String toolTipText;
+    Image disabledImage, hotImage;
+    Image disabledImage2;
+    int id;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>ToolBar</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#PUSH
+ * @see DWT#CHECK
+ * @see DWT#RADIO
+ * @see DWT#SEPARATOR
+ * @see DWT#DROP_DOWN
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public ToolItem (ToolBar parent, int style) {
+    super (parent, checkStyle (style));
+    this.parent = parent;
+    parent.createItem (this, parent.getItemCount ());
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>ToolBar</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#PUSH
+ * @see DWT#CHECK
+ * @see DWT#RADIO
+ * @see DWT#SEPARATOR
+ * @see DWT#DROP_DOWN
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public ToolItem (ToolBar parent, int style, int index) {
+    super (parent, checkStyle (style));
+    this.parent = parent;
+    parent.createItem (this, index);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called when the mouse is over the arrow portion of a drop-down tool,
+ * the event object detail field contains the value <code>DWT.ARROW</code>.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the control is selected by the user,
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener(SelectionListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.PUSH, DWT.CHECK, DWT.RADIO, DWT.SEPARATOR, DWT.DROP_DOWN, 0);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+void click (bool dropDown) {
+    int hwnd = parent.handle;
+    if (OS.GetKeyState (OS.VK_LBUTTON) < 0) return;
+    int index = OS.SendMessage (hwnd, OS.TB_COMMANDTOINDEX, id, 0);
+    RECT rect = new RECT ();
+    OS.SendMessage (hwnd, OS.TB_GETITEMRECT, index, rect);
+    int hotIndex = OS.SendMessage (hwnd, OS.TB_GETHOTITEM, 0, 0);
+
+    /*
+    * In order to emulate all the processing that
+    * happens when a mnemonic key is pressed, fake
+    * a mouse press and release.  This will ensure
+    * that radio and pull down items are handled
+    * properly.
+    */
+    int y = rect.top + (rect.bottom - rect.top) / 2;
+    int lParam = ((dropDown ? rect.right - 1 : rect.left) & 0xFFFF) | ((y << 16) & 0xFFFF0000);
+    parent.ignoreMouse = true;
+    OS.SendMessage (hwnd, OS.WM_LBUTTONDOWN, 0, lParam);
+    OS.SendMessage (hwnd, OS.WM_LBUTTONUP, 0, lParam);
+    parent.ignoreMouse = false;
+
+    if (hotIndex !is -1) {
+        OS.SendMessage (hwnd, OS.TB_SETHOTITEM, hotIndex, 0);
+    }
+}
+
+void destroyWidget () {
+    parent.destroyItem (this);
+    releaseHandle ();
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent.
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds () {
+    checkWidget();
+    int hwnd = parent.handle;
+    int index = OS.SendMessage (hwnd, OS.TB_COMMANDTOINDEX, id, 0);
+    RECT rect = new RECT ();
+    OS.SendMessage (hwnd, OS.TB_GETITEMRECT, index, rect);
+    int width = rect.right - rect.left;
+    int height = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+/**
+ * Returns the control that is used to fill the bounds of
+ * the item when the item is a <code>SEPARATOR</code>.
+ *
+ * @return the control
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Control getControl () {
+    checkWidget();
+    return control;
+}
+
+/**
+ * Returns the receiver's disabled image if it has one, or null
+ * if it does not.
+ * <p>
+ * The disabled image is displayed when the receiver is disabled.
+ * </p>
+ *
+ * @return the receiver's disabled image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getDisabledImage () {
+    checkWidget();
+    return disabledImage;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled, and
+ * <code>false</code> otherwise. A disabled control is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #isEnabled
+ */
+public bool getEnabled () {
+    checkWidget();
+    if ((style & DWT.SEPARATOR) !is 0) {
+        return (state & DISABLED) is 0;
+    }
+    int hwnd = parent.handle;
+    int fsState = OS.SendMessage (hwnd, OS.TB_GETSTATE, id, 0);
+    return (fsState & OS.TBSTATE_ENABLED) !is 0;
+}
+
+/**
+ * Returns the receiver's hot image if it has one, or null
+ * if it does not.
+ * <p>
+ * The hot image is displayed when the mouse enters the receiver.
+ * </p>
+ *
+ * @return the receiver's hot image
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getHotImage () {
+    checkWidget();
+    return hotImage;
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>ToolBar</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public ToolBar getParent () {
+    checkWidget();
+    return parent;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is selected,
+ * and false otherwise.
+ * <p>
+ * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
+ * it is selected when it is checked (which some platforms draw as a
+ * pushed in button). If the receiver is of any other type, this method
+ * returns false.
+ * </p>
+ *
+ * @return the selection state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getSelection () {
+    checkWidget();
+    if ((style & (DWT.CHECK | DWT.RADIO)) is 0) return false;
+    int hwnd = parent.handle;
+    int fsState = OS.SendMessage (hwnd, OS.TB_GETSTATE, id, 0);
+    return (fsState & OS.TBSTATE_CHECKED) !is 0;
+}
+
+/**
+ * Returns the receiver's tool tip text, or null if it has not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getToolTipText () {
+    checkWidget();
+    return toolTipText;
+}
+
+/**
+ * Gets the width of the receiver.
+ *
+ * @return the width
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getWidth () {
+    checkWidget();
+    int hwnd = parent.handle;
+    int index = OS.SendMessage (hwnd, OS.TB_COMMANDTOINDEX, id, 0);
+    RECT rect = new RECT ();
+    OS.SendMessage (hwnd, OS.TB_GETITEMRECT, index, rect);
+    return rect.right - rect.left;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is enabled and all
+ * of the receiver's ancestors are enabled, and <code>false</code>
+ * otherwise. A disabled control is typically not selectable from the
+ * user interface and draws with an inactive or "grayed" look.
+ *
+ * @return the receiver's enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getEnabled
+ */
+public bool isEnabled () {
+    checkWidget();
+    return getEnabled () && parent.isEnabled ();
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    releaseImages ();
+    control = null;
+    toolTipText = null;
+    disabledImage = hotImage = null;
+    if (disabledImage2 !is null) disabledImage2.dispose ();
+    disabledImage2 = null;
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+    id = -1;
+}
+
+void releaseImages () {
+    TBBUTTONINFO info = new TBBUTTONINFO ();
+    info.cbSize = TBBUTTONINFO.sizeof;
+    info.dwMask = OS.TBIF_IMAGE | OS.TBIF_STYLE;
+    int hwnd = parent.handle;
+    OS.SendMessage (hwnd, OS.TB_GETBUTTONINFO, id, info);
+    /*
+    * Feature in Windows.  For some reason, a tool item that has
+    * the style BTNS_SEP does not return I_IMAGENONE when queried
+    * for an image index, despite the fact that no attempt has been
+    * made to assign an image to the item.  As a result, operations
+    * on an image list that use the wrong index cause random results.
+    * The fix is to ensure that the tool item is not a separator
+    * before using the image index.  Since separators cannot have
+    * an image and one is never assigned, this is not a problem.
+    */
+    if ((info.fsStyle & OS.BTNS_SEP) is 0 && info.iImage !is OS.I_IMAGENONE) {
+        ImageList imageList = parent.getImageList ();
+        ImageList hotImageList = parent.getHotImageList ();
+        ImageList disabledImageList = parent.getDisabledImageList();
+        if (imageList !is null) imageList.put (info.iImage, null);
+        if (hotImageList !is null) hotImageList.put (info.iImage, null);
+        if (disabledImageList !is null) disabledImageList.put (info.iImage, null);
+    }
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+void resizeControl () {
+    if (control !is null && !control.isDisposed ()) {
+        /*
+        * Set the size and location of the control
+        * separately to minimize flashing in the
+        * case where the control does not resize
+        * to the size that was requested.  This
+        * case can occur when the control is a
+        * combo box.
+        */
+        Rectangle itemRect = getBounds ();
+        control.setSize (itemRect.width, itemRect.height);
+        Rectangle rect = control.getBounds ();
+        rect.x = itemRect.x + (itemRect.width - rect.width) / 2;
+        rect.y = itemRect.y + (itemRect.height - rect.height) / 2;
+        control.setLocation (rect.x, rect.y);
+    }
+}
+
+void selectRadio () {
+    int index = 0;
+    ToolItem [] items = parent.getItems ();
+    while (index < items.length && items [index] !is this) index++;
+    int i = index - 1;
+    while (i >= 0 && items [i].setRadioSelection (false)) --i;
+    int j = index + 1;
+    while (j < items.length && items [j].setRadioSelection (false)) j++;
+    setSelection (true);
+}
+
+/**
+ * Sets the control that is used to fill the bounds of
+ * the item when the item is a <code>SEPARATOR</code>.
+ *
+ * @param control the new control
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
+ *    <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setControl (Control control) {
+    checkWidget();
+    if (control !is null) {
+        if (control.isDisposed()) error (DWT.ERROR_INVALID_ARGUMENT);
+        if (control.parent !is parent) error (DWT.ERROR_INVALID_PARENT);
+    }
+    if ((style & DWT.SEPARATOR) is 0) return;
+    this.control = control;
+    /*
+    * Feature in Windows.  When a tool bar wraps, tool items
+    * with the style BTNS_SEP are used as wrap points.  This
+    * means that controls that are placed on top of separator
+    * items are not positioned properly.  Also, vertical tool
+    * bars are implemented using TB_SETROWS to set the number
+    * of rows.  When a control is placed on top of a separator,
+    * the height of the separator does not grow.  The fix in
+    * both cases is to change the tool item style from BTNS_SEP
+    * to BTNS_BUTTON, causing the item to wrap like a tool item
+    * button.  The new tool item button is disabled to avoid key
+    * traversal and the image is set to I_IMAGENONE to avoid
+    * getting the first image from the image list.
+    */
+    if ((parent.style & (DWT.WRAP | DWT.VERTICAL)) !is 0) {
+        bool changed = false;
+        int hwnd = parent.handle;
+        TBBUTTONINFO info = new TBBUTTONINFO ();
+        info.cbSize = TBBUTTONINFO.sizeof;
+        info.dwMask = OS.TBIF_STYLE | OS.TBIF_STATE;
+        OS.SendMessage (hwnd, OS.TB_GETBUTTONINFO, id, info);
+        if (control is null) {
+            if ((info.fsStyle & OS.BTNS_SEP) is 0) {
+                changed = true;
+                info.fsStyle &= ~OS.BTNS_BUTTON;
+                info.fsStyle |= OS.BTNS_SEP;
+                if ((state & DISABLED) !is 0) {
+                    info.fsState &= ~OS.TBSTATE_ENABLED;
+                } else {
+                    info.fsState |= OS.TBSTATE_ENABLED;
+                }
+            }
+        } else {
+            if ((info.fsStyle & OS.BTNS_SEP) !is 0) {
+                changed = true;
+                info.fsStyle &= ~OS.BTNS_SEP;
+                info.fsStyle |= OS.BTNS_BUTTON;
+                info.fsState &= ~OS.TBSTATE_ENABLED;
+                info.dwMask |= OS.TBIF_IMAGE;
+                info.iImage = OS.I_IMAGENONE;
+            }
+        }
+        if (changed) {
+            OS.SendMessage (hwnd, OS.TB_SETBUTTONINFO, id, info);
+            /*
+            * Bug in Windows.  When TB_SETBUTTONINFO changes the
+            * style of a tool item from BTNS_SEP to BTNS_BUTTON
+            * and the tool bar is wrapped, the tool bar does not
+            * redraw properly.  Windows uses separator items as
+            * wrap points and sometimes draws etching above or
+            * below and entire row.  The fix is to redraw the
+            * tool bar.
+            */
+            if (OS.SendMessage (hwnd, OS.TB_GETROWS, 0, 0) > 1) {
+                OS.InvalidateRect (hwnd, null, true);
+            }
+        }
+    }
+    resizeControl ();
+}
+
+/**
+ * Enables the receiver if the argument is <code>true</code>,
+ * and disables it otherwise.
+ * <p>
+ * A disabled control is typically
+ * not selectable from the user interface and draws with an
+ * inactive or "grayed" look.
+ * </p>
+ *
+ * @param enabled the new enabled state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setEnabled (bool enabled) {
+    checkWidget();
+    int hwnd = parent.handle;
+    int fsState = OS.SendMessage (hwnd, OS.TB_GETSTATE, id, 0);
+    /*
+    * Feature in Windows.  When TB_SETSTATE is used to set the
+    * state of a tool item, the item redraws even when the state
+    * has not changed.  The fix is to detect this case and avoid
+    * setting the state.
+    */
+    if (((fsState & OS.TBSTATE_ENABLED) !is 0) is enabled) return;
+    if (enabled) {
+        fsState |= OS.TBSTATE_ENABLED;
+        state &= ~DISABLED;
+    } else {
+        fsState &= ~OS.TBSTATE_ENABLED;
+        state |= DISABLED;
+    }
+    OS.SendMessage (hwnd, OS.TB_SETSTATE, id, fsState);
+    if ((style & DWT.SEPARATOR) is 0) {
+        if (image !is null) updateImages (enabled && parent.getEnabled ());
+    }
+}
+
+/**
+ * Sets the receiver's disabled image to the argument, which may be
+ * null indicating that no disabled image should be displayed.
+ * <p>
+ * The disabled image is displayed when the receiver is disabled.
+ * </p>
+ *
+ * @param image the disabled image to display on the receiver (may be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setDisabledImage (Image image) {
+    checkWidget();
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    if (image !is null && image.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    disabledImage = image;
+    updateImages (getEnabled () && parent.getEnabled ());
+}
+
+/**
+ * Sets the receiver's hot image to the argument, which may be
+ * null indicating that no hot image should be displayed.
+ * <p>
+ * The hot image is displayed when the mouse enters the receiver.
+ * </p>
+ *
+ * @param image the hot image to display on the receiver (may be null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setHotImage (Image image) {
+    checkWidget();
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    if (image !is null && image.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    hotImage = image;
+    updateImages (getEnabled () && parent.getEnabled ());
+}
+
+public void setImage (Image image) {
+    checkWidget();
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    if (image !is null && image.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    super.setImage (image);
+    updateImages (getEnabled () && parent.getEnabled ());
+}
+
+bool setRadioSelection (bool value) {
+    if ((style & DWT.RADIO) is 0) return false;
+    if (getSelection () !is value) {
+        setSelection (value);
+        postEvent (DWT.Selection);
+    }
+    return true;
+}
+
+/**
+ * Sets the selection state of the receiver.
+ * <p>
+ * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
+ * it is selected when it is checked (which some platforms draw as a
+ * pushed in button).
+ * </p>
+ *
+ * @param selected the new selection state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setSelection (bool selected) {
+    checkWidget();
+    if ((style & (DWT.CHECK | DWT.RADIO)) is 0) return;
+    int hwnd = parent.handle;
+    int fsState = OS.SendMessage (hwnd, OS.TB_GETSTATE, id, 0);
+    /*
+    * Feature in Windows.  When TB_SETSTATE is used to set the
+    * state of a tool item, the item redraws even when the state
+    * has not changed.  The fix is to detect this case and avoid
+    * setting the state.
+    */
+    if (((fsState & OS.TBSTATE_CHECKED) !is 0) is selected) return;
+    if (selected) {
+        fsState |= OS.TBSTATE_CHECKED;
+    } else {
+        fsState &= ~OS.TBSTATE_CHECKED;
+    }
+    OS.SendMessage (hwnd, OS.TB_SETSTATE, id, fsState);
+
+    /*
+    * Bug in Windows.  When a tool item with the style
+    * BTNS_CHECK or BTNS_CHECKGROUP is selected and then
+    * disabled, the item does not draw using the disabled
+    * image.  The fix is to use the disabled image in all
+    * image lists for the item.
+    *
+    * NOTE: This means that the image list must be updated
+    * when the selection changes in a disabled tool item.
+    */
+    if ((style & (DWT.CHECK | DWT.RADIO)) !is 0) {
+        if (!getEnabled () || !parent.getEnabled ()) {
+            updateImages (false);
+        }
+    }
+}
+
+/**
+ * Sets the receiver's text. The string may include
+ * the mnemonic character.
+ * </p>
+ * <p>
+ * Mnemonics are indicated by an '&amp;' that causes the next
+ * character to be the mnemonic.  When the user presses a
+ * key sequence that matches the mnemonic, a selection
+ * event occurs. On most platforms, the mnemonic appears
+ * underlined but may be emphasised in a platform specific
+ * manner.  The mnemonic indicator character '&amp;' can be
+ * escaped by doubling it in the string, causing a single
+ * '&amp;' to be displayed.
+ * </p>
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+    checkWidget();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    if (string.equals (text)) return;
+    super.setText (string);
+    int hwnd = parent.handle;
+    TBBUTTONINFO info = new TBBUTTONINFO ();
+    info.cbSize = TBBUTTONINFO.sizeof;
+    info.dwMask = OS.TBIF_TEXT | OS.TBIF_STYLE;
+    info.fsStyle = (byte) (widgetStyle () | OS.BTNS_AUTOSIZE);
+    int hHeap = OS.GetProcessHeap (), pszText = 0;
+    if (string.length () !is 0) {
+        info.fsStyle |= OS.BTNS_SHOWTEXT;
+        TCHAR buffer = new TCHAR (parent.getCodePage (), string, true);
+        int byteCount = buffer.length () * TCHAR.sizeof;
+        pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+        OS.MoveMemory (pszText, buffer, byteCount);
+        info.pszText = pszText;
+    }
+    OS.SendMessage (hwnd, OS.TB_SETBUTTONINFO, id, info);
+    if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+
+    /*
+    * Bug in Windows.  For some reason, when the font is set
+    * before any tool item has text, the tool items resize to
+    * a very small size.  Also, a tool item will only show text
+    * when text has already been set on one item and then a new
+    * item is created.  The fix is to use WM_SETFONT to force
+    * the tool bar to redraw and layout.
+    */
+    parent.setDropDownItems (false);
+    int hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0);
+    OS.SendMessage (hwnd, OS.WM_SETFONT, hFont, 0);
+    parent.setDropDownItems (true);
+    parent.layoutItems ();
+}
+
+/**
+ * Sets the receiver's tool tip text to the argument, which
+ * may be null indicating that no tool tip text should be shown.
+ *
+ * @param string the new tool tip text (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setToolTipText (String string) {
+    checkWidget();
+    toolTipText = string;
+}
+
+/**
+ * Sets the width of the receiver, for <code>SEPARATOR</code> ToolItems.
+ *
+ * @param width the new width
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setWidth (int width) {
+    checkWidget();
+    if ((style & DWT.SEPARATOR) is 0) return;
+    if (width < 0) return;
+    int hwnd = parent.handle;
+    TBBUTTONINFO info = new TBBUTTONINFO ();
+    info.cbSize = TBBUTTONINFO.sizeof;
+    info.dwMask = OS.TBIF_SIZE;
+    info.cx = (short) width;
+    OS.SendMessage (hwnd, OS.TB_SETBUTTONINFO, id, info);
+    parent.layoutItems ();
+}
+
+void updateImages (bool enabled) {
+    if ((style & DWT.SEPARATOR) !is 0) return;
+    int hwnd = parent.handle;
+    TBBUTTONINFO info = new TBBUTTONINFO ();
+    info.cbSize = TBBUTTONINFO.sizeof;
+    info.dwMask = OS.TBIF_IMAGE;
+    OS.SendMessage (hwnd, OS.TB_GETBUTTONINFO, id, info);
+    if (info.iImage is OS.I_IMAGENONE && image is null) return;
+    ImageList imageList = parent.getImageList ();
+    ImageList hotImageList = parent.getHotImageList ();
+    ImageList disabledImageList = parent.getDisabledImageList();
+    if (info.iImage is OS.I_IMAGENONE) {
+        Rectangle bounds = image.getBounds ();
+        int listStyle = parent.style & DWT.RIGHT_TO_LEFT;
+        if (imageList is null) {
+            imageList = display.getImageListToolBar (listStyle, bounds.width, bounds.height);
+        }
+        if (disabledImageList is null) {
+            disabledImageList = display.getImageListToolBarDisabled (listStyle, bounds.width, bounds.height);
+        }
+        if (hotImageList is null) {
+            hotImageList = display.getImageListToolBarHot (listStyle, bounds.width, bounds.height);
+        }
+        Image disabled = disabledImage;
+        if (disabledImage is null) {
+            if (disabledImage2 !is null) disabledImage2.dispose ();
+            disabledImage2 = null;
+            disabled = image;
+            if (!enabled) {
+                disabled = disabledImage2 = new Image (display, image, DWT.IMAGE_DISABLE);
+            }
+        }
+        /*
+        * Bug in Windows.  When a tool item with the style
+        * BTNS_CHECK or BTNS_CHECKGROUP is selected and then
+        * disabled, the item does not draw using the disabled
+        * image.  The fix is to assign the disabled image in
+        * all image lists.
+        */
+        Image image2 = image, hot = hotImage;
+        if ((style & (DWT.CHECK | DWT.RADIO)) !is 0) {
+            if (!enabled) image2 = hot = disabled;
+        }
+        info.iImage = imageList.add (image2);
+        disabledImageList.add (disabled);
+        hotImageList.add (hot !is null ? hot : image2);
+        parent.setImageList (imageList);
+        parent.setDisabledImageList (disabledImageList);
+        parent.setHotImageList (hotImageList);
+    } else {
+        Image disabled = null;
+        if (disabledImageList !is null) {
+            if (image !is null) {
+                if (disabledImage2 !is null) disabledImage2.dispose ();
+                disabledImage2 = null;
+                disabled = disabledImage;
+                if (disabledImage is null) {
+                    disabled = image;
+                    if (!enabled) {
+                        disabled = disabledImage2 = new Image (display, image, DWT.IMAGE_DISABLE);
+                    }
+                }
+            }
+            disabledImageList.put (info.iImage, disabled);
+        }
+        /*
+        * Bug in Windows.  When a tool item with the style
+        * BTNS_CHECK or BTNS_CHECKGROUP is selected and then
+        * disabled, the item does not draw using the disabled
+        * image.  The fix is to use the disabled image in all
+        * image lists.
+        */
+        Image image2 = image, hot = hotImage;
+        if ((style & (DWT.CHECK | DWT.RADIO)) !is 0) {
+            if (!enabled) image2 = hot = disabled;
+        }
+        if (imageList !is null) imageList.put (info.iImage, image2);
+        if (hotImageList !is null) {
+            hotImageList.put (info.iImage, hot !is null ? hot : image2);
+        }
+        if (image is null) info.iImage = OS.I_IMAGENONE;
+    }
+
+    /*
+    * Bug in Windows.  If the width of an item has already been
+    * calculated, the tool bar control will not recalculate it to
+    * include the space for the image.  The fix is to set the width
+    * to zero, forcing the control recalculate the width for the item.
+    */
+    info.dwMask |= OS.TBIF_SIZE;
+    info.cx = 0;
+    OS.SendMessage (hwnd, OS.TB_SETBUTTONINFO, id, info);
+
+    parent.layoutItems ();
+}
+
+int widgetStyle () {
+    if ((style & DWT.DROP_DOWN) !is 0) return OS.BTNS_DROPDOWN;
+    if ((style & DWT.PUSH) !is 0) return OS.BTNS_BUTTON;
+    if ((style & DWT.CHECK) !is 0) return OS.BTNS_CHECK;
+    /*
+    * This code is intentionally commented.  In order to
+    * consistently support radio tool items across platforms,
+    * the platform radio behavior is not used.
+    */
+//  if ((style & DWT.RADIO) !is 0) return OS.BTNS_CHECKGROUP;
+    if ((style & DWT.RADIO) !is 0) return OS.BTNS_CHECK;
+    if ((style & DWT.SEPARATOR) !is 0) return OS.BTNS_SEP;
+    return OS.BTNS_BUTTON;
+}
+
+LRESULT wmCommandChild (int wParam, int lParam) {
+    if ((style & DWT.RADIO) !is 0) {
+        if ((parent.getStyle () & DWT.NO_RADIO_GROUP) is 0) {
+            selectRadio ();
+        }
+    }
+    postEvent (DWT.Selection);
+    return null;
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/ToolTip.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,565 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.ToolTip;
+
+import dwt.widgets.Widget;
+class ToolTip : Widget {
+    this( Widget, int );
+}
+/++
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Point;
+import dwt.internal.win32.MONITORINFO;
+import dwt.internal.win32.NOTIFYICONDATA;
+import dwt.internal.win32.NOTIFYICONDATAA;
+import dwt.internal.win32.NOTIFYICONDATAW;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TOOLINFO;
+
+/**
+ * Instances of this class represent popup windows that are used
+ * to inform or warn the user.
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>BALLOON, ICON_ERROR, ICON_INFORMATION, ICON_WARNING</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles ICON_ERROR, ICON_INFORMATION,
+ * and ICON_WARNING may be specified.
+ * </p><p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation.
+ * </p>
+ *
+ * @since 3.2
+ */
+
+public class ToolTip extends Widget {
+    Shell parent;
+    TrayItem item;
+    String text = "", message = "";
+    int id, x, y;
+    bool autoHide = true, hasLocation, visible;
+    static final int TIMER_ID = 100;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#ICON_ERROR
+ * @see DWT#ICON_INFORMATION
+ * @see DWT#ICON_WARNING
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public ToolTip (Shell parent, int style) {
+    super (parent, checkStyle (style));
+    this.parent = parent;
+    checkOrientation (parent);
+    parent.createToolTip (this);
+}
+
+static int checkStyle (int style) {
+    int mask = DWT.ICON_ERROR | DWT.ICON_INFORMATION | DWT.ICON_WARNING;
+    if ((style & mask) is 0) return style;
+    return checkBits (style, DWT.ICON_INFORMATION, DWT.ICON_WARNING, DWT.ICON_ERROR, 0, 0, 0);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the receiver is selected.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the receiver is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener(listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+void destroyWidget () {
+    if (parent !is null) parent.destroyToolTip (this);
+    releaseHandle ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is automatically
+ * hidden by the platform, and <code>false</code> otherwise.
+ *
+ * @return the receiver's auto hide state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ */
+public bool getAutoHide () {
+    checkWidget();
+    return autoHide;
+}
+
+/**
+ * Returns the receiver's message, which will be an empty
+ * string if it has never been set.
+ *
+ * @return the receiver's message
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getMessage () {
+    checkWidget();
+    return message;
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Shell</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Shell getParent () {
+    checkWidget ();
+    return parent;
+}
+
+/**
+ * Returns the receiver's text, which will be an empty
+ * string if it has never been set.
+ *
+ * @return the receiver's text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText () {
+    checkWidget();
+    return text;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible, and
+ * <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getVisible () {
+    checkWidget();
+    if (OS.IsWinCE) return false;
+    if (item !is null) return visible;
+    int hwndToolTip = hwndToolTip ();
+    if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, 0) !is 0) {
+        TOOLINFO lpti = new TOOLINFO ();
+        lpti.cbSize = TOOLINFO.sizeof;
+        if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) !is 0) {
+            return (lpti.uFlags & OS.TTF_IDISHWND) is 0 && lpti.uId is id;
+        }
+    }
+    return false;
+}
+
+int hwndToolTip () {
+    return (style & DWT.BALLOON) !is 0 ? parent.balloonTipHandle () : parent.toolTipHandle ();
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible and all
+ * of the receiver's ancestors are visible and <code>false</code>
+ * otherwise.
+ *
+ * @return the receiver's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getVisible
+ */
+public bool isVisible () {
+    checkWidget ();
+    if (item !is null) return getVisible () && item.getVisible ();
+    return getVisible ();
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+    item = null;
+    id = -1;
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if (item is null) {
+        if (autoHide) {
+            int hwndToolTip = hwndToolTip ();
+            if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, 0) !is 0) {
+                TOOLINFO lpti = new TOOLINFO ();
+                lpti.cbSize = TOOLINFO.sizeof;
+                if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) !is 0) {
+                    if ((lpti.uFlags & OS.TTF_IDISHWND) is 0) {
+                        if (lpti.uId is id) {
+                            OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 0, lpti);
+                            OS.SendMessage (hwndToolTip, OS.TTM_POP, 0, 0);
+                            OS.KillTimer (hwndToolTip, TIMER_ID);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if (item !is null && item.toolTip is this) {
+        item.toolTip = null;
+    }
+    item = null;
+    text = message = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the receiver is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Makes the receiver hide automatically when <code>true</code>,
+ * and remain visible when <code>false</code>.
+ *
+ * @param autoHide the auto hide state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getVisible
+ * @see #setVisible
+ */
+public void setAutoHide (bool autoHide) {
+    checkWidget ();
+    this.autoHide = autoHide;
+    //TODO - update when visible
+}
+
+/**
+ * Sets the location of the receiver, which must be a tooltip,
+ * to the point specified by the arguments which are relative
+ * to the display.
+ * <p>
+ * Note that this is different from most widgets where the
+ * location of the widget is relative to the parent.
+ * </p>
+ *
+ * @param x the new x coordinate for the receiver
+ * @param y the new y coordinate for the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLocation (int x, int y) {
+    checkWidget ();
+    this.x = x;
+    this.y = y;
+    hasLocation = true;
+    //TODO - update when visible
+}
+
+/**
+ * Sets the location of the receiver, which must be a tooltip,
+ * to the point specified by the argument which is relative
+ * to the display.
+ * <p>
+ * Note that this is different from most widgets where the
+ * location of the widget is relative to the parent.
+ * </p><p>
+ * Note that the platform window manager ultimately has control
+ * over the location of tooltips.
+ * </p>
+ *
+ * @param location the new location for the receiver
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLocation (Point location) {
+    checkWidget ();
+    if (location is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    setLocation (location.x, location.y);
+}
+
+/**
+ * Sets the receiver's message.
+ *
+ * @param string the new message
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setMessage (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    message = string;
+    //TODO - update when visible
+}
+
+/**
+ * Sets the receiver's text.
+ *
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    text = string;
+    //TODO - update when visible
+}
+
+/**
+ * Marks the receiver as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param visible the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setVisible (bool visible) {
+    checkWidget ();
+    if (OS.IsWinCE) return;
+    if (visible is getVisible ()) return;
+    if (item is null) {
+        int hwnd = parent.handle;
+        TOOLINFO lpti = new TOOLINFO ();
+        lpti.cbSize = TOOLINFO.sizeof;
+        lpti.uId = id;
+        lpti.hwnd = hwnd;
+        int hwndToolTip = hwndToolTip ();
+        Shell shell = parent.getShell ();
+        if (text.length () !is 0) {
+            int icon = OS.TTI_NONE;
+            if ((style & DWT.ICON_INFORMATION) !is 0) icon = OS.TTI_INFO;
+            if ((style & DWT.ICON_WARNING) !is 0) icon = OS.TTI_WARNING;
+            if ((style & DWT.ICON_ERROR) !is 0) icon = OS.TTI_ERROR;
+            shell.setToolTipTitle (hwndToolTip, text, icon);
+        } else {
+            shell.setToolTipTitle (hwndToolTip, null, 0);
+        }
+        int maxWidth = 0;
+        if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (4, 10)) {
+            RECT rect = new RECT ();
+            OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, rect, 0);
+            maxWidth = (rect.right - rect.left) / 4;
+        } else {
+            int hmonitor = OS.MonitorFromWindow (hwnd, OS.MONITOR_DEFAULTTONEAREST);
+            MONITORINFO lpmi = new MONITORINFO ();
+            lpmi.cbSize = MONITORINFO.sizeof;
+            OS.GetMonitorInfo (hmonitor, lpmi);
+            maxWidth = (lpmi.rcWork_right - lpmi.rcWork_left) / 4;
+        }
+        OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, maxWidth);
+        if (visible) {
+            int nX = x, nY = y;
+            if (!hasLocation) {
+                POINT pt = new POINT ();
+                if (OS.GetCursorPos (pt)) {
+                    nX = pt.x;
+                    nY = pt.y;
+                }
+            }
+            int lParam = (nX & 0xFFFF) | ((nY << 16) & 0xFFFF0000);
+            OS.SendMessage (hwndToolTip, OS.TTM_TRACKPOSITION, 0, lParam);
+
+            /*
+            * Feature in Windows.  Windows will not show a tool tip
+            * if the cursor is outside the parent window (even on XP,
+            * TTM_POPUP will not do this).  The fix is to temporarily
+            * move the cursor into the tool window, show the tool tip,
+            * and then restore the cursor.
+            */
+            POINT pt = new POINT ();
+            OS.GetCursorPos (pt);
+            RECT rect = new RECT ();
+            OS.GetClientRect (hwnd, rect);
+            OS.MapWindowPoints (hwnd, 0, rect, 2);
+            if (!OS.PtInRect (rect, pt)) {
+                int hCursor = OS.GetCursor ();
+                OS.SetCursor (0);
+                OS.SetCursorPos (rect.left, rect.top);
+                OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 1, lpti);
+                OS.SetCursorPos (pt.x, pt.y);
+                OS.SetCursor (hCursor);
+            } else {
+                OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 1, lpti);
+            }
+
+            int time = OS.SendMessage (hwndToolTip, OS.TTM_GETDELAYTIME, OS.TTDT_AUTOPOP, 0);
+            OS.SetTimer (hwndToolTip, TIMER_ID, time, 0);
+        } else {
+            OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 0, lpti);
+            OS.SendMessage (hwndToolTip, OS.TTM_POP, 0, 0);
+            OS.KillTimer (hwndToolTip, TIMER_ID);
+        }
+        return;
+    }
+    if (item !is null && OS.SHELL32_MAJOR >= 5) {
+        if (visible) {
+            NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA ();
+            TCHAR buffer1 = new TCHAR (0, text, true);
+            TCHAR buffer2 = new TCHAR (0, message, true);
+            if (OS.IsUnicode) {
+                char [] szInfoTitle = ((NOTIFYICONDATAW) iconData).szInfoTitle;
+                int length1 = Math.min (szInfoTitle.length - 1, buffer1.length ());
+                System.arraycopy (buffer1.chars, 0, szInfoTitle, 0, length1);
+                char [] szInfo = ((NOTIFYICONDATAW) iconData).szInfo;
+                int length2 = Math.min (szInfo.length - 1, buffer2.length ());
+                System.arraycopy (buffer2.chars, 0, szInfo, 0, length2);
+            } else {
+                byte [] szInfoTitle = ((NOTIFYICONDATAA) iconData).szInfoTitle;
+                int length = Math.min (szInfoTitle.length - 1, buffer1.length ());
+                System.arraycopy (buffer1.bytes, 0, szInfoTitle, 0, length);
+                byte [] szInfo = ((NOTIFYICONDATAA) iconData).szInfo;
+                int length2 = Math.min (szInfo.length - 1, buffer2.length ());
+                System.arraycopy (buffer2.bytes, 0, szInfo, 0, length2);
+            }
+            Display display = item.getDisplay ();
+            iconData.cbSize = NOTIFYICONDATA.sizeof;
+            iconData.uID = item.id;
+            iconData.hWnd = display.hwndMessage;
+            iconData.uFlags = OS.NIF_INFO;
+            if ((style & DWT.ICON_INFORMATION) !is 0) iconData.dwInfoFlags = OS.NIIF_INFO;
+            if ((style & DWT.ICON_WARNING) !is 0) iconData.dwInfoFlags = OS.NIIF_WARNING;
+            if ((style & DWT.ICON_ERROR) !is 0) iconData.dwInfoFlags = OS.NIIF_ERROR;
+            sendEvent (DWT.Show);
+            this.visible = OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData);
+        } else {
+            //TODO - hide the tray item
+        }
+    }
+}
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Tracker.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1173 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Tracker;
+
+import dwt.widgets.Widget;
+class Tracker : Widget {
+    this( Widget, int );
+}
+/++
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.ControlListener;
+import dwt.events.KeyListener;
+import dwt.graphics.Cursor;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.Callback;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MSG;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.PAINTSTRUCT;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+
+/**
+ *  Instances of this class implement rubber banding rectangles that are
+ *  drawn onto a parent <code>Composite</code> or <code>Display</code>.
+ *  These rectangles can be specified to respond to mouse and key events
+ *  by either moving or resizing themselves accordingly.  Trackers are
+ *  typically used to represent window geometries in a lightweight manner.
+ *
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>LEFT, RIGHT, UP, DOWN, RESIZE</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Move, Resize</dd>
+ * </dl>
+ * <p>
+ * Note: Rectangle move behavior is assumed unless RESIZE is specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+public class Tracker extends Widget {
+    Control parent;
+    bool tracking, cancelled, stippled;
+    Rectangle [] rectangles = new Rectangle [0], proportions = rectangles;
+    Rectangle bounds;
+    int resizeCursor, clientCursor, cursorOrientation = DWT.NONE;
+    bool inEvent = false;
+    int hwndTransparent, oldProc, oldX, oldY;
+
+    /*
+    * The following values mirror step sizes on Windows
+    */
+    final static int STEPSIZE_SMALL = 1;
+    final static int STEPSIZE_LARGE = 9;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#LEFT
+ * @see DWT#RIGHT
+ * @see DWT#UP
+ * @see DWT#DOWN
+ * @see DWT#RESIZE
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Tracker (Composite parent, int style) {
+    super (parent, checkStyle (style));
+    this.parent = parent;
+}
+
+/**
+ * Constructs a new instance of this class given the display
+ * to create it on and a style value describing its behavior
+ * and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p><p>
+ * Note: Currently, null can be passed in for the display argument.
+ * This has the effect of creating the tracker on the currently active
+ * display if there is one. If there is no current display, the
+ * tracker is created on a "default" display. <b>Passing in null as
+ * the display argument is not considered to be good coding style,
+ * and may not be supported in a future release of DWT.</b>
+ * </p>
+ *
+ * @param display the display to create the tracker on
+ * @param style the style of control to construct
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#LEFT
+ * @see DWT#RIGHT
+ * @see DWT#UP
+ * @see DWT#DOWN
+ */
+public Tracker (Display display, int style) {
+    if (display is null) display = Display.getCurrent ();
+    if (display is null) display = Display.getDefault ();
+    if (!display.isValidThread ()) {
+        error (DWT.ERROR_THREAD_INVALID_ACCESS);
+    }
+    this.style = checkStyle (style);
+    this.display = display;
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is moved or resized, by sending
+ * it one of the messages defined in the <code>ControlListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #removeControlListener
+ */
+public void addControlListener (ControlListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Resize, typedListener);
+    addListener (DWT.Move, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when keys are pressed and released on the system keyboard, by sending
+ * it one of the messages defined in the <code>KeyListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see KeyListener
+ * @see #removeKeyListener
+ */
+public void addKeyListener (KeyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.KeyUp,typedListener);
+    addListener (DWT.KeyDown,typedListener);
+}
+
+Point adjustMoveCursor () {
+    if (bounds is null) return null;
+    int newX = bounds.x + bounds.width / 2;
+    int newY = bounds.y;
+    POINT pt = new POINT ();
+    pt.x = newX;  pt.y = newY;
+    /*
+     * Convert to screen coordinates iff needed
+     */
+    if (parent !is null) {
+        OS.ClientToScreen (parent.handle, pt);
+    }
+    OS.SetCursorPos (pt.x, pt.y);
+    return new Point (pt.x, pt.y);
+}
+
+Point adjustResizeCursor () {
+    if (bounds is null) return null;
+    int newX, newY;
+
+    if ((cursorOrientation & DWT.LEFT) !is 0) {
+        newX = bounds.x;
+    } else if ((cursorOrientation & DWT.RIGHT) !is 0) {
+        newX = bounds.x + bounds.width;
+    } else {
+        newX = bounds.x + bounds.width / 2;
+    }
+
+    if ((cursorOrientation & DWT.UP) !is 0) {
+        newY = bounds.y;
+    } else if ((cursorOrientation & DWT.DOWN) !is 0) {
+        newY = bounds.y + bounds.height;
+    } else {
+        newY = bounds.y + bounds.height / 2;
+    }
+
+    POINT pt = new POINT ();
+    pt.x = newX;  pt.y = newY;
+    /*
+     * Convert to screen coordinates iff needed
+     */
+    if (parent !is null) {
+        OS.ClientToScreen (parent.handle, pt);
+    }
+    OS.SetCursorPos (pt.x, pt.y);
+
+    /*
+    * If the client has not provided a custom cursor then determine
+    * the appropriate resize cursor.
+    */
+    if (clientCursor is 0) {
+        int newCursor = 0;
+        switch (cursorOrientation) {
+            case DWT.UP:
+                newCursor = OS.LoadCursor (0, OS.IDC_SIZENS);
+                break;
+            case DWT.DOWN:
+                newCursor = OS.LoadCursor (0, OS.IDC_SIZENS);
+                break;
+            case DWT.LEFT:
+                newCursor = OS.LoadCursor (0, OS.IDC_SIZEWE);
+                break;
+            case DWT.RIGHT:
+                newCursor = OS.LoadCursor (0, OS.IDC_SIZEWE);
+                break;
+            case DWT.LEFT | DWT.UP:
+                newCursor = OS.LoadCursor (0, OS.IDC_SIZENWSE);
+                break;
+            case DWT.RIGHT | DWT.DOWN:
+                newCursor = OS.LoadCursor (0, OS.IDC_SIZENWSE);
+                break;
+            case DWT.LEFT | DWT.DOWN:
+                newCursor = OS.LoadCursor (0, OS.IDC_SIZENESW);
+                break;
+            case DWT.RIGHT | DWT.UP:
+                newCursor = OS.LoadCursor (0, OS.IDC_SIZENESW);
+                break;
+            default:
+                newCursor = OS.LoadCursor (0, OS.IDC_SIZEALL);
+                break;
+        }
+        OS.SetCursor (newCursor);
+        if (resizeCursor !is 0) {
+            OS.DestroyCursor (resizeCursor);
+        }
+        resizeCursor = newCursor;
+    }
+
+    return new Point (pt.x, pt.y);
+}
+
+static int checkStyle (int style) {
+    if ((style & (DWT.LEFT | DWT.RIGHT | DWT.UP | DWT.DOWN)) is 0) {
+        style |= DWT.LEFT | DWT.RIGHT | DWT.UP | DWT.DOWN;
+    }
+    return style;
+}
+
+/**
+ * Stops displaying the tracker rectangles.  Note that this is not considered
+ * to be a cancelation by the user.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void close () {
+    checkWidget ();
+    tracking = false;
+}
+
+Rectangle computeBounds () {
+    if (rectangles.length is 0) return null;
+    int xMin = rectangles [0].x;
+    int yMin = rectangles [0].y;
+    int xMax = rectangles [0].x + rectangles [0].width;
+    int yMax = rectangles [0].y + rectangles [0].height;
+
+    for (int i = 1; i < rectangles.length; i++) {
+        if (rectangles [i].x < xMin) xMin = rectangles [i].x;
+        if (rectangles [i].y < yMin) yMin = rectangles [i].y;
+        int rectRight = rectangles [i].x + rectangles [i].width;
+        if (rectRight > xMax) xMax = rectRight;
+        int rectBottom = rectangles [i].y + rectangles [i].height;
+        if (rectBottom > yMax) yMax = rectBottom;
+    }
+
+    return new Rectangle (xMin, yMin, xMax - xMin, yMax - yMin);
+}
+
+Rectangle [] computeProportions (Rectangle [] rects) {
+    Rectangle [] result = new Rectangle [rects.length];
+    bounds = computeBounds ();
+    if (bounds !is null) {
+        for (int i = 0; i < rects.length; i++) {
+            int x = 0, y = 0, width = 0, height = 0;
+            if (bounds.width !is 0) {
+                x = (rects [i].x - bounds.x) * 100 / bounds.width;
+                width = rects [i].width * 100 / bounds.width;
+            } else {
+                width = 100;
+            }
+            if (bounds.height !is 0) {
+                y = (rects [i].y - bounds.y) * 100 / bounds.height;
+                height = rects [i].height * 100 / bounds.height;
+            } else {
+                height = 100;
+            }
+            result [i] = new Rectangle (x, y, width, height);
+        }
+    }
+    return result;
+}
+
+/**
+ * Draw the rectangles displayed by the tracker.
+ */
+void drawRectangles (Rectangle [] rects, bool stippled) {
+    if (parent is null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        RECT rect1 = new RECT();
+        int bandWidth = stippled ? 3 : 1;
+        for (int i = 0; i < rects.length; i++) {
+            Rectangle rect = rects[i];
+            rect1.left = rect.x - bandWidth;
+            rect1.top = rect.y - bandWidth;
+            rect1.right = rect.x + rect.width + bandWidth * 2;
+            rect1.bottom = rect.y + rect.height + bandWidth * 2;
+            OS.RedrawWindow (hwndTransparent, rect1, 0, OS.RDW_INVALIDATE);
+        }
+        return;
+    }
+    int bandWidth = 1;
+    int hwndTrack = OS.GetDesktopWindow ();
+    if (parent !is null) hwndTrack = parent.handle;
+    int hDC = OS.GetDCEx (hwndTrack, 0, OS.DCX_CACHE);
+    int hBitmap = 0, hBrush = 0, oldBrush = 0;
+    if (stippled) {
+        bandWidth = 3;
+        byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0};
+        hBitmap = OS.CreateBitmap (8, 8, 1, 1, bits);
+        hBrush = OS.CreatePatternBrush (hBitmap);
+        oldBrush = OS.SelectObject (hDC, hBrush);
+    }
+    for (int i=0; i<rects.length; i++) {
+        Rectangle rect = rects [i];
+        OS.PatBlt (hDC, rect.x, rect.y, rect.width, bandWidth, OS.PATINVERT);
+        OS.PatBlt (hDC, rect.x, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT);
+        OS.PatBlt (hDC, rect.x + rect.width - bandWidth, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATINVERT);
+        OS.PatBlt (hDC, rect.x, rect.y + rect.height - bandWidth, rect.width, bandWidth, OS.PATINVERT);
+    }
+    if (stippled) {
+        OS.SelectObject (hDC, oldBrush);
+        OS.DeleteObject (hBrush);
+        OS.DeleteObject (hBitmap);
+    }
+    OS.ReleaseDC (hwndTrack, hDC);
+}
+
+/**
+ * Returns the bounds that are being drawn, expressed relative to the parent
+ * widget.  If the parent is a <code>Display</code> then these are screen
+ * coordinates.
+ *
+ * @return the bounds of the Rectangles being drawn
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle [] getRectangles () {
+    checkWidget();
+    Rectangle [] result = new Rectangle [rectangles.length];
+    for (int i = 0; i < rectangles.length; i++) {
+        Rectangle current = rectangles [i];
+        result [i] = new Rectangle (current.x, current.y, current.width, current.height);
+    }
+    return result;
+}
+
+/**
+ * Returns <code>true</code> if the rectangles are drawn with a stippled line, <code>false</code> otherwise.
+ *
+ * @return the stippled effect of the rectangles
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getStippled () {
+    checkWidget ();
+    return stippled;
+}
+
+void moveRectangles (int xChange, int yChange) {
+    if (bounds is null) return;
+    if (xChange < 0 && ((style & DWT.LEFT) is 0)) xChange = 0;
+    if (xChange > 0 && ((style & DWT.RIGHT) is 0)) xChange = 0;
+    if (yChange < 0 && ((style & DWT.UP) is 0)) yChange = 0;
+    if (yChange > 0 && ((style & DWT.DOWN) is 0)) yChange = 0;
+    if (xChange is 0 && yChange is 0) return;
+    bounds.x += xChange; bounds.y += yChange;
+    for (int i = 0; i < rectangles.length; i++) {
+        rectangles [i].x += xChange;
+        rectangles [i].y += yChange;
+    }
+}
+
+/**
+ * Displays the Tracker rectangles for manipulation by the user.  Returns when
+ * the user has either finished manipulating the rectangles or has cancelled the
+ * Tracker.
+ *
+ * @return <code>true</code> if the user did not cancel the Tracker, <code>false</code> otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool open () {
+    checkWidget ();
+    cancelled = false;
+    tracking = true;
+
+    /*
+    * If exactly one of UP/DOWN is specified as a style then set the cursor
+    * orientation accordingly (the same is done for LEFT/RIGHT styles below).
+    */
+    int vStyle = style & (DWT.UP | DWT.DOWN);
+    if (vStyle is DWT.UP || vStyle is DWT.DOWN) {
+        cursorOrientation |= vStyle;
+    }
+    int hStyle = style & (DWT.LEFT | DWT.RIGHT);
+    if (hStyle is DWT.LEFT || hStyle is DWT.RIGHT) {
+        cursorOrientation |= hStyle;
+    }
+
+    /*
+    * If this tracker is being created without a mouse drag then
+    * we need to create a transparent window that fills the screen
+    * in order to get all mouse/keyboard events that occur
+    * outside of our visible windows (ie.- over the desktop).
+    */
+    Callback newProc = null;
+    bool mouseDown = OS.GetKeyState(OS.VK_LBUTTON) < 0;
+    bool isVista = !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0);
+    if ((parent is null && isVista) || !mouseDown) {
+        int width = OS.GetSystemMetrics (OS.SM_CXSCREEN);
+        int height = OS.GetSystemMetrics (OS.SM_CYSCREEN);
+        hwndTransparent = OS.CreateWindowEx (
+            isVista ? OS.WS_EX_LAYERED | OS.WS_EX_NOACTIVATE : OS.WS_EX_TRANSPARENT,
+            display.windowClass,
+            null,
+            OS.WS_POPUP,
+            0, 0,
+            width, height,
+            0,
+            0,
+            OS.GetModuleHandle (null),
+            null);
+        oldProc = OS.GetWindowLong (hwndTransparent, OS.GWL_WNDPROC);
+        newProc = new Callback (this, "transparentProc", 4); //$NON-NLS-1$
+        int newProcAddress = newProc.getAddress ();
+        if (newProcAddress is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS);
+        OS.SetWindowLong (hwndTransparent, OS.GWL_WNDPROC, newProcAddress);
+        if (isVista) {
+            OS.SetLayeredWindowAttributes (hwndTransparent, 0xFFFFFF, (byte)0xFF, OS.LWA_COLORKEY | OS.LWA_ALPHA);
+        }
+        OS.ShowWindow (hwndTransparent, OS.SW_SHOWNOACTIVATE);
+    }
+
+    update ();
+    drawRectangles (rectangles, stippled);
+    Point cursorPos = null;
+    if (mouseDown) {
+        POINT pt = new POINT ();
+        OS.GetCursorPos (pt);
+        cursorPos = new Point (pt.x, pt.y);
+    } else {
+        if ((style & DWT.RESIZE) !is 0) {
+            cursorPos = adjustResizeCursor ();
+        } else {
+            cursorPos = adjustMoveCursor ();
+        }
+    }
+    if (cursorPos !is null) {
+        oldX = cursorPos.x;
+        oldY = cursorPos.y;
+    }
+
+    try {
+        /* Tracker behaves like a Dialog with its own OS event loop. */
+        MSG msg = new MSG ();
+        while (tracking && !cancelled) {
+            if (parent !is null && parent.isDisposed ()) break;
+            OS.GetMessage (msg, 0, 0, 0);
+            OS.TranslateMessage (msg);
+            switch (msg.message) {
+                case OS.WM_LBUTTONUP:
+                case OS.WM_MOUSEMOVE:
+                    wmMouse (msg.message, msg.wParam, msg.lParam);
+                    break;
+                case OS.WM_IME_CHAR: wmIMEChar (msg.hwnd, msg.wParam, msg.lParam); break;
+                case OS.WM_CHAR: wmChar (msg.hwnd, msg.wParam, msg.lParam); break;
+                case OS.WM_KEYDOWN: wmKeyDown (msg.hwnd, msg.wParam, msg.lParam); break;
+                case OS.WM_KEYUP: wmKeyUp (msg.hwnd, msg.wParam, msg.lParam); break;
+                case OS.WM_SYSCHAR: wmSysChar (msg.hwnd, msg.wParam, msg.lParam); break;
+                case OS.WM_SYSKEYDOWN: wmSysKeyDown (msg.hwnd, msg.wParam, msg.lParam); break;
+                case OS.WM_SYSKEYUP: wmSysKeyUp (msg.hwnd, msg.wParam, msg.lParam); break;
+            }
+            if (OS.WM_KEYFIRST <= msg.message && msg.message <= OS.WM_KEYLAST) continue;
+            if (OS.WM_MOUSEFIRST <= msg.message && msg.message <= OS.WM_MOUSELAST) continue;
+            if (!(parent is null && isVista)) {
+                if (msg.message is OS.WM_PAINT) {
+                    update ();
+                    drawRectangles (rectangles, stippled);
+                }
+            }
+            OS.DispatchMessage (msg);
+            if (!(parent is null && isVista)) {
+                if (msg.message is OS.WM_PAINT) {
+                    drawRectangles (rectangles, stippled);
+                }
+            }
+        }
+        if (mouseDown) OS.ReleaseCapture ();
+        if (!isDisposed()) {
+            update ();
+            drawRectangles (rectangles, stippled);
+        }
+    } finally {
+        /*
+        * Cleanup: If a transparent window was created in order to capture events then
+        * destroy it and its callback object now.
+        */
+        if (hwndTransparent !is 0) {
+            OS.DestroyWindow (hwndTransparent);
+            hwndTransparent = 0;
+        }
+        if (newProc !is null) {
+            newProc.dispose ();
+            oldProc = 0;
+        }
+        /*
+        * Cleanup: If this tracker was resizing then the last cursor that it created
+        * needs to be destroyed.
+        */
+        if (resizeCursor !is 0) {
+            OS.DestroyCursor (resizeCursor);
+            resizeCursor = 0;
+        }
+    }
+    tracking = false;
+    return !cancelled;
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    parent = null;
+    rectangles = proportions = null;
+    bounds = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is moved or resized.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #addControlListener
+ */
+public void removeControlListener (ControlListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Resize, listener);
+    eventTable.unhook (DWT.Move, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when keys are pressed and released on the system keyboard.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see KeyListener
+ * @see #addKeyListener
+ */
+public void removeKeyListener(KeyListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.KeyUp, listener);
+    eventTable.unhook (DWT.KeyDown, listener);
+}
+
+void resizeRectangles (int xChange, int yChange) {
+    if (bounds is null) return;
+    /*
+    * If the cursor orientation has not been set in the orientation of
+    * this change then try to set it here.
+    */
+    if (xChange < 0 && ((style & DWT.LEFT) !is 0) && ((cursorOrientation & DWT.RIGHT) is 0)) {
+        cursorOrientation |= DWT.LEFT;
+    }
+    if (xChange > 0 && ((style & DWT.RIGHT) !is 0) && ((cursorOrientation & DWT.LEFT) is 0)) {
+        cursorOrientation |= DWT.RIGHT;
+    }
+    if (yChange < 0 && ((style & DWT.UP) !is 0) && ((cursorOrientation & DWT.DOWN) is 0)) {
+        cursorOrientation |= DWT.UP;
+    }
+    if (yChange > 0 && ((style & DWT.DOWN) !is 0) && ((cursorOrientation & DWT.UP) is 0)) {
+        cursorOrientation |= DWT.DOWN;
+    }
+
+    /*
+     * If the bounds will flip about the x or y axis then apply the adjustment
+     * up to the axis (ie.- where bounds width/height becomes 0), change the
+     * cursor's orientation accordingly, and flip each Rectangle's origin (only
+     * necessary for > 1 Rectangles)
+     */
+    if ((cursorOrientation & DWT.LEFT) !is 0) {
+        if (xChange > bounds.width) {
+            if ((style & DWT.RIGHT) is 0) return;
+            cursorOrientation |= DWT.RIGHT;
+            cursorOrientation &= ~DWT.LEFT;
+            bounds.x += bounds.width;
+            xChange -= bounds.width;
+            bounds.width = 0;
+            if (proportions.length > 1) {
+                for (int i = 0; i < proportions.length; i++) {
+                    Rectangle proportion = proportions [i];
+                    proportion.x = 100 - proportion.x - proportion.width;
+                }
+            }
+        }
+    } else if ((cursorOrientation & DWT.RIGHT) !is 0) {
+        if (bounds.width < -xChange) {
+            if ((style & DWT.LEFT) is 0) return;
+            cursorOrientation |= DWT.LEFT;
+            cursorOrientation &= ~DWT.RIGHT;
+            xChange += bounds.width;
+            bounds.width = 0;
+            if (proportions.length > 1) {
+                for (int i = 0; i < proportions.length; i++) {
+                    Rectangle proportion = proportions [i];
+                    proportion.x = 100 - proportion.x - proportion.width;
+                }
+            }
+        }
+    }
+    if ((cursorOrientation & DWT.UP) !is 0) {
+        if (yChange > bounds.height) {
+            if ((style & DWT.DOWN) is 0) return;
+            cursorOrientation |= DWT.DOWN;
+            cursorOrientation &= ~DWT.UP;
+            bounds.y += bounds.height;
+            yChange -= bounds.height;
+            bounds.height = 0;
+            if (proportions.length > 1) {
+                for (int i = 0; i < proportions.length; i++) {
+                    Rectangle proportion = proportions [i];
+                    proportion.y = 100 - proportion.y - proportion.height;
+                }
+            }
+        }
+    } else if ((cursorOrientation & DWT.DOWN) !is 0) {
+        if (bounds.height < -yChange) {
+            if ((style & DWT.UP) is 0) return;
+            cursorOrientation |= DWT.UP;
+            cursorOrientation &= ~DWT.DOWN;
+            yChange += bounds.height;
+            bounds.height = 0;
+            if (proportions.length > 1) {
+                for (int i = 0; i < proportions.length; i++) {
+                    Rectangle proportion = proportions [i];
+                    proportion.y = 100 - proportion.y - proportion.height;
+                }
+            }
+        }
+    }
+
+    // apply the bounds adjustment
+    if ((cursorOrientation & DWT.LEFT) !is 0) {
+        bounds.x += xChange;
+        bounds.width -= xChange;
+    } else if ((cursorOrientation & DWT.RIGHT) !is 0) {
+        bounds.width += xChange;
+    }
+    if ((cursorOrientation & DWT.UP) !is 0) {
+        bounds.y += yChange;
+        bounds.height -= yChange;
+    } else if ((cursorOrientation & DWT.DOWN) !is 0) {
+        bounds.height += yChange;
+    }
+
+    Rectangle [] newRects = new Rectangle [rectangles.length];
+    for (int i = 0; i < rectangles.length; i++) {
+        Rectangle proportion = proportions[i];
+        newRects[i] = new Rectangle (
+            proportion.x * bounds.width / 100 + bounds.x,
+            proportion.y * bounds.height / 100 + bounds.y,
+            proportion.width * bounds.width / 100,
+            proportion.height * bounds.height / 100);
+    }
+    rectangles = newRects;
+}
+
+/**
+ * Sets the <code>Cursor</code> of the Tracker.  If this cursor is <code>null</code>
+ * then the cursor reverts to the default.
+ *
+ * @param newCursor the new <code>Cursor</code> to display
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setCursor(Cursor newCursor) {
+    checkWidget();
+    clientCursor = 0;
+    if (newCursor !is null) {
+        clientCursor = newCursor.handle;
+        if (inEvent) OS.SetCursor (clientCursor);
+    }
+}
+
+/**
+ * Specifies the rectangles that should be drawn, expressed relative to the parent
+ * widget.  If the parent is a Display then these are screen coordinates.
+ *
+ * @param rectangles the bounds of the rectangles to be drawn
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the set of rectangles is null or contains a null rectangle</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setRectangles (Rectangle [] rectangles) {
+    checkWidget ();
+    if (rectangles is null) error (DWT.ERROR_NULL_ARGUMENT);
+    this.rectangles = new Rectangle [rectangles.length];
+    for (int i = 0; i < rectangles.length; i++) {
+        Rectangle current = rectangles [i];
+        if (current is null) error (DWT.ERROR_NULL_ARGUMENT);
+        this.rectangles [i] = new Rectangle (current.x, current.y, current.width, current.height);
+    }
+    proportions = computeProportions (rectangles);
+}
+
+/**
+ * Changes the appearance of the line used to draw the rectangles.
+ *
+ * @param stippled <code>true</code> if rectangle should appear stippled
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setStippled (bool stippled) {
+    checkWidget ();
+    this.stippled = stippled;
+}
+
+int transparentProc (int hwnd, int msg, int wParam, int lParam) {
+    switch (msg) {
+        /*
+        * We typically do not want to answer that the transparent window is
+        * transparent to hits since doing so negates the effect of having it
+        * to grab events.  However, clients of the tracker should not be aware
+        * of this transparent window.  Therefore if there is a hit query
+        * performed as a result of client code then answer that the transparent
+        * window is transparent to hits so that its existence will not impact
+        * the client.
+        */
+        case OS.WM_NCHITTEST:
+            if (inEvent) return OS.HTTRANSPARENT;
+            break;
+        case OS.WM_SETCURSOR:
+            if (clientCursor !is 0) {
+                OS.SetCursor (clientCursor);
+                return 1;
+            }
+            if (resizeCursor !is 0) {
+                OS.SetCursor (resizeCursor);
+                return 1;
+            }
+            break;
+        case OS.WM_PAINT:
+            if (parent is null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                PAINTSTRUCT ps = new PAINTSTRUCT();
+                int hDC = OS.BeginPaint (hwnd, ps);
+                int hBitmap = 0, hBrush = 0, oldBrush = 0;
+                int transparentBrush = OS.CreateSolidBrush(0xFFFFFF);
+                oldBrush = OS.SelectObject (hDC, transparentBrush);
+                OS.PatBlt (hDC, ps.left, ps.top, ps.right - ps.left, ps.bottom - ps.top, OS.PATCOPY);
+                OS.SelectObject (hDC, oldBrush);
+                OS.DeleteObject (transparentBrush);
+                int bandWidth = 1;
+                if (stippled) {
+                    bandWidth = 3;
+                    byte [] bits = {-86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0, -86, 0, 85, 0};
+                    hBitmap = OS.CreateBitmap (8, 8, 1, 1, bits);
+                    hBrush = OS.CreatePatternBrush (hBitmap);
+                    oldBrush = OS.SelectObject (hDC, hBrush);
+                    OS.SetBkColor (hDC, 0xF0F0F0);
+                } else {
+                    oldBrush = OS.SelectObject (hDC, OS.GetStockObject(OS.BLACK_BRUSH));
+                }
+                Rectangle[] rects = this.rectangles;
+                for (int i=0; i<rects.length; i++) {
+                    Rectangle rect = rects [i];
+                    OS.PatBlt (hDC, rect.x, rect.y, rect.width, bandWidth, OS.PATCOPY);
+                    OS.PatBlt (hDC, rect.x, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATCOPY);
+                    OS.PatBlt (hDC, rect.x + rect.width - bandWidth, rect.y + bandWidth, bandWidth, rect.height - (bandWidth * 2), OS.PATCOPY);
+                    OS.PatBlt (hDC, rect.x, rect.y + rect.height - bandWidth, rect.width, bandWidth, OS.PATCOPY);
+                }
+                OS.SelectObject (hDC, oldBrush);
+                if (stippled) {
+                    OS.DeleteObject (hBrush);
+                    OS.DeleteObject (hBitmap);
+                }
+                OS.EndPaint (hwnd, ps);
+                return 0;
+            }
+    }
+    return OS.CallWindowProc (oldProc, hwnd, msg, wParam, lParam);
+}
+
+void update () {
+    if (parent is null && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) return;
+    if (parent !is null) {
+        if (parent.isDisposed ()) return;
+        Shell shell = parent.getShell ();
+        shell.update (true);
+    } else {
+        display.update ();
+    }
+}
+
+LRESULT wmKeyDown (int hwnd, int wParam, int lParam) {
+    LRESULT result = super.wmKeyDown (hwnd, wParam, lParam);
+    if (result !is null) return result;
+    bool isMirrored = parent !is null && (parent.style & DWT.MIRRORED) !is 0;
+    int stepSize = OS.GetKeyState (OS.VK_CONTROL) < 0 ? STEPSIZE_SMALL : STEPSIZE_LARGE;
+    int xChange = 0, yChange = 0;
+    switch (wParam) {
+        case OS.VK_ESCAPE:
+            cancelled = true;
+            tracking = false;
+            break;
+        case OS.VK_RETURN:
+            tracking = false;
+            break;
+        case OS.VK_LEFT:
+            xChange = isMirrored ? stepSize : -stepSize;
+            break;
+        case OS.VK_RIGHT:
+            xChange = isMirrored ? -stepSize : stepSize;
+            break;
+        case OS.VK_UP:
+            yChange = -stepSize;
+            break;
+        case OS.VK_DOWN:
+            yChange = stepSize;
+            break;
+    }
+    if (xChange !is 0 || yChange !is 0) {
+        Rectangle [] oldRectangles = rectangles;
+        bool oldStippled = stippled;
+        Rectangle [] rectsToErase = new Rectangle [rectangles.length];
+        for (int i = 0; i < rectangles.length; i++) {
+            Rectangle current = rectangles [i];
+            rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height);
+        }
+        Event event = new Event ();
+        event.x = oldX + xChange;
+        event.y = oldY + yChange;
+        Point cursorPos;
+        if ((style & DWT.RESIZE) !is 0) {
+            resizeRectangles (xChange, yChange);
+            inEvent = true;
+            sendEvent (DWT.Resize, event);
+            inEvent = false;
+            /*
+            * It is possible (but unlikely) that application
+            * code could have disposed the widget in the resize
+            * event.  If this happens return false to indicate
+            * that the tracking has failed.
+            */
+            if (isDisposed ()) {
+                cancelled = true;
+                return LRESULT.ONE;
+            }
+            bool draw = false;
+            /*
+             * It is possible that application code could have
+             * changed the rectangles in the resize event.  If this
+             * happens then only redraw the tracker if the rectangle
+             * values have changed.
+             */
+            if (rectangles !is oldRectangles) {
+                int length = rectangles.length;
+                if (length !is rectsToErase.length) {
+                    draw = true;
+                } else {
+                    for (int i = 0; i < length; i++) {
+                        if (!rectangles [i].equals (rectsToErase [i])) {
+                            draw = true;
+                            break;
+                        }
+                    }
+                }
+            } else {
+                draw = true;
+            }
+            if (draw) {
+                drawRectangles (rectsToErase, oldStippled);
+                update ();
+                drawRectangles (rectangles, stippled);
+            }
+            cursorPos = adjustResizeCursor ();
+        } else {
+            moveRectangles (xChange, yChange);
+            inEvent = true;
+            sendEvent (DWT.Move, event);
+            inEvent = false;
+            /*
+            * It is possible (but unlikely) that application
+            * code could have disposed the widget in the move
+            * event.  If this happens return false to indicate
+            * that the tracking has failed.
+            */
+            if (isDisposed ()) {
+                cancelled = true;
+                return LRESULT.ONE;
+            }
+            bool draw = false;
+            /*
+             * It is possible that application code could have
+             * changed the rectangles in the move event.  If this
+             * happens then only redraw the tracker if the rectangle
+             * values have changed.
+             */
+            if (rectangles !is oldRectangles) {
+                int length = rectangles.length;
+                if (length !is rectsToErase.length) {
+                    draw = true;
+                } else {
+                    for (int i = 0; i < length; i++) {
+                        if (!rectangles [i].equals (rectsToErase [i])) {
+                            draw = true;
+                            break;
+                        }
+                    }
+                }
+            } else {
+                draw = true;
+            }
+            if (draw) {
+                drawRectangles (rectsToErase, oldStippled);
+                update ();
+                drawRectangles (rectangles, stippled);
+            }
+            cursorPos = adjustMoveCursor ();
+        }
+        if (cursorPos !is null) {
+            oldX = cursorPos.x;
+            oldY = cursorPos.y;
+        }
+    }
+    return result;
+}
+
+LRESULT wmSysKeyDown (int hwnd, int wParam, int lParam) {
+    LRESULT result = super.wmSysKeyDown (hwnd, wParam, lParam);
+    if (result !is null) return result;
+    cancelled = true;
+    tracking = false;
+    return result;
+}
+
+LRESULT wmMouse (int message, int wParam, int lParam) {
+    bool isMirrored = parent !is null && (parent.style & DWT.MIRRORED) !is 0;
+    int newPos = OS.GetMessagePos ();
+    int newX = (short) (newPos & 0xFFFF);
+    int newY = (short) (newPos >> 16);
+    if (newX !is oldX || newY !is oldY) {
+        Rectangle [] oldRectangles = rectangles;
+        bool oldStippled = stippled;
+        Rectangle [] rectsToErase = new Rectangle [rectangles.length];
+        for (int i = 0; i < rectangles.length; i++) {
+            Rectangle current = rectangles [i];
+            rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height);
+        }
+        Event event = new Event ();
+        event.x = newX;
+        event.y = newY;
+        if ((style & DWT.RESIZE) !is 0) {
+            if (isMirrored) {
+               resizeRectangles (oldX - newX, newY - oldY);
+            } else {
+               resizeRectangles (newX - oldX, newY - oldY);
+            }
+            inEvent = true;
+            sendEvent (DWT.Resize, event);
+            inEvent = false;
+            /*
+            * It is possible (but unlikely), that application
+            * code could have disposed the widget in the resize
+            * event.  If this happens, return false to indicate
+            * that the tracking has failed.
+            */
+            if (isDisposed ()) {
+                cancelled = true;
+                return LRESULT.ONE;
+            }
+            bool draw = false;
+            /*
+             * It is possible that application code could have
+             * changed the rectangles in the resize event.  If this
+             * happens then only redraw the tracker if the rectangle
+             * values have changed.
+             */
+            if (rectangles !is oldRectangles) {
+                int length = rectangles.length;
+                if (length !is rectsToErase.length) {
+                    draw = true;
+                } else {
+                    for (int i = 0; i < length; i++) {
+                        if (!rectangles [i].equals (rectsToErase [i])) {
+                            draw = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            else {
+                draw = true;
+            }
+            if (draw) {
+                drawRectangles (rectsToErase, oldStippled);
+                update ();
+                drawRectangles (rectangles, stippled);
+            }
+            Point cursorPos = adjustResizeCursor ();
+            if (cursorPos !is null) {
+                newX = cursorPos.x;
+                newY = cursorPos.y;
+            }
+        } else {
+            if (isMirrored) {
+                moveRectangles (oldX - newX, newY - oldY);
+            } else {
+                moveRectangles (newX - oldX, newY - oldY);
+            }
+            inEvent = true;
+            sendEvent (DWT.Move, event);
+            inEvent = false;
+            /*
+            * It is possible (but unlikely), that application
+            * code could have disposed the widget in the move
+            * event.  If this happens, return false to indicate
+            * that the tracking has failed.
+            */
+            if (isDisposed ()) {
+                cancelled = true;
+                return LRESULT.ONE;
+            }
+            bool draw = false;
+            /*
+             * It is possible that application code could have
+             * changed the rectangles in the move event.  If this
+             * happens then only redraw the tracker if the rectangle
+             * values have changed.
+             */
+            if (rectangles !is oldRectangles) {
+                int length = rectangles.length;
+                if (length !is rectsToErase.length) {
+                    draw = true;
+                } else {
+                    for (int i = 0; i < length; i++) {
+                        if (!rectangles [i].equals (rectsToErase [i])) {
+                            draw = true;
+                            break;
+                        }
+                    }
+                }
+            } else {
+                draw = true;
+            }
+            if (draw) {
+                drawRectangles (rectsToErase, oldStippled);
+                update ();
+                drawRectangles (rectangles, stippled);
+            }
+        }
+        oldX = newX;
+        oldY = newY;
+    }
+    tracking = message !is OS.WM_LBUTTONUP;
+    return null;
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Tray.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.Tray;
+
+import dwt.widgets.Display;
+import dwt.widgets.Widget;
+import dwt.widgets.TrayItem;
+import dwt.DWT;
+import dwt.dwthelper.System;
+
+/**
+ * Instances of this class represent the system tray that is part
+ * of the task bar status area on some operating systems.
+ *
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see Display#getSystemTray
+ *
+ * @since 3.0
+ */
+public class Tray : Widget {
+    int itemCount;
+    TrayItem [] items;
+
+this (Display display, int style) {
+    items = new TrayItem [4];
+    if (display is null) display = Display.getCurrent ();
+    if (display is null) display = Display.getDefault ();
+    if (!display.isValidThread ()) {
+        error (DWT.ERROR_THREAD_INVALID_ACCESS);
+    }
+    this.display = display;
+}
+
+void createItem (TrayItem item, int index) {
+    if (!(0 <= index && index <= itemCount)) error (DWT.ERROR_INVALID_RANGE);
+    if (itemCount is items.length) {
+        TrayItem [] newItems = new TrayItem [items.length + 4];
+        System.arraycopy (items, 0, newItems, 0, items.length);
+        items = newItems;
+    }
+    System.arraycopy (items, index, items, index + 1, itemCount++ - index);
+    items [index] = item;
+}
+
+void destroyItem (TrayItem item) {
+    int index = 0;
+    while (index < itemCount) {
+        if (items [index] is item) break;
+        index++;
+    }
+    if (index is itemCount) return;
+    System.arraycopy (items, index + 1, items, index, --itemCount - index);
+    items [itemCount] = null;
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TrayItem getItem (int index) {
+    checkWidget ();
+    if (!(0 <= index && index < itemCount)) error (DWT.ERROR_INVALID_RANGE);
+    return items [index];
+}
+
+/**
+ * Returns the number of items contained in the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    return itemCount;
+}
+
+/**
+ * Returns an array of <code>TrayItem</code>s which are the items
+ * in the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TrayItem [] getItems () {
+    checkWidget ();
+    TrayItem [] result = new TrayItem [itemCount];
+    System.arraycopy (items, 0, result, 0, result.length);
+    return result;
+}
+
+void releaseChildren (bool destroy) {
+    if (items !is null) {
+        for (int i=0; i<items.length; i++) {
+            TrayItem item = items [i];
+            if (item !is null && !item.isDisposed ()) {
+                item.release (false);
+            }
+        }
+        items = null;
+    }
+    super.releaseChildren (destroy);
+}
+
+void releaseParent () {
+    super.releaseParent ();
+    if (display.tray is this) display.tray = null;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/TrayItem.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,541 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.TrayItem;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+class TrayItem : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.MenuDetectListener;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Image;
+import dwt.internal.win32.NOTIFYICONDATA;
+import dwt.internal.win32.NOTIFYICONDATAA;
+import dwt.internal.win32.NOTIFYICONDATAW;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.TCHAR;
+
+/**
+ * Instances of this class represent icons that can be placed on the
+ * system tray or task bar status area.
+ * <p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>DefaultSelection, MenuDetect, Selection</dd>
+ * </dl>
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class TrayItem extends Item {
+    Tray parent;
+    int id;
+    Image image2;
+    ToolTip toolTip;
+    String toolTipText;
+    bool visible = true;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tray</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TrayItem (Tray parent, int style) {
+    super (parent, style);
+    this.parent = parent;
+    parent.createItem (this, parent.getItemCount ());
+    createWidget ();
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the receiver is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the receiver is selected
+ * <code>widgetDefaultSelected</code> is called when the receiver is double-clicked
+ * </p>
+ *
+ * @param listener the listener which should be notified when the receiver is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the platform-specific context menu trigger
+ * has occurred, by sending it one of the messages defined in
+ * the <code>MenuDetectListener</code> interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MenuDetectListener
+ * @see #removeMenuDetectListener
+ *
+ * @since 3.3
+ */
+public void addMenuDetectListener (MenuDetectListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.MenuDetect, typedListener);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+void createWidget () {
+    NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA ();
+    iconData.cbSize = NOTIFYICONDATA.sizeof;
+    iconData.uID = id = display.nextTrayId++;
+    iconData.hWnd = display.hwndMessage;
+    iconData.uFlags = OS.NIF_MESSAGE;
+    iconData.uCallbackMessage = Display.SWT_TRAYICONMSG;
+    OS.Shell_NotifyIcon (OS.NIM_ADD, iconData);
+}
+
+void destroyWidget () {
+    parent.destroyItem (this);
+    releaseHandle ();
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Tray</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public Tray getParent () {
+    checkWidget ();
+    return parent;
+}
+
+/**
+ * Returns the receiver's tool tip, or null if it has
+ * not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public ToolTip getToolTip () {
+    checkWidget ();
+    return toolTip;
+}
+
+/**
+ * Returns the receiver's tool tip text, or null if it has
+ * not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getToolTipText () {
+    checkWidget ();
+    return toolTipText;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is visible and
+ * <code>false</code> otherwise.
+ *
+ * @return the receiver's visibility
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getVisible () {
+    checkWidget ();
+    return visible;
+}
+
+int messageProc (int hwnd, int msg, int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When the user clicks on the tray
+    * icon, another application may be the foreground window.
+    * This means that the event loop is not running and can
+    * cause problems.  For example, if a menu is shown, when
+    * the user clicks outside of the menu to cancel it, the
+    * menu is not hidden until an event is processed.  If
+    * another application is the foreground window, then the
+    * menu is not hidden.  The fix is to force the tray icon
+    * message window to the foreground when sending an event.
+    */
+    switch (lParam) {
+        case OS.WM_LBUTTONDOWN:
+            if (hooks (DWT.Selection)) {
+                OS.SetForegroundWindow (hwnd);
+                postEvent (DWT.Selection);
+            }
+            break;
+        case OS.WM_LBUTTONDBLCLK:
+        case OS.WM_RBUTTONDBLCLK:
+            if (hooks (DWT.DefaultSelection)) {
+                OS.SetForegroundWindow (hwnd);
+                postEvent (DWT.DefaultSelection);
+            }
+            break;
+        case OS.WM_RBUTTONUP: {
+            if (hooks (DWT.MenuDetect)) {
+                OS.SetForegroundWindow (hwnd);
+                sendEvent (DWT.MenuDetect);
+                // widget could be disposed at this point
+                if (isDisposed()) return 0;
+            }
+            break;
+        }
+        case OS.NIN_BALLOONSHOW:
+            if (toolTip !is null && !toolTip.visible) {
+                toolTip.visible = true;
+                if (toolTip.hooks (DWT.Show)) {
+                    OS.SetForegroundWindow (hwnd);
+                    toolTip.sendEvent (DWT.Show);
+                    // widget could be disposed at this point
+                    if (isDisposed()) return 0;
+                }
+            }
+            break;
+        case OS.NIN_BALLOONHIDE:
+        case OS.NIN_BALLOONTIMEOUT:
+        case OS.NIN_BALLOONUSERCLICK:
+            if (toolTip !is null) {
+                if (toolTip.visible) {
+                    toolTip.visible = false;
+                    if (toolTip.hooks (DWT.Hide)) {
+                        OS.SetForegroundWindow (hwnd);
+                        toolTip.sendEvent (DWT.Hide);
+                        // widget could be disposed at this point
+                        if (isDisposed()) return 0;
+                    }
+                }
+                if (lParam is OS.NIN_BALLOONUSERCLICK) {
+                    if (toolTip.hooks (DWT.Selection)) {
+                        OS.SetForegroundWindow (hwnd);
+                        toolTip.postEvent (DWT.Selection);
+                        // widget could be disposed at this point
+                        if (isDisposed()) return 0;
+                    }
+                }
+            }
+            break;
+    }
+    display.wakeThread ();
+    return 0;
+}
+
+void recreate () {
+    createWidget ();
+    if (!visible) setVisible (false);
+    if (text.length () !is 0) setText (text);
+    if (image !is null) setImage (image);
+    if (toolTipText !is null) setToolTipText (toolTipText);
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    if (toolTip !is null) toolTip.item = null;
+    toolTip = null;
+    if (image2 !is null) image2.dispose ();
+    image2 = null;
+    toolTipText = null;
+    NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA ();
+    iconData.cbSize = NOTIFYICONDATA.sizeof;
+    iconData.uID = id;
+    iconData.hWnd = display.hwndMessage;
+    OS.Shell_NotifyIcon (OS.NIM_DELETE, iconData);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the receiver is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the platform-specific context menu trigger has
+ * occurred.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see MenuDetectListener
+ * @see #addMenuDetectListener
+ *
+ * @since 3.3
+ */
+public void removeMenuDetectListener (MenuDetectListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.MenuDetect, listener);
+}
+
+/**
+ * Sets the receiver's image.
+ *
+ * @param image the new image
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (Image image) {
+    checkWidget ();
+    if (image !is null && image.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+    super.setImage (image);
+    if (image2 !is null) image2.dispose ();
+    image2 = null;
+    int hIcon = 0;
+    Image icon = image;
+    if (icon !is null) {
+        switch (icon.type) {
+            case DWT.BITMAP:
+                image2 = Display.createIcon (image);
+                hIcon = image2.handle;
+                break;
+            case DWT.ICON:
+                hIcon = icon.handle;
+                break;
+        }
+    }
+    NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA ();
+    iconData.cbSize = NOTIFYICONDATA.sizeof;
+    iconData.uID = id;
+    iconData.hWnd = display.hwndMessage;
+    iconData.hIcon = hIcon;
+    iconData.uFlags = OS.NIF_ICON;
+    OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData);
+}
+
+/**
+ * Sets the receiver's tool tip to the argument, which
+ * may be null indicating that no tool tip should be shown.
+ *
+ * @param toolTip the new tool tip (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setToolTip (ToolTip toolTip) {
+    checkWidget ();
+    ToolTip oldTip = this.toolTip, newTip = toolTip;
+    if (oldTip !is null) oldTip.item = null;
+    this.toolTip = newTip;
+    if (newTip !is null) newTip.item = this;
+}
+
+/**
+ * Sets the receiver's tool tip text to the argument, which
+ * may be null indicating that no tool tip text should be shown.
+ *
+ * @param value the new tool tip text (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setToolTipText (String value) {
+    checkWidget ();
+    toolTipText = value;
+    NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA ();
+    TCHAR buffer = new TCHAR (0, toolTipText is null ? "" : toolTipText, true);
+    /*
+    * Note that the size of the szTip field is different in version 5.0 of shell32.dll.
+    */
+    int length = OS.SHELL32_MAJOR < 5 ? 64 : 128;
+    if (OS.IsUnicode) {
+        char [] szTip = ((NOTIFYICONDATAW) iconData).szTip;
+        length = Math.min (length - 1, buffer.length ());
+        System.arraycopy (buffer.chars, 0, szTip, 0, length);
+    } else {
+        byte [] szTip = ((NOTIFYICONDATAA) iconData).szTip;
+        length = Math.min (length - 1, buffer.length ());
+        System.arraycopy (buffer.bytes, 0, szTip, 0, length);
+    }
+    iconData.cbSize = NOTIFYICONDATA.sizeof;
+    iconData.uID = id;
+    iconData.hWnd = display.hwndMessage;
+    iconData.uFlags = OS.NIF_TIP;
+    OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData);
+}
+
+/**
+ * Makes the receiver visible if the argument is <code>true</code>,
+ * and makes it invisible otherwise.
+ *
+ * @param visible the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setVisible (bool visible) {
+    checkWidget ();
+    if (this.visible is visible) return;
+    if (visible) {
+        /*
+        * It is possible (but unlikely), that application
+        * code could have disposed the widget in the show
+        * event.  If this happens, just return.
+        */
+        sendEvent (DWT.Show);
+        if (isDisposed ()) return;
+    }
+    this.visible = visible;
+    NOTIFYICONDATA iconData = OS.IsUnicode ? (NOTIFYICONDATA) new NOTIFYICONDATAW () : new NOTIFYICONDATAA ();
+    iconData.cbSize = NOTIFYICONDATA.sizeof;
+    iconData.uID = id;
+    iconData.hWnd = display.hwndMessage;
+    if (OS.SHELL32_MAJOR < 5) {
+        if (visible) {
+            iconData.uFlags = OS.NIF_MESSAGE;
+            iconData.uCallbackMessage = Display.SWT_TRAYICONMSG;
+            OS.Shell_NotifyIcon (OS.NIM_ADD, iconData);
+            setImage (image);
+            setToolTipText (toolTipText);
+        } else {
+            OS.Shell_NotifyIcon (OS.NIM_DELETE, iconData);
+        }
+    } else {
+        iconData.uFlags = OS.NIF_STATE;
+        iconData.dwState = visible ? 0 : OS.NIS_HIDDEN;
+        iconData.dwStateMask = OS.NIS_HIDDEN;
+        OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData);
+    }
+    if (!visible) sendEvent (DWT.Hide);
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Tree.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,7209 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Tree;
+
+import dwt.widgets.Composite;
+class Tree : Composite {
+}
+/++
+
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.events.TreeListener;
+import dwt.graphics.Cursor;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.Callback;
+import dwt.internal.ImageList;
+import dwt.internal.win32.HDHITTESTINFO;
+import dwt.internal.win32.HDITEM;
+import dwt.internal.win32.HDLAYOUT;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.NMHDR;
+import dwt.internal.win32.NMHEADER;
+import dwt.internal.win32.NMRGINFO;
+import dwt.internal.win32.NMTTDISPINFO;
+import dwt.internal.win32.NMTVCUSTOMDRAW;
+import dwt.internal.win32.NMTVDISPINFO;
+import dwt.internal.win32.NMTVITEMCHANGE;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.PAINTSTRUCT;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SCROLLINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TEXTMETRIC;
+import dwt.internal.win32.TEXTMETRICA;
+import dwt.internal.win32.TEXTMETRICW;
+import dwt.internal.win32.TOOLINFO;
+import dwt.internal.win32.TVHITTESTINFO;
+import dwt.internal.win32.TVINSERTSTRUCT;
+import dwt.internal.win32.TVITEM;
+import dwt.internal.win32.TVSORTCB;
+import dwt.internal.win32.WINDOWPOS;
+import dwt.internal.win32.WNDCLASS;
+
+/**
+ * Instances of this class provide a selectable user interface object
+ * that displays a hierarchy of items and issues notification when an
+ * item in the hierarchy is selected.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>TreeItem</code>.
+ * </p><p>
+ * Style <code>VIRTUAL</code> is used to create a <code>Tree</code> whose
+ * <code>TreeItem</code>s are to be populated by the client on an on-demand basis
+ * instead of up-front.  This can provide significant performance improvements for
+ * trees that are very large or for which <code>TreeItem</code> population is
+ * expensive (for example, retrieving values from an external source).
+ * </p><p>
+ * Here is an example of using a <code>Tree</code> with style <code>VIRTUAL</code>:
+ * <code><pre>
+ *  final Tree tree = new Tree(parent, DWT.VIRTUAL | DWT.BORDER);
+ *  tree.setItemCount(20);
+ *  tree.addListener(DWT.SetData, new Listener() {
+ *      public void handleEvent(Event event) {
+ *          TreeItem item = (TreeItem)event.item;
+ *          TreeItem parentItem = item.getParentItem();
+ *          String text = null;
+ *          if (parentItem is null) {
+ *              text = "node " + tree.indexOf(item);
+ *          } else {
+ *              text = parentItem.getText() + " - " + parentItem.indexOf(item);
+ *          }
+ *          item.setText(text);
+ *          System.out.println(text);
+ *          item.setItemCount(10);
+ *      }
+ *  });
+ * </pre></code>
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not make sense to add <code>Control</code> children to it,
+ * or set a layout on it.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, VIRTUAL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection, DefaultSelection, Collapse, Expand, SetData, MeasureItem, EraseItem, PaintItem</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles SINGLE and MULTI may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+public class Tree extends Composite {
+    TreeItem [] items;
+    TreeColumn [] columns;
+    ImageList imageList, headerImageList;
+    TreeItem currentItem;
+    TreeColumn sortColumn;
+    int hwndParent, hwndHeader, hAnchor, hInsert, lastID, hSelect;
+    int hFirstIndexOf, hLastIndexOf, lastIndexOf, itemCount, sortDirection;
+    bool dragStarted, gestureCompleted, insertAfter, shrink, ignoreShrink;
+    bool ignoreSelect, ignoreExpand, ignoreDeselect, ignoreResize;
+    bool lockSelection, oldSelected, newSelected, ignoreColumnMove;
+    bool linesVisible, customDraw, printClient, painted, ignoreItemHeight;
+    bool ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus;
+    bool ignoreDrawSelection, ignoreDrawHot, ignoreFullSelection, explorerTheme;
+    int scrollWidth, itemToolTipHandle, headerToolTipHandle, selectionForeground;
+    static final int INSET = 3;
+    static final int GRID_WIDTH = 1;
+    static final int SORT_WIDTH = 10;
+    static final int HEADER_MARGIN = 12;
+    static final int HEADER_EXTRA = 3;
+    static final int INCREMENT = 5;
+    static final int EXPLORER_EXTRA = 2;
+    static final bool EXPLORER_THEME = true;
+    static final int TreeProc;
+    static final TCHAR TreeClass = new TCHAR (0, OS.WC_TREEVIEW, true);
+    static final int HeaderProc;
+    static final TCHAR HeaderClass = new TCHAR (0, OS.WC_HEADER, true);
+    static {
+        WNDCLASS lpWndClass = new WNDCLASS ();
+        OS.GetClassInfo (0, TreeClass, lpWndClass);
+        TreeProc = lpWndClass.lpfnWndProc;
+        OS.GetClassInfo (0, HeaderClass, lpWndClass);
+        HeaderProc = lpWndClass.lpfnWndProc;
+    }
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#SINGLE
+ * @see DWT#MULTI
+ * @see DWT#CHECK
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Tree (Composite parent, int style) {
+    super (parent, checkStyle (style));
+}
+
+static int checkStyle (int style) {
+    /*
+    * Feature in Windows.  It is not possible to create
+    * a tree that scrolls and does not have scroll bars.
+    * The TVS_NOSCROLL style will remove the scroll bars
+    * but the tree will never scroll.  Therefore, no matter
+    * what style bits are specified, set the H_SCROLL and
+    * V_SCROLL bits so that the DWT style will match the
+    * widget that Windows creates.
+    */
+    style |= DWT.H_SCROLL | DWT.V_SCROLL;
+    return checkBits (style, DWT.SINGLE, DWT.MULTI, 0, 0, 0, 0);
+}
+
+void _addListener (int eventType, Listener listener) {
+    super._addListener (eventType, listener);
+    switch (eventType) {
+        case DWT.DragDetect: {
+            if ((state & DRAG_DETECT) !is 0) {
+                int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+                bits &= ~OS.TVS_DISABLEDRAGDROP;
+                OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+            }
+            break;
+        }
+        case DWT.MeasureItem:
+        case DWT.EraseItem:
+        case DWT.PaintItem: {
+            customDraw = true;
+            style |= DWT.DOUBLE_BUFFERED;
+            OS.SendMessage (handle, OS.TVM_SETSCROLLTIME, 0, 0);
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            bits |= OS.TVS_NOTOOLTIPS;
+            if (eventType is DWT.MeasureItem) bits |= OS.TVS_NOHSCROLL;
+            /*
+            * Feature in Windows.  When the tree has the style
+            * TVS_FULLROWSELECT, the background color for the
+            * entire row is filled when an item is painted,
+            * drawing on top of any custom drawing.  The fix
+            * is to clear TVS_FULLROWSELECT.
+            */
+            if ((style & DWT.FULL_SELECTION) !is 0) {
+                if (eventType !is DWT.MeasureItem) {
+                    if (!explorerTheme) bits &= ~OS.TVS_FULLROWSELECT;
+                }
+            }
+            if (bits !is OS.GetWindowLong (handle, OS.GWL_STYLE)) {
+                OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+                OS.InvalidateRect (handle, null, true);
+                /*
+                * Bug in Windows.  When TVS_NOHSCROLL is set after items
+                * have been inserted into the tree, Windows shows the
+                * scroll bar.  The fix is to check for this case and
+                * explicitly hide the scroll bar.
+                */
+                int count = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
+                if (count !is 0 && (bits & OS.TVS_NOHSCROLL) !is 0) {
+                    if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false);
+                }
+            }
+            break;
+        }
+    }
+}
+
+TreeItem _getItem (int hItem) {
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    tvItem.hItem = hItem;
+    if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) !is 0) {
+        return _getItem (tvItem.hItem, tvItem.lParam);
+    }
+    return null;
+}
+
+TreeItem _getItem (int hItem, int id) {
+    if ((style & DWT.VIRTUAL) is 0) return items [id];
+    return id !is -1 ? items [id] : new TreeItem (this, DWT.NONE, -1, -1, hItem);
+}
+
+void _setBackgroundPixel (int newPixel) {
+    int oldPixel = OS.SendMessage (handle, OS.TVM_GETBKCOLOR, 0, 0);
+    if (oldPixel !is newPixel) {
+        /*
+        * Bug in Windows.  When TVM_SETBKCOLOR is used more
+        * than once to set the background color of a tree,
+        * the background color of the lines and the plus/minus
+        * does not change to the new color.  The fix is to set
+        * the background color to the default before setting
+        * the new color.
+        */
+        if (oldPixel !is -1) {
+            OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, -1);
+        }
+
+        /* Set the background color */
+        OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, newPixel);
+
+        /*
+        * Feature in Windows.  When TVM_SETBKCOLOR is used to
+        * set the background color of a tree, the plus/minus
+        * animation draws badly.  The fix is to clear the effect.
+        */
+        if (explorerTheme) {
+            int bits2 = OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0);
+            if (newPixel is -1 && findImageControl () is null) {
+                bits2 |= OS.TVS_EX_FADEINOUTEXPANDOS;
+            } else {
+                bits2 &= ~OS.TVS_EX_FADEINOUTEXPANDOS;
+            }
+            OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, 0, bits2);
+        }
+
+        /* Set the checkbox image list */
+        if ((style & DWT.CHECK) !is 0) setCheckboxImageList ();
+    }
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's selection, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the item field of the event object is valid.
+ * If the receiver has the <code>DWT.CHECK</code> style and the check selection changes,
+ * the event object detail field contains the value <code>DWT.CHECK</code>.
+ * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
+ * The item field of the event object is valid for default selection, but the detail field is not used.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the user changes the receiver's selection
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection, typedListener);
+    addListener (DWT.DefaultSelection, typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when an item in the receiver is expanded or collapsed
+ * by sending it one of the messages defined in the <code>TreeListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see TreeListener
+ * @see #removeTreeListener
+ */
+public void addTreeListener(TreeListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Expand, typedListener);
+    addListener (DWT.Collapse, typedListener);
+}
+
+int borderHandle () {
+    return hwndParent !is 0 ? hwndParent : handle;
+}
+
+LRESULT CDDS_ITEMPOSTPAINT (NMTVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    if (ignoreCustomDraw) return null;
+    if (nmcd.left is nmcd.right) return new LRESULT (OS.CDRF_DODEFAULT);
+    int hDC = nmcd.hdc;
+    OS.RestoreDC (hDC, -1);
+    TreeItem item = getItem (nmcd);
+
+    /*
+    * Feature in Windows.  When a new tree item is inserted
+    * using TVM_INSERTITEM and the tree is using custom draw,
+    * a NM_CUSTOMDRAW is sent before TVM_INSERTITEM returns
+    * and before the item is added to the items array.  The
+    * fix is to check for null.
+    *
+    * NOTE: This only happens on XP with the version 6.00 of
+    * COMCTL32.DLL,
+    */
+    if (item is null) return null;
+
+    /*
+    * Feature in Windows.  Under certain circumstances, Windows
+    * sends CDDS_ITEMPOSTPAINT for an empty rectangle.  This is
+    * not a problem providing that graphics do not occur outside
+    * the rectangle.  The fix is to test for the rectangle and
+    * draw nothing.
+    *
+    * NOTE:  This seems to happen when both I_IMAGECALLBACK
+    * and LPSTR_TEXTCALLBACK are used at the same time with
+    * TVM_SETITEM.
+    */
+    if (nmcd.left >= nmcd.right || nmcd.top >= nmcd.bottom) return null;
+    if (!OS.IsWindowVisible (handle)) return null;
+    if ((style & DWT.FULL_SELECTION) !is 0 || findImageControl () !is null || ignoreDrawSelection || explorerTheme) {
+        OS.SetBkMode (hDC, OS.TRANSPARENT);
+    }
+    bool selected = isItemSelected (nmcd);
+    bool hot = explorerTheme && (nmcd.uItemState & OS.CDIS_HOT) !is 0;
+    if (OS.IsWindowEnabled (handle)) {
+        if (explorerTheme) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            if ((bits & OS.TVS_TRACKSELECT) !is 0) {
+                if ((style & DWT.FULL_SELECTION) !is 0 && (selected || hot)) {
+                    OS.SetTextColor (hDC, OS.GetSysColor (OS.COLOR_WINDOWTEXT));
+                } else {
+                    OS.SetTextColor (hDC, getForegroundPixel ());
+                }
+            }
+        }
+    }
+    int count = 0;
+    int [] order = null;
+    RECT clientRect = new RECT ();
+    OS.GetClientRect (scrolledHandle (), clientRect);
+    if (hwndHeader !is 0) {
+        OS.MapWindowPoints (hwndParent, handle, clientRect, 2);
+        count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+        if (count !is 0) {
+            order = new int [count];
+            OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
+        }
+    }
+    int sortIndex = -1, clrSortBk = -1;
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        if (sortColumn !is null && sortDirection !is DWT.NONE) {
+            if (findImageControl () is null) {
+                sortIndex = indexOf (sortColumn);
+                clrSortBk = getSortColumnPixel ();
+            }
+        }
+    }
+    int x = 0;
+    Point size = null;
+    for (int i=0; i<Math.max (1, count); i++) {
+        int index = order is null ? i : order [i], width = nmcd.right - nmcd.left;
+        if (count > 0 && hwndHeader !is 0) {
+            HDITEM hdItem = new HDITEM ();
+            hdItem.mask = OS.HDI_WIDTH;
+            OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+            width = hdItem.cxy;
+        }
+        if (i is 0) {
+            if ((style & DWT.FULL_SELECTION) !is 0) {
+                bool clear = !explorerTheme && !ignoreDrawSelection && findImageControl () is null;
+                if (clear || (selected && !ignoreDrawSelection) || (hot && !ignoreDrawHot)) {
+                    bool draw = true;
+                    RECT pClipRect = new RECT ();
+                    OS.SetRect (pClipRect, width, nmcd.top, nmcd.right, nmcd.bottom);
+                    if (explorerTheme) {
+                        if (hooks (DWT.EraseItem)) {
+                            RECT itemRect = item.getBounds (index, true, true, false, false, true, hDC);
+                            itemRect.left -= EXPLORER_EXTRA;
+                            itemRect.right += EXPLORER_EXTRA + 1;
+                            pClipRect.left = itemRect.left;
+                            pClipRect.right = itemRect.right;
+                            if (count > 0 && hwndHeader !is 0) {
+                                HDITEM hdItem = new HDITEM ();
+                                hdItem.mask = OS.HDI_WIDTH;
+                                OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+                                pClipRect.right = Math.min (pClipRect.right, nmcd.left + hdItem.cxy);
+                            }
+                        }
+                        RECT pRect = new RECT ();
+                        OS.SetRect (pRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+                        if (count > 0 && hwndHeader !is 0) {
+                            int totalWidth = 0;
+                            HDITEM hdItem = new HDITEM ();
+                            hdItem.mask = OS.HDI_WIDTH;
+                            for (int j=0; j<count; j++) {
+                                OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, hdItem);
+                                totalWidth += hdItem.cxy;
+                            }
+                            if (totalWidth > clientRect.right - clientRect.left) {
+                                pRect.left = 0;
+                                pRect.right = totalWidth;
+                            } else {
+                                pRect.left = clientRect.left;
+                                pRect.right = clientRect.right;
+                            }
+                        }
+                        draw = false;
+                        int hTheme = OS.OpenThemeData (handle, Display.TREEVIEW);
+                        int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT;
+                        if (OS.GetFocus () !is handle && selected && !hot) iStateId = OS.TREIS_SELECTEDNOTFOCUS;
+                        OS.DrawThemeBackground (hTheme, hDC, OS.TVP_TREEITEM, iStateId, pRect, pClipRect);
+                        OS.CloseThemeData (hTheme);
+                    }
+                    if (draw) fillBackground (hDC, OS.GetBkColor (hDC), pClipRect);
+                }
+            } else {
+                if (explorerTheme && hooks (DWT.EraseItem)) {
+                    if ((selected && !ignoreDrawSelection) || (hot && !ignoreDrawHot)) {
+                        RECT pRect = item.getBounds (index, true, true, false, false, false, hDC);
+                        RECT pClipRect = item.getBounds (index, true, true, false, false, true, hDC);
+                        pRect.left -= EXPLORER_EXTRA;
+                        pRect.right += EXPLORER_EXTRA;
+                        pClipRect.left -= EXPLORER_EXTRA;
+                        pClipRect.right += EXPLORER_EXTRA;
+                        int hTheme = OS.OpenThemeData (handle, Display.TREEVIEW);
+                        int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT;
+                        if (OS.GetFocus () !is handle && selected && !hot) iStateId = OS.TREIS_SELECTEDNOTFOCUS;
+                        OS.DrawThemeBackground (hTheme, hDC, OS.TVP_TREEITEM, iStateId, pRect, pClipRect);
+                        OS.CloseThemeData (hTheme);
+                    }
+                }
+            }
+        }
+        if (x + width > clientRect.left) {
+            RECT rect = new RECT (), backgroundRect = null;
+            bool drawItem = true, drawText = true, drawImage = true, drawBackground = false;
+            if (i is 0) {
+                drawItem = drawImage = drawText = false;
+                if (findImageControl () !is null) {
+                    if (explorerTheme) {
+                        if (OS.IsWindowEnabled (handle) && !hooks (DWT.EraseItem)) {
+                            Image image = null;
+                            if (index is 0) {
+                                image = item.image;
+                            } else {
+                                Image [] images  = item.images;
+                                if (images !is null) image = images [index];
+                            }
+                            if (image !is null) {
+                                Rectangle bounds = image.getBounds ();
+                                if (size is null) size = getImageSize ();
+                                if (!ignoreDrawForeground) {
+                                    GCData data = new GCData();
+                                    data.device = display;
+                                    GC gc = GC.win32_new (hDC, data);
+                                    RECT iconRect = item.getBounds (index, false, true, false, false, true, hDC);
+                                    gc.setClipping (iconRect.left, iconRect.top, iconRect.right - iconRect.left, iconRect.bottom - iconRect.top);
+                                    gc.drawImage (image, 0, 0, bounds.width, bounds.height, iconRect.left, iconRect.top, size.x, size.y);
+                                    OS.SelectClipRgn (hDC, 0);
+                                    gc.dispose ();
+                                }
+                            }
+                        }
+                    } else {
+                        drawItem = drawText = drawBackground = true;
+                        rect = item.getBounds (index, true, false, false, false, true, hDC);
+                        if (linesVisible) {
+                            rect.right++;
+                            rect.bottom++;
+                        }
+                    }
+                }
+                if (selected && !ignoreDrawSelection && !ignoreDrawBackground) {
+                    if (!explorerTheme) fillBackground (hDC, OS.GetBkColor (hDC), rect);
+                    drawBackground = false;
+                }
+                backgroundRect = rect;
+                if (hooks (DWT.EraseItem)) {
+                    drawItem = drawText = drawImage = true;
+                    rect = item.getBounds (index, true, true, false, false, true, hDC);
+                    if ((style & DWT.FULL_SELECTION) !is 0) {
+                        backgroundRect = rect;
+                    } else {
+                        backgroundRect = item.getBounds (index, true, false, false, false, true, hDC);
+                    }
+                }
+            } else {
+                selectionForeground = -1;
+                ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = false;
+                OS.SetRect (rect, x, nmcd.top, x + width, nmcd.bottom);
+                backgroundRect = rect;
+            }
+            int clrText = -1, clrTextBk = -1;
+            int hFont = item.cellFont !is null ? item.cellFont [index] : -1;
+            if (hFont is -1) hFont = item.font;
+            if (selectionForeground !is -1) clrText = selectionForeground;
+            if (OS.IsWindowEnabled (handle)) {
+                bool drawForeground = false;
+                if (selected) {
+                    if (i !is 0 && (style & DWT.FULL_SELECTION) is 0) {
+                        OS.SetTextColor (hDC, getForegroundPixel ());
+                        OS.SetBkColor (hDC, getBackgroundPixel ());
+                        drawForeground = drawBackground = true;
+                    }
+                } else {
+                    drawForeground = drawBackground = true;
+                }
+                if (drawForeground) {
+                    clrText = item.cellForeground !is null ? item.cellForeground [index] : -1;
+                    if (clrText is -1) clrText = item.foreground;
+                }
+                if (drawBackground) {
+                    clrTextBk = item.cellBackground !is null ? item.cellBackground [index] : -1;
+                    if (clrTextBk is -1) clrTextBk = item.background;
+                    if (clrTextBk is -1 && index is sortIndex) clrTextBk = clrSortBk;
+                }
+            } else {
+                if (clrTextBk is -1 && index is sortIndex) {
+                    drawBackground = true;
+                    clrTextBk = clrSortBk;
+                }
+            }
+            if (explorerTheme) {
+                if (selected || (nmcd.uItemState & OS.CDIS_HOT) !is 0) {
+                    if ((style & DWT.FULL_SELECTION) !is 0) {
+                        drawBackground = false;
+                    } else {
+                        if (i is 0) {
+                            drawBackground = false;
+                            if (!hooks (DWT.EraseItem)) drawText = false;
+                        }
+                    }
+                }
+            }
+            if (drawItem) {
+                if (i !is 0) {
+                    if (hooks (DWT.MeasureItem)) {
+                        RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC);
+                        int nSavedDC = OS.SaveDC (hDC);
+                        GCData data = new GCData ();
+                        data.device = display;
+                        data.hFont = hFont;
+                        GC gc = GC.win32_new (hDC, data);
+                        Event event = new Event ();
+                        event.item = item;
+                        event.index = index;
+                        event.gc = gc;
+                        event.x = itemRect.left;
+                        event.y = itemRect.top;
+                        event.width = itemRect.right - itemRect.left;
+                        event.height = itemRect.bottom - itemRect.top;
+                        sendEvent (DWT.MeasureItem, event);
+                        event.gc = null;
+                        gc.dispose ();
+                        OS.RestoreDC (hDC, nSavedDC);
+                        if (isDisposed () || item.isDisposed ()) break;
+                        if (event.height > getItemHeight ()) setItemHeight (event.height);
+                    }
+                    if (hooks (DWT.EraseItem)) {
+                        RECT cellRect = item.getBounds (index, true, true, true, true, true, hDC);
+                        int nSavedDC = OS.SaveDC (hDC);
+                        GCData data = new GCData ();
+                        data.device = display;
+                        data.foreground = OS.GetTextColor (hDC);
+                        data.background = OS.GetBkColor (hDC);
+                        if (!selected || (style & DWT.FULL_SELECTION) is 0) {
+                            if (clrText !is -1) data.foreground = clrText;
+                            if (clrTextBk !is -1) data.background = clrTextBk;
+                        }
+                        data.hFont = hFont;
+                        data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+                        GC gc = GC.win32_new (hDC, data);
+                        Event event = new Event ();
+                        event.item = item;
+                        event.index = index;
+                        event.gc = gc;
+                        event.detail |= DWT.FOREGROUND;
+                        if (clrTextBk !is -1) event.detail |= DWT.BACKGROUND;
+                        if ((style & DWT.FULL_SELECTION) !is 0) {
+                            if (hot) event.detail |= DWT.HOT;
+                            if (selected) event.detail |= DWT.SELECTED;
+                            if (!explorerTheme) {
+                                //if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) {
+                                if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) is nmcd.dwItemSpec) {
+                                    if (handle is OS.GetFocus ()) {
+                                        int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+                                        if ((uiState & OS.UISF_HIDEFOCUS) is 0) event.detail |= DWT.FOCUSED;
+                                    }
+                                }
+                            }
+                        }
+                        event.x = cellRect.left;
+                        event.y = cellRect.top;
+                        event.width = cellRect.right - cellRect.left;
+                        event.height = cellRect.bottom - cellRect.top;
+                        gc.setClipping (event.x, event.y, event.width, event.height);
+                        sendEvent (DWT.EraseItem, event);
+                        event.gc = null;
+                        int newTextClr = data.foreground;
+                        gc.dispose ();
+                        OS.RestoreDC (hDC, nSavedDC);
+                        if (isDisposed () || item.isDisposed ()) break;
+                        if (event.doit) {
+                            ignoreDrawForeground = (event.detail & DWT.FOREGROUND) is 0;
+                            ignoreDrawBackground = (event.detail & DWT.BACKGROUND) is 0;
+                            if ((style & DWT.FULL_SELECTION) !is 0) {
+                                ignoreDrawSelection = (event.detail & DWT.SELECTED) is 0;
+                                ignoreDrawFocus = (event.detail & DWT.FOCUSED) is 0;
+                                ignoreDrawHot = (event.detail & DWT.HOT) is 0;
+                            }
+                        } else {
+                            ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true;
+                        }
+                        if (selected && ignoreDrawSelection) ignoreDrawHot = true;
+                        if ((style & DWT.FULL_SELECTION) !is 0) {
+                            if (ignoreDrawSelection) ignoreFullSelection = true;
+                            if (!ignoreDrawSelection || !ignoreDrawHot) {
+                                if (!selected && !hot) {
+                                    selectionForeground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
+                                } else {
+                                    if (!explorerTheme) {
+                                        drawBackground = true;
+                                        ignoreDrawBackground = false;
+                                        if (handle is OS.GetFocus () && OS.IsWindowEnabled (handle)) {
+                                            clrTextBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
+                                        } else {
+                                            clrTextBk = OS.GetSysColor (OS.COLOR_3DFACE);
+                                        }
+                                        if (!ignoreFullSelection && index is count - 1) {
+                                            RECT selectionRect = new RECT ();
+                                            OS.SetRect (selectionRect, backgroundRect.left, backgroundRect.top, nmcd.right, backgroundRect.bottom);
+                                            backgroundRect = selectionRect;
+                                        }
+                                    } else {
+                                        RECT pRect = new RECT ();
+                                        OS.SetRect (pRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+                                        if (count > 0 && hwndHeader !is 0) {
+                                            int totalWidth = 0;
+                                            HDITEM hdItem = new HDITEM ();
+                                            hdItem.mask = OS.HDI_WIDTH;
+                                            for (int j=0; j<count; j++) {
+                                                OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, hdItem);
+                                                totalWidth += hdItem.cxy;
+                                            }
+                                            if (totalWidth > clientRect.right - clientRect.left) {
+                                                pRect.left = 0;
+                                                pRect.right = totalWidth;
+                                            } else {
+                                                pRect.left = clientRect.left;
+                                                pRect.right = clientRect.right;
+                                            }
+                                            if (index is count - 1) {
+                                                RECT selectionRect = new RECT ();
+                                                OS.SetRect (selectionRect, backgroundRect.left, backgroundRect.top, pRect.right, backgroundRect.bottom);
+                                                backgroundRect = selectionRect;
+                                            }
+                                        }
+                                        int hTheme = OS.OpenThemeData (handle, Display.TREEVIEW);
+                                        int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT;
+                                        if (OS.GetFocus () !is handle && selected && !hot) iStateId = OS.TREIS_SELECTEDNOTFOCUS;
+                                        OS.DrawThemeBackground (hTheme, hDC, OS.TVP_TREEITEM, iStateId, pRect, backgroundRect);
+                                        OS.CloseThemeData (hTheme);
+                                    }
+                                }
+                            } else {
+                                if (selected) {
+                                    selectionForeground = newTextClr;
+                                    if (!explorerTheme) {
+                                        if (clrTextBk is -1 && OS.IsWindowEnabled (handle)) {
+                                            Control control = findBackgroundControl ();
+                                            if (control is null) control = this;
+                                            clrTextBk = control.getBackgroundPixel ();
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    if (selectionForeground !is -1) clrText = selectionForeground;
+                }
+                if (!ignoreDrawBackground) {
+                    if (clrTextBk !is -1) {
+                        if (drawBackground) fillBackground (hDC, clrTextBk, backgroundRect);
+                    } else {
+                        Control control = findImageControl ();
+                        if (control !is null) {
+                            if (i is 0) {
+                                int right = Math.min (rect.right, width);
+                                OS.SetRect (rect, rect.left, rect.top, right, rect.bottom);
+                                if (drawBackground) fillImageBackground (hDC, control, rect);
+                            } else {
+                                if (drawBackground) fillImageBackground (hDC, control, rect);
+                            }
+                        }
+                    }
+                }
+                rect.left += INSET - 1;
+                if (drawImage) {
+                    Image image = null;
+                    if (index is 0) {
+                        image = item.image;
+                    } else {
+                        Image [] images  = item.images;
+                        if (images !is null) image = images [index];
+                    }
+                    int inset = i !is 0 ? INSET : 0;
+                    int offset = i !is 0 ? INSET : INSET + 2;
+                    if (image !is null) {
+                        Rectangle bounds = image.getBounds ();
+                        if (size is null) size = getImageSize ();
+                        //int y = rect.top + (index is 0 ? (getItemHeight () - size.y) / 2 : 0);
+                        int y = rect.top;
+                        if (!ignoreDrawForeground) {
+                            //TODO - share GC, clip the drawing for index is 0
+                            GCData data = new GCData();
+                            data.device = display;
+                            GC gc = GC.win32_new (hDC, data);
+                            //if (index is 0) { //must clear
+                                //gc.setClipping (rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+                            //}
+                            gc.drawImage (image, 0, 0, bounds.width, bounds.height, rect.left - inset + 1, y, size.x, size.y);
+                            gc.dispose ();
+                        }
+                        OS.SetRect (rect, rect.left + size.x + offset, rect.top, rect.right - inset, rect.bottom);
+                    } else {
+                        if (i is 0) {
+                            if (OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0) !is 0) {
+                                if (size is null) size = getImageSize ();
+                                rect.left = Math.min (rect.left + size.x + offset, rect.right);
+                            }
+                        } else {
+                            OS.SetRect (rect, rect.left + offset, rect.top, rect.right - inset, rect.bottom);
+                        }
+                    }
+                }
+                if (drawText) {
+                    /*
+                    * Bug in Windows.  When DrawText() is used with DT_VCENTER
+                    * and DT_ENDELLIPSIS, the ellipsis can draw outside of the
+                    * rectangle when the rectangle is empty.  The fix is avoid
+                    * all text drawing for empty rectangles.
+                    */
+                    if (rect.left < rect.right) {
+                        String string = null;
+                        if (index is 0) {
+                            string = item.text;
+                        } else {
+                            String [] strings  = item.strings;
+                            if (strings !is null) string = strings [index];
+                        }
+                        if (string !is null) {
+                            if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+                            if (clrText !is -1) clrText = OS.SetTextColor (hDC, clrText);
+                            if (clrTextBk !is -1) clrTextBk = OS.SetBkColor (hDC, clrTextBk);
+                            int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER;
+                            if (i !is 0) flags |= OS.DT_ENDELLIPSIS;
+                            TreeColumn column = columns !is null ? columns [index] : null;
+                            if (column !is null) {
+                                if ((column.style & DWT.CENTER) !is 0) flags |= OS.DT_CENTER;
+                                if ((column.style & DWT.RIGHT) !is 0) flags |= OS.DT_RIGHT;
+                            }
+                            TCHAR buffer = new TCHAR (getCodePage (), string, false);
+                            if (!ignoreDrawForeground) OS.DrawText (hDC, buffer, buffer.length (), rect, flags);
+                            OS.DrawText (hDC, buffer, buffer.length (), rect, flags | OS.DT_CALCRECT);
+                            if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+                            if (clrText !is -1) clrText = OS.SetTextColor (hDC, clrText);
+                            if (clrTextBk !is -1) clrTextBk = OS.SetBkColor (hDC, clrTextBk);
+                        }
+                    }
+                }
+            }
+            if (selectionForeground !is -1) clrText = selectionForeground;
+            if (hooks (DWT.PaintItem)) {
+                RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC);
+                int nSavedDC = OS.SaveDC (hDC);
+                GCData data = new GCData ();
+                data.device = display;
+                data.hFont = hFont;
+                data.foreground = OS.GetTextColor (hDC);
+                data.background = OS.GetBkColor (hDC);
+                if (selected && (style & DWT.FULL_SELECTION) !is 0) {
+                    if (selectionForeground !is -1) data.foreground = selectionForeground;
+                } else {
+                    if (clrText !is -1) data.foreground = clrText;
+                    if (clrTextBk !is -1) data.background = clrTextBk;
+                }
+                data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+                GC gc = GC.win32_new (hDC, data);
+                Event event = new Event ();
+                event.item = item;
+                event.index = index;
+                event.gc = gc;
+                event.detail |= DWT.FOREGROUND;
+                if (clrTextBk !is -1) event.detail |= DWT.BACKGROUND;
+                if (hot) event.detail |= DWT.HOT;
+                if (selected && (i is 0 /*nmcd.iSubItem is 0*/ || (style & DWT.FULL_SELECTION) !is 0)) {
+                    event.detail |= DWT.SELECTED;
+                }
+                if (!explorerTheme) {
+                    //if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) {
+                    if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) is nmcd.dwItemSpec) {
+                        if (i is 0 /*nmcd.iSubItem is 0*/ || (style & DWT.FULL_SELECTION) !is 0) {
+                            if (handle is OS.GetFocus ()) {
+                                int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+                                if ((uiState & OS.UISF_HIDEFOCUS) is 0) event.detail |= DWT.FOCUSED;
+                            }
+                        }
+                    }
+                }
+                event.x = itemRect.left;
+                event.y = itemRect.top;
+                event.width = itemRect.right - itemRect.left;
+                event.height = itemRect.bottom - itemRect.top;
+                RECT cellRect = item.getBounds (index, true, true, true, true, true, hDC);
+                int cellWidth = cellRect.right - cellRect.left;
+                int cellHeight = cellRect.bottom - cellRect.top;
+                gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight);
+                sendEvent (DWT.PaintItem, event);
+                event.gc = null;
+                gc.dispose ();
+                OS.RestoreDC (hDC, nSavedDC);
+                if (isDisposed () || item.isDisposed ()) break;
+            }
+        }
+        x += width;
+        if (x > clientRect.right) break;
+    }
+    if (linesVisible) {
+        if ((style & DWT.FULL_SELECTION) !is 0) {
+            if (hwndHeader !is 0) {
+                if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0) !is 0) {
+                    HDITEM hdItem = new HDITEM ();
+                    hdItem.mask = OS.HDI_WIDTH;
+                    OS.SendMessage (hwndHeader, OS.HDM_GETITEM, 0, hdItem);
+                    RECT rect = new RECT ();
+                    OS.SetRect (rect, nmcd.left + hdItem.cxy, nmcd.top, nmcd.right, nmcd.bottom);
+                    OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
+                }
+            }
+        }
+        RECT rect = new RECT ();
+        OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+        OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
+    }
+    if (!explorerTheme) {
+        if (handle is OS.GetFocus ()) {
+            int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+            if ((uiState & OS.UISF_HIDEFOCUS) is 0) {
+                int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+                if (hItem is item.handle) {
+                    if (!ignoreDrawFocus && findImageControl () !is null) {
+                        if ((style & DWT.FULL_SELECTION) !is 0) {
+                            RECT focusRect = new RECT ();
+                            OS.SetRect (focusRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+                            if (count > 0 && hwndHeader !is 0) {
+                                int width = 0;
+                                HDITEM hdItem = new HDITEM ();
+                                hdItem.mask = OS.HDI_WIDTH;
+                                for (int j=0; j<count; j++) {
+                                    OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, hdItem);
+                                    width += hdItem.cxy;
+                                }
+                                focusRect.left = 0;
+                                RECT rect = new RECT ();
+                                OS.GetClientRect (handle, rect);
+                                focusRect.right = Math.max (width, rect.right - OS.GetSystemMetrics (OS.SM_CXVSCROLL));
+                            }
+                            OS.DrawFocusRect (hDC, focusRect);
+                        } else {
+                            int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
+                            RECT focusRect = item.getBounds (index, true, false, false, false, false, hDC);
+                            RECT clipRect = item.getBounds (index, true, false, false, false, true, hDC);
+                            OS.IntersectClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+                            OS.DrawFocusRect (hDC, focusRect);
+                            OS.SelectClipRgn (hDC, 0);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return new LRESULT (OS.CDRF_DODEFAULT);
+}
+
+LRESULT CDDS_ITEMPREPAINT (NMTVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    /*
+    * Even when custom draw is being ignored, the font needs
+    * to be selected into the HDC so that the item bounds are
+    * measured correctly.
+    */
+    TreeItem item = getItem (nmcd);
+    /*
+    * Feature in Windows.  When a new tree item is inserted
+    * using TVM_INSERTITEM and the tree is using custom draw,
+    * a NM_CUSTOMDRAW is sent before TVM_INSERTITEM returns
+    * and before the item is added to the items array.  The
+    * fix is to check for null.
+    *
+    * NOTE: This only happens on XP with the version 6.00 of
+    * COMCTL32.DLL,
+    */
+    if (item is null) return null;
+    int hDC = nmcd.hdc;
+    int index = hwndHeader !is 0 ? OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0) : 0;
+    int hFont = item.cellFont !is null ? item.cellFont [index] : -1;
+    if (hFont is -1) hFont = item.font;
+    if (hFont !is -1) OS.SelectObject (hDC, hFont);
+    if (ignoreCustomDraw || nmcd.left is nmcd.right) {
+        return new LRESULT (hFont is -1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT);
+    }
+    int count = 0;
+    RECT clipRect = null;
+    if (hwndHeader !is 0) {
+        count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+        if (count !is 0) {
+            bool clip = !printClient;
+            if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                clip = true;
+            }
+            if (clip) {
+                clipRect = new RECT ();
+                HDITEM hdItem = new HDITEM ();
+                hdItem.mask = OS.HDI_WIDTH;
+                OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+                OS.SetRect (clipRect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom);
+            }
+        }
+    }
+    int clrText = -1, clrTextBk = -1;
+    if (OS.IsWindowEnabled (handle)) {
+        clrText = item.cellForeground !is null ? item.cellForeground [index] : -1;
+        if (clrText is -1) clrText = item.foreground;
+        clrTextBk = item.cellBackground !is null ? item.cellBackground [index] : -1;
+        if (clrTextBk is -1) clrTextBk = item.background;
+    }
+    int clrSortBk = -1;
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        if (sortColumn !is null && sortDirection !is DWT.NONE) {
+            if (findImageControl () is null) {
+                if (indexOf (sortColumn) is index) {
+                    clrSortBk = getSortColumnPixel ();
+                    if (clrTextBk is -1) clrTextBk = clrSortBk;
+                }
+            }
+        }
+    }
+    bool selected = isItemSelected (nmcd);
+    bool hot = explorerTheme && (nmcd.uItemState & OS.CDIS_HOT) !is 0;
+    if (OS.IsWindowVisible (handle) && nmcd.left < nmcd.right && nmcd.top < nmcd.bottom) {
+        if (hFont !is -1) OS.SelectObject (hDC, hFont);
+        if (linesVisible) {
+            RECT rect = new RECT ();
+            OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+            OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
+        }
+        //TODO - BUG - measure and erase sent when first column is clipped
+        if (hooks (DWT.MeasureItem)) {
+            RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC);
+            int nSavedDC = OS.SaveDC (hDC);
+            GCData data = new GCData ();
+            data.device = display;
+            data.hFont = hFont;
+            GC gc = GC.win32_new (hDC, data);
+            Event event = new Event ();
+            event.item = item;
+            event.gc = gc;
+            event.index = index;
+            event.x = itemRect.left;
+            event.y = itemRect.top;
+            event.width = itemRect.right - itemRect.left;
+            event.height = itemRect.bottom - itemRect.top;
+            sendEvent (DWT.MeasureItem, event);
+            event.gc = null;
+            gc.dispose ();
+            OS.RestoreDC (hDC, nSavedDC);
+            if (isDisposed () || item.isDisposed ()) return null;
+            if (hwndHeader !is 0) {
+                if (count is 0) {
+                    if (event.x + event.width > scrollWidth) {
+                        setScrollWidth (scrollWidth = event.x + event.width);
+                    }
+                }
+            }
+            if (event.height > getItemHeight ()) setItemHeight (event.height);
+        }
+        selectionForeground = -1;
+        ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = ignoreFullSelection = false;
+        if (hooks (DWT.EraseItem)) {
+            RECT rect = new RECT ();
+            OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+            if (OS.IsWindowEnabled (handle) || findImageControl () !is null) {
+                drawBackground (hDC, rect);
+            } else {
+                fillBackground (hDC, OS.GetBkColor (hDC), rect);
+            }
+            RECT cellRect = item.getBounds (index, true, true, true, true, true, hDC);
+            if (clrSortBk !is -1) {
+                RECT fullRect = item.getBounds (index, true, true, true, true, true, hDC);
+                drawBackground (hDC, fullRect, clrSortBk);
+            }
+            int nSavedDC = OS.SaveDC (hDC);
+            GCData data = new GCData ();
+            data.device = display;
+            if (selected && explorerTheme) {
+                data.foreground = OS.GetSysColor (OS.COLOR_WINDOWTEXT);
+            } else {
+                data.foreground = OS.GetTextColor (hDC);
+            }
+            data.background = OS.GetBkColor (hDC);
+            if (!selected) {
+                if (clrText !is -1) data.foreground = clrText;
+                if (clrTextBk !is -1) data.background = clrTextBk;
+            }
+            data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+            if (hFont !is -1) data.hFont = hFont;
+            GC gc = GC.win32_new (hDC, data);
+            Event event = new Event ();
+            event.index = index;
+            event.item = item;
+            event.gc = gc;
+            event.detail |= DWT.FOREGROUND;
+            if (clrTextBk !is -1) event.detail |= DWT.BACKGROUND;
+            if (hot) event.detail |= DWT.HOT;
+            if (selected) event.detail |= DWT.SELECTED;
+            if (!explorerTheme) {
+                //if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) {
+                if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) is nmcd.dwItemSpec) {
+                    if (handle is OS.GetFocus ()) {
+                        int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+                        if ((uiState & OS.UISF_HIDEFOCUS) is 0) event.detail |= DWT.FOCUSED;
+                    }
+                }
+            }
+            event.x = cellRect.left;
+            event.y = cellRect.top;
+            event.width = cellRect.right - cellRect.left;
+            event.height = cellRect.bottom - cellRect.top;
+            gc.setClipping (event.x, event.y, event.width, event.height);
+            sendEvent (DWT.EraseItem, event);
+            event.gc = null;
+            int newTextClr = data.foreground;
+            gc.dispose ();
+            OS.RestoreDC (hDC, nSavedDC);
+            if (isDisposed () || item.isDisposed ()) return null;
+            if (event.doit) {
+                ignoreDrawForeground = (event.detail & DWT.FOREGROUND) is 0;
+                ignoreDrawBackground = (event.detail & DWT.BACKGROUND) is 0;
+                ignoreDrawSelection = (event.detail & DWT.SELECTED) is 0;
+                ignoreDrawFocus = (event.detail & DWT.FOCUSED) is 0;
+                ignoreDrawHot = (event.detail & DWT.HOT) is 0;
+            } else {
+                ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true;
+            }
+            if (selected && ignoreDrawSelection) ignoreDrawHot = true;
+            if (!ignoreDrawBackground && clrTextBk !is -1) {
+                bool draw = !selected && !hot;
+                if (!explorerTheme && selected) draw = !ignoreDrawSelection;
+                if (draw) {
+                    if (count is 0) {
+                        if ((style & DWT.FULL_SELECTION) !is 0) {
+                            fillBackground (hDC, clrTextBk, rect);
+                        } else {
+                            RECT textRect = item.getBounds (index, true, false, true, false, true, hDC);
+                            fillBackground (hDC, clrTextBk, textRect);
+                        }
+                    } else {
+                        fillBackground (hDC, clrTextBk, cellRect);
+                    }
+                }
+            }
+            if (ignoreDrawSelection) ignoreFullSelection = true;
+            if (!ignoreDrawSelection || !ignoreDrawHot) {
+                if (!selected && !hot) {
+                    selectionForeground = clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
+                }
+                if (!explorerTheme) {
+                    /*
+                    * Feature in Windows.  When the tree has the style
+                    * TVS_FULLROWSELECT, the background color for the
+                    * entire row is filled when an item is painted,
+                    * drawing on top of any custom drawing.  The fix
+                    * is to emulate TVS_FULLROWSELECT.
+                    */
+                    if ((style & DWT.FULL_SELECTION) !is 0) {
+                        if ((style & DWT.FULL_SELECTION) !is 0 && count is 0) {
+                            fillBackground (hDC, OS.GetBkColor (hDC), rect);
+                        } else {
+                            fillBackground (hDC, OS.GetBkColor (hDC), cellRect);
+                        }
+                    } else {
+                        RECT textRect = item.getBounds (index, true, false, false, false, true, hDC);
+                        fillBackground (hDC, OS.GetBkColor (hDC), textRect);
+                    }
+                }
+            } else {
+                if (selected || hot) {
+                    selectionForeground = clrText = newTextClr;
+                    ignoreDrawSelection = ignoreDrawHot = true;
+                }
+                if (explorerTheme) {
+                    nmcd.uItemState |= OS.CDIS_DISABLED;
+                    /*
+                    * Feature in Windows.  On Vista only, when the text
+                    * color is unchanged and an item is asked to draw
+                    * disabled, it uses the disabled color.  The fix is
+                    * to modify the color so that is it no longer equal.
+                    */
+                    int newColor = clrText is -1 ? getForegroundPixel () : clrText;
+                    if (nmcd.clrText is newColor) {
+                        nmcd.clrText |= 0x20000000;
+                        if (nmcd.clrText is newColor) nmcd.clrText &= ~0x20000000;
+                    } else {
+                        nmcd.clrText = newColor;
+                    }
+                    OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
+                }
+            }
+            if (explorerTheme) {
+                if (selected || (hot && ignoreDrawHot)) nmcd.uItemState &= ~OS.CDIS_HOT;
+                OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
+            }
+            RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC);
+            OS.SaveDC (hDC);
+            OS.SelectClipRgn (hDC, 0);
+            if (explorerTheme) {
+                itemRect.left -= EXPLORER_EXTRA;
+                itemRect.right += EXPLORER_EXTRA;
+            }
+            //TODO - bug in Windows selection or DWT itemRect
+            /*if (selected)*/ itemRect.right++;
+            if (linesVisible) itemRect.bottom++;
+            if (clipRect !is null) {
+                OS.IntersectClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+            }
+            OS.ExcludeClipRect (hDC, itemRect.left, itemRect.top, itemRect.right, itemRect.bottom);
+            return new LRESULT (OS.CDRF_DODEFAULT | OS.CDRF_NOTIFYPOSTPAINT);
+        }
+        /*
+        * Feature in Windows.  When the tree has the style
+        * TVS_FULLROWSELECT, the background color for the
+        * entire row is filled when an item is painted,
+        * drawing on top of any custom drawing.  The fix
+        * is to emulate TVS_FULLROWSELECT.
+        */
+        if ((style & DWT.FULL_SELECTION) !is 0) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            if ((bits & OS.TVS_FULLROWSELECT) is 0) {
+                RECT rect = new RECT ();
+                OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+                if (selected) {
+                    fillBackground (hDC, OS.GetBkColor (hDC), rect);
+                } else {
+                    if (OS.IsWindowEnabled (handle)) drawBackground (hDC, rect);
+                }
+                nmcd.uItemState &= ~OS.CDIS_FOCUS;
+                OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
+            }
+        }
+    }
+    LRESULT result = null;
+    if (clrText is -1 && clrTextBk is -1 && hFont is -1) {
+        result = new LRESULT (OS.CDRF_DODEFAULT | OS.CDRF_NOTIFYPOSTPAINT);
+    } else {
+        result = new LRESULT (OS.CDRF_NEWFONT | OS.CDRF_NOTIFYPOSTPAINT);
+        if (hFont !is -1) OS.SelectObject (hDC, hFont);
+        if (OS.IsWindowEnabled (handle) && OS.IsWindowVisible (handle)) {
+            /*
+            * Feature in Windows.  Windows does not fill the entire cell
+            * with the background color when TVS_FULLROWSELECT is not set.
+            * The fix is to fill the cell with the background color.
+            */
+            if (clrTextBk !is -1) {
+                int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+                if ((bits & OS.TVS_FULLROWSELECT) is 0) {
+                    if (count !is 0 && hwndHeader !is 0) {
+                        RECT rect = new RECT ();
+                        HDITEM hdItem = new HDITEM ();
+                        hdItem.mask = OS.HDI_WIDTH;
+                        OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+                        OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom);
+                        if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) {
+                            RECT itemRect = new RECT ();
+                            itemRect.left = item.handle;
+                            if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, itemRect) !is 0) {
+                                rect.left = Math.min (itemRect.left, rect.right);
+                            }
+                        }
+                        if ((style & DWT.FULL_SELECTION) !is 0) {
+                            if (!selected) fillBackground (hDC, clrTextBk, rect);
+                        } else {
+                            if (explorerTheme) {
+                                if (!selected && !hot) fillBackground (hDC, clrTextBk, rect);
+                            } else {
+                                fillBackground (hDC, clrTextBk, rect);
+                            }
+                        }
+                    } else {
+                        if ((style & DWT.FULL_SELECTION) !is 0) {
+                            RECT rect = new RECT ();
+                            OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+                            if (!selected) fillBackground (hDC, clrTextBk, rect);
+                        }
+                    }
+                }
+            }
+            if (!selected) {
+                nmcd.clrText = clrText is -1 ? getForegroundPixel () : clrText;
+                nmcd.clrTextBk = clrTextBk is -1 ? getBackgroundPixel () : clrTextBk;
+                OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
+            }
+        }
+    }
+    if (OS.IsWindowEnabled (handle)) {
+        /*
+        * On Vista only, when an item is asked to draw disabled,
+        * the background of the text is not filled with the
+        * background color of the tree.  This is true for both
+        * regular and full selection trees.  In order to draw a
+        * background image, mark the item as disabled using
+        * CDIS_DISABLED (when not selected) and set the text
+        * to the regular text color to avoid drawing disabled.
+        */
+        if (explorerTheme) {
+            if (findImageControl () !is  null) {
+                if (!selected && (nmcd.uItemState & (OS.CDIS_HOT | OS.CDIS_SELECTED)) is 0) {
+                    nmcd.uItemState |= OS.CDIS_DISABLED;
+                    /*
+                    * Feature in Windows.  On Vista only, when the text
+                    * color is unchanged and an item is asked to draw
+                    * disabled, it uses the disabled color.  The fix is
+                    * to modify the color so it is no longer equal.
+                    */
+                    int newColor = clrText is -1 ? getForegroundPixel () : clrText;
+                    if (nmcd.clrText is newColor) {
+                        nmcd.clrText |= 0x20000000;
+                        if (nmcd.clrText is newColor) nmcd.clrText &= ~0x20000000;
+                    } else {
+                        nmcd.clrText = newColor;
+                    }
+                    OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
+                    if (clrTextBk !is -1) {
+                        if ((style & DWT.FULL_SELECTION) !is 0) {
+                            RECT rect = new RECT ();
+                            if (count !is 0) {
+                                HDITEM hdItem = new HDITEM ();
+                                hdItem.mask = OS.HDI_WIDTH;
+                                OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+                                OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom);
+                            } else {
+                                OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+                            }
+                            fillBackground (hDC, clrTextBk, rect);
+                        } else {
+                            RECT textRect = item.getBounds (index, true, false, true, false, true, hDC);
+                            fillBackground (hDC, clrTextBk, textRect);
+                        }
+                    }
+                }
+            }
+        }
+    } else {
+        /*
+        * Feature in Windows.  When the tree is disabled, it draws
+        * with a gray background over the sort column.  The fix is
+        * to fill the background with the sort column color.
+        */
+        if (clrSortBk !is -1) {
+            RECT rect = new RECT ();
+            HDITEM hdItem = new HDITEM ();
+            hdItem.mask = OS.HDI_WIDTH;
+            OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+            OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom);
+            fillBackground (hDC, clrSortBk, rect);
+        }
+    }
+    OS.SaveDC (hDC);
+    if (clipRect !is null) {
+        int hRgn = OS.CreateRectRgn (clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+        POINT lpPoint = new POINT ();
+        OS.GetWindowOrgEx (hDC, lpPoint);
+        OS.OffsetRgn (hRgn, -lpPoint.x, -lpPoint.y);
+        OS.SelectClipRgn (hDC, hRgn);
+        OS.DeleteObject (hRgn);
+    }
+    return result;
+}
+
+LRESULT CDDS_POSTPAINT (NMTVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    if (ignoreCustomDraw) return null;
+    if (OS.IsWindowVisible (handle)) {
+        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+            if (sortColumn !is null && sortDirection !is DWT.NONE) {
+                if (findImageControl () is null) {
+                    int index = indexOf (sortColumn);
+                    if (index !is -1) {
+                        int top = nmcd.top;
+                        /*
+                        * Bug in Windows.  For some reason, during a collapse,
+                        * when TVM_GETNEXTITEM is sent with TVGN_LASTVISIBLE
+                        * and the collapse causes the item being collapsed
+                        * to become the last visible item in the tree, the
+                        * message takes a long time to process.  In order for
+                        * the slowness to happen, the children of the item
+                        * must have children.  Times of up to 11 seconds have
+                        * been observed with 23 children, each having one
+                        * child.  The fix is to use the bottom partially
+                        * visible item rather than the last possible item
+                        * that could be visible.
+                        *
+                        * NOTE: This problem only happens on Vista during
+                        * WM_NOTIFY with NM_CUSTOMDRAW and CDDS_POSTPAINT.
+                        */
+                        int hItem = 0;
+                        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                            hItem = getBottomItem ();
+                        } else {
+                            hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0);
+                        }
+                        if (hItem !is 0) {
+                            RECT rect = new RECT ();
+                            rect.left = hItem;
+                            if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect) !is 0) {
+                                top = rect.bottom;
+                            }
+                        }
+                        RECT rect = new RECT ();
+                        OS.SetRect (rect, nmcd.left, top, nmcd.right, nmcd.bottom);
+                        RECT headerRect = new RECT ();
+                        OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+                        rect.left = headerRect.left;
+                        rect.right = headerRect.right;
+                        fillBackground (nmcd.hdc, getSortColumnPixel (), rect);
+                    }
+                }
+            }
+        }
+        if (linesVisible) {
+            int hDC = nmcd.hdc;
+            if (hwndHeader !is 0) {
+                int x = 0;
+                RECT rect = new RECT ();
+                HDITEM hdItem = new HDITEM ();
+                hdItem.mask = OS.HDI_WIDTH;
+                int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                for (int i=0; i<count; i++) {
+                    int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, i, 0);
+                    OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+                    OS.SetRect (rect, x, nmcd.top, x + hdItem.cxy, nmcd.bottom);
+                    OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_RIGHT);
+                    x += hdItem.cxy;
+                }
+            }
+            int height = 0;
+            RECT rect = new RECT ();
+            /*
+            * Bug in Windows.  For some reason, during a collapse,
+            * when TVM_GETNEXTITEM is sent with TVGN_LASTVISIBLE
+            * and the collapse causes the item being collapsed
+            * to become the last visible item in the tree, the
+            * message takes a long time to process.  In order for
+            * the slowness to happen, the children of the item
+            * must have children.  Times of up to 11 seconds have
+            * been observed with 23 children, each having one
+            * child.  The fix is to use the bottom partially
+            * visible item rather than the last possible item
+            * that could be visible.
+            *
+            * NOTE: This problem only happens on Vista during
+            * WM_NOTIFY with NM_CUSTOMDRAW and CDDS_POSTPAINT.
+            */
+            int hItem = 0;
+            if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                hItem = getBottomItem ();
+            } else {
+                hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0);
+            }
+            if (hItem !is 0) {
+                rect.left = hItem;
+                if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect) !is 0) {
+                    height = rect.bottom - rect.top;
+                }
+            }
+            if (height is 0) {
+                height = OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0);
+                OS.GetClientRect (handle, rect);
+                OS.SetRect (rect, rect.left, rect.top, rect.right, rect.top + height);
+                OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
+            }
+            while (rect.bottom < nmcd.bottom) {
+                int top = rect.top + height;
+                OS.SetRect (rect, rect.left, top, rect.right, top + height);
+                OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
+            }
+        }
+    }
+    return new LRESULT (OS.CDRF_DODEFAULT);
+}
+
+LRESULT CDDS_PREPAINT (NMTVCUSTOMDRAW nmcd, int wParam, int lParam) {
+    if (explorerTheme) {
+        if ((OS.IsWindowEnabled (handle) && hooks (DWT.EraseItem)) || findImageControl () !is null) {
+            RECT rect = new RECT ();
+            OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
+            drawBackground (nmcd.hdc, rect);
+        }
+    }
+    return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (handle is 0) return 0;
+    if (hwndParent !is 0 && hwnd is hwndParent) {
+        return OS.DefWindowProc (hwnd, msg, wParam, lParam);
+    }
+    if (hwndHeader !is 0 && hwnd is hwndHeader) {
+        return OS.CallWindowProc (HeaderProc, hwnd, msg, wParam, lParam);
+    }
+    switch (msg) {
+        case OS.WM_SETFOCUS: {
+            /*
+            * Feature in Windows.  When a tree control processes WM_SETFOCUS,
+            * if no item is selected, the first item in the tree is selected.
+            * This is unexpected and might clear the previous selection.
+            * The fix is to detect that there is no selection and set it to
+            * the first visible item in the tree.  If the item was not selected,
+            * only the focus is assigned.
+            */
+            if ((style & DWT.SINGLE) !is 0) break;
+            int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+            if (hItem is 0) {
+                hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+                if (hItem !is 0) {
+                    TVITEM tvItem = new TVITEM ();
+                    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+                    tvItem.hItem = hItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                    hSelect = hItem;
+                    ignoreDeselect = ignoreSelect = lockSelection = true;
+                    OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hItem);
+                    ignoreDeselect = ignoreSelect = lockSelection = false;
+                    hSelect = 0;
+                    if ((tvItem.state & OS.TVIS_SELECTED) is 0) {
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                    }
+                }
+            }
+            break;
+        }
+    }
+    int hItem = 0;
+    bool redraw = false;
+    switch (msg) {
+        /* Keyboard messages */
+        case OS.WM_KEYDOWN:
+            if (wParam is OS.VK_CONTROL || wParam is OS.VK_SHIFT) break;
+            //FALL THROUGH
+        case OS.WM_CHAR:
+        case OS.WM_IME_CHAR:
+        case OS.WM_KEYUP:
+        case OS.WM_SYSCHAR:
+        case OS.WM_SYSKEYDOWN:
+        case OS.WM_SYSKEYUP:
+            //FALL THROUGH
+
+        /* Scroll messages */
+        case OS.WM_HSCROLL:
+        case OS.WM_VSCROLL:
+            //FALL THROUGH
+
+        /* Resize messages */
+        case OS.WM_SIZE:
+            redraw = findImageControl () !is null && drawCount is 0 && OS.IsWindowVisible (handle);
+            if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+            //FALL THROUGH
+
+        /* Mouse messages */
+        case OS.WM_LBUTTONDBLCLK:
+        case OS.WM_LBUTTONDOWN:
+        case OS.WM_LBUTTONUP:
+        case OS.WM_MBUTTONDBLCLK:
+        case OS.WM_MBUTTONDOWN:
+        case OS.WM_MBUTTONUP:
+        case OS.WM_MOUSEHOVER:
+        case OS.WM_MOUSELEAVE:
+        case OS.WM_MOUSEMOVE:
+        case OS.WM_MOUSEWHEEL:
+        case OS.WM_RBUTTONDBLCLK:
+        case OS.WM_RBUTTONDOWN:
+        case OS.WM_RBUTTONUP:
+        case OS.WM_XBUTTONDBLCLK:
+        case OS.WM_XBUTTONDOWN:
+        case OS.WM_XBUTTONUP:
+            //FALL THROUGH
+
+        /* Other messages */
+        case OS.WM_SETFONT:
+        case OS.WM_TIMER: {
+            if (findImageControl () !is null) {
+                hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+            }
+            break;
+        }
+    }
+    int code = OS.CallWindowProc (TreeProc, hwnd, msg, wParam, lParam);
+    switch (msg) {
+        /* Keyboard messages */
+        case OS.WM_KEYDOWN:
+            if (wParam is OS.VK_CONTROL || wParam is OS.VK_SHIFT) break;
+            //FALL THROUGH
+        case OS.WM_CHAR:
+        case OS.WM_IME_CHAR:
+        case OS.WM_KEYUP:
+        case OS.WM_SYSCHAR:
+        case OS.WM_SYSKEYDOWN:
+        case OS.WM_SYSKEYUP:
+            //FALL THROUGH
+
+        /* Scroll messages */
+        case OS.WM_HSCROLL:
+        case OS.WM_VSCROLL:
+            //FALL THROUGH
+
+        /* Resize messages */
+        case OS.WM_SIZE:
+            if (redraw) {
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+                OS.InvalidateRect (handle, null, true);
+                if (hwndHeader !is 0) OS.InvalidateRect (hwndHeader, null, true);
+            }
+            //FALL THROUGH
+
+        /* Mouse messages */
+        case OS.WM_LBUTTONDBLCLK:
+        case OS.WM_LBUTTONDOWN:
+        case OS.WM_LBUTTONUP:
+        case OS.WM_MBUTTONDBLCLK:
+        case OS.WM_MBUTTONDOWN:
+        case OS.WM_MBUTTONUP:
+        case OS.WM_MOUSEHOVER:
+        case OS.WM_MOUSELEAVE:
+        case OS.WM_MOUSEMOVE:
+        case OS.WM_MOUSEWHEEL:
+        case OS.WM_RBUTTONDBLCLK:
+        case OS.WM_RBUTTONDOWN:
+        case OS.WM_RBUTTONUP:
+        case OS.WM_XBUTTONDBLCLK:
+        case OS.WM_XBUTTONDOWN:
+        case OS.WM_XBUTTONUP:
+            //FALL THROUGH
+
+        /* Other messages */
+        case OS.WM_SETFONT:
+        case OS.WM_TIMER: {
+            if (findImageControl () !is null) {
+                if (hItem !is OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0)) {
+                    OS.InvalidateRect (handle, null, true);
+                }
+            }
+            updateScrollBar ();
+            break;
+        }
+
+        case OS.WM_PAINT:
+            painted = true;
+            break;
+    }
+    return code;
+}
+
+void checkBuffered () {
+    super.checkBuffered ();
+    if ((style & DWT.VIRTUAL) !is 0) {
+        style |= DWT.DOUBLE_BUFFERED;
+        OS.SendMessage (handle, OS.TVM_SETSCROLLTIME, 0, 0);
+    }
+    if (EXPLORER_THEME) {
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+            int exStyle = OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0);
+            if ((exStyle & OS.TVS_EX_DOUBLEBUFFER) !is 0) style |= DWT.DOUBLE_BUFFERED;
+        }
+    }
+}
+
+bool checkData (TreeItem item, bool redraw) {
+    if ((style & DWT.VIRTUAL) is 0) return true;
+    TreeItem parentItem = item.getParentItem ();
+    return checkData (item, parentItem is null ? indexOf (item) : parentItem.indexOf (item), redraw);
+}
+
+bool checkData (TreeItem item, int index, bool redraw) {
+    if ((style & DWT.VIRTUAL) is 0) return true;
+    if (!item.cached) {
+        item.cached = true;
+        Event event = new Event ();
+        event.item = item;
+        event.index = index;
+        TreeItem oldItem = currentItem;
+        currentItem = item;
+        sendEvent (DWT.SetData, event);
+        //widget could be disposed at this point
+        currentItem = oldItem;
+        if (isDisposed () || item.isDisposed ()) return false;
+        if (redraw) item.redraw ();
+    }
+    return true;
+}
+
+bool checkHandle (int hwnd) {
+    return hwnd is handle || (hwndParent !is 0 && hwnd is hwndParent) || (hwndHeader !is 0 && hwnd is hwndHeader);
+}
+
+bool checkScroll (int hItem) {
+    /*
+    * Feature in Windows.  If redraw is turned off using WM_SETREDRAW
+    * and a tree item that is not a child of the first root is selected or
+    * scrolled using TVM_SELECTITEM or TVM_ENSUREVISIBLE, then scrolling
+    * does not occur.  The fix is to detect this case, and make sure
+    * that redraw is temporarily enabled.  To avoid flashing, DefWindowProc()
+    * is called to disable redrawing.
+    *
+    * NOTE:  The code that actually works around the problem is in the
+    * callers of this method.
+    */
+    if (drawCount is 0) return false;
+    int hRoot = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    int hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
+    while (hParent !is hRoot && hParent !is 0) {
+        hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hParent);
+    }
+    return hParent is 0;
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+/**
+ * Clears the item at the given zero-relative index in the receiver.
+ * The text, icon and other attributes of the item are set to the default
+ * value.  If the tree was created with the <code>DWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param index the index of the item to clear
+ * @param all <code>true</code> if all child items of the indexed item should be
+ * cleared recursively, and <code>false</code> otherwise
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT#VIRTUAL
+ * @see DWT#SetData
+ *
+ * @since 3.2
+ */
+public void clear (int index, bool all) {
+    checkWidget ();
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    if (hItem is 0) error (DWT.ERROR_INVALID_RANGE);
+    hItem = findItem (hItem, index);
+    if (hItem is 0) error (DWT.ERROR_INVALID_RANGE);
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    clear (hItem, tvItem);
+    if (all) {
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
+        clearAll (hItem, tvItem, all);
+    }
+}
+
+void clear (int hItem, TVITEM tvItem) {
+    tvItem.hItem = hItem;
+    TreeItem item = null;
+    if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) !is 0) {
+        item = tvItem.lParam !is -1 ? items [tvItem.lParam] : null;
+    }
+    if (item !is null) {
+        item.clear ();
+        item.redraw ();
+    }
+}
+
+/**
+ * Clears all the items in the receiver. The text, icon and other
+ * attributes of the items are set to their default values. If the
+ * tree was created with the <code>DWT.VIRTUAL</code> style, these
+ * attributes are requested again as needed.
+ *
+ * @param all <code>true</code> if all child items should be cleared
+ * recursively, and <code>false</code> otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT#VIRTUAL
+ * @see DWT#SetData
+ *
+ * @since 3.2
+ */
+public void clearAll (bool all) {
+    checkWidget ();
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    if (hItem is 0) return;
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    clearAll (hItem, tvItem, all);
+}
+
+void clearAll (int hItem, TVITEM tvItem, bool all) {
+    while (hItem !is 0) {
+        clear (hItem, tvItem);
+        int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
+        if (all) clearAll (hFirstItem, tvItem, all);
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+    }
+}
+
+int CompareFunc (int lParam1, int lParam2, int lParamSort) {
+    TreeItem item1 = items [lParam1], item2 = items [lParam2];
+    String text1 = item1.getText (lParamSort), text2 = item2.getText (lParamSort);
+    return sortDirection is DWT.UP ? text1.compareTo (text2) : text2.compareTo (text1);
+}
+
+public Point computeSize (int wHint, int hHint, bool changed) {
+    checkWidget ();
+    int width = 0, height = 0;
+    if (hwndHeader !is 0) {
+        HDITEM hdItem = new HDITEM ();
+        hdItem.mask = OS.HDI_WIDTH;
+        int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+        for (int i=0; i<count; i++) {
+            OS.SendMessage (hwndHeader, OS.HDM_GETITEM, i, hdItem);
+            width += hdItem.cxy;
+        }
+        RECT rect = new RECT ();
+        OS.GetWindowRect (hwndHeader, rect);
+        height += rect.bottom - rect.top;
+    }
+    RECT rect = new RECT ();
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    while (hItem !is 0) {
+        if ((style & DWT.VIRTUAL) is 0 && !painted) {
+            TVITEM tvItem = new TVITEM ();
+            tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT;
+            tvItem.hItem = hItem;
+            tvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+            ignoreCustomDraw = true;
+            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+            ignoreCustomDraw = false;
+        }
+        rect.left = hItem;
+        if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, rect) !is 0) {
+            width = Math.max (width, rect.right);
+            height += rect.bottom - rect.top;
+        }
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+    }
+    if (width is 0) width = DEFAULT_WIDTH;
+    if (height is 0) height = DEFAULT_HEIGHT;
+    if (wHint !is DWT.DEFAULT) width = wHint;
+    if (hHint !is DWT.DEFAULT) height = hHint;
+    int border = getBorderWidth ();
+    width += border * 2;
+    height += border * 2;
+    if ((style & DWT.V_SCROLL) !is 0) {
+        width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
+    }
+    if ((style & DWT.H_SCROLL) !is 0) {
+        height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
+    }
+    return new Point (width, height);
+}
+
+void createHandle () {
+    super.createHandle ();
+    state &= ~(CANVAS | THEME_BACKGROUND);
+
+    /* Use the Explorer theme */
+    if (EXPLORER_THEME) {
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+            explorerTheme = true;
+            OS.SetWindowTheme (handle, Display.EXPLORER, null);
+            int bits = OS.TVS_EX_DOUBLEBUFFER | OS.TVS_EX_FADEINOUTEXPANDOS | OS.TVS_EX_RICHTOOLTIP;
+            if ((style & DWT.FULL_SELECTION) is 0) bits |= OS.TVS_EX_AUTOHSCROLL;
+            OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, 0, bits);
+            /*
+            * Bug in Windows.  When the tree is using the explorer
+            * theme, it does not use COLOR_WINDOW_TEXT for the
+            * default foreground color.  The fix is to explicitly
+            * set the foreground.
+            */
+            setForegroundPixel (-1);
+        }
+    }
+
+    /*
+    * Feature in Windows.  In version 5.8 of COMCTL32.DLL,
+    * if the font is changed for an item, the bounds for the
+    * item are not updated, causing the text to be clipped.
+    * The fix is to detect the version of COMCTL32.DLL, and
+    * if it is one of the versions with the problem, then
+    * use version 5.00 of the control (a version that does
+    * not have the problem).  This is the recommended work
+    * around from the MSDN.
+    */
+    if (!OS.IsWinCE) {
+        if (OS.COMCTL32_MAJOR < 6) {
+            OS.SendMessage (handle, OS.CCM_SETVERSION, 5, 0);
+        }
+    }
+
+    /* Set the checkbox image list */
+    if ((style & DWT.CHECK) !is 0) setCheckboxImageList ();
+
+    /*
+    * Feature in Windows.  When the control is created,
+    * it does not use the default system font.  A new HFONT
+    * is created and destroyed when the control is destroyed.
+    * This means that a program that queries the font from
+    * this control, uses the font in another control and then
+    * destroys this control will have the font unexpectedly
+    * destroyed in the other control.  The fix is to assign
+    * the font ourselves each time the control is created.
+    * The control will not destroy a font that it did not
+    * create.
+    */
+    int hFont = OS.GetStockObject (OS.SYSTEM_FONT);
+    OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
+}
+
+void createHeaderToolTips () {
+    if (OS.IsWinCE) return;
+    if (headerToolTipHandle !is 0) return;
+    headerToolTipHandle = OS.CreateWindowEx (
+        0,
+        new TCHAR (0, OS.TOOLTIPS_CLASS, true),
+        null,
+        0,
+        OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
+        handle,
+        0,
+        OS.GetModuleHandle (null),
+        null);
+    if (headerToolTipHandle is 0) error (DWT.ERROR_NO_HANDLES);
+    /*
+    * Feature in Windows.  Despite the fact that the
+    * tool tip text contains \r\n, the tooltip will
+    * not honour the new line unless TTM_SETMAXTIPWIDTH
+    * is set.  The fix is to set TTM_SETMAXTIPWIDTH to
+    * a large value.
+    */
+    OS.SendMessage (headerToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
+}
+
+void createItem (TreeColumn column, int index) {
+    if (hwndHeader is 0) createParent ();
+    int columnCount = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= index && index <= columnCount)) error (DWT.ERROR_INVALID_RANGE);
+    if (columnCount is columns.length) {
+        TreeColumn [] newColumns = new TreeColumn [columns.length + 4];
+        System.arraycopy (columns, 0, newColumns, 0, columns.length);
+        columns = newColumns;
+    }
+    for (int i=0; i<items.length; i++) {
+        TreeItem item = items [i];
+        if (item !is null) {
+            String [] strings = item.strings;
+            if (strings !is null) {
+                String [] temp = new String [columnCount + 1];
+                System.arraycopy (strings, 0, temp, 0, index);
+                System.arraycopy (strings, index, temp, index + 1, columnCount - index);
+                item.strings = temp;
+            }
+            Image [] images = item.images;
+            if (images !is null) {
+                Image [] temp = new Image [columnCount + 1];
+                System.arraycopy (images, 0, temp, 0, index);
+                System.arraycopy (images, index, temp, index + 1, columnCount - index);
+                item.images = temp;
+            }
+            if (index is 0) {
+                if (columnCount !is 0) {
+                    if (strings is null) {
+                        item.strings = new String [columnCount + 1];
+                        item.strings [1] = item.text;
+                    }
+                    item.text = "";
+                    if (images is null) {
+                        item.images = new Image [columnCount + 1];
+                        item.images [1] = item.image;
+                    }
+                    item.image = null;
+                }
+            }
+            if (item.cellBackground !is null) {
+                int [] cellBackground = item.cellBackground;
+                int [] temp = new int [columnCount + 1];
+                System.arraycopy (cellBackground, 0, temp, 0, index);
+                System.arraycopy (cellBackground, index, temp, index + 1, columnCount - index);
+                temp [index] = -1;
+                item.cellBackground = temp;
+            }
+            if (item.cellForeground !is null) {
+                int [] cellForeground = item.cellForeground;
+                int [] temp = new int [columnCount + 1];
+                System.arraycopy (cellForeground, 0, temp, 0, index);
+                System.arraycopy (cellForeground, index, temp, index + 1, columnCount - index);
+                temp [index] = -1;
+                item.cellForeground = temp;
+            }
+            if (item.cellFont !is null) {
+                int [] cellFont = item.cellFont;
+                int [] temp = new int [columnCount + 1];
+                System.arraycopy (cellFont, 0, temp, 0, index);
+                System.arraycopy (cellFont, index, temp, index + 1, columnCount- index);
+                temp [index] = -1;
+                item.cellFont = temp;
+            }
+        }
+    }
+    System.arraycopy (columns, index, columns, index + 1, columnCount - index);
+    columns [index] = column;
+
+    /*
+    * Bug in Windows.  For some reason, when HDM_INSERTITEM
+    * is used to insert an item into a header without text,
+    * if is not possible to set the text at a later time.
+    * The fix is to insert the item with an empty string.
+    */
+    int hHeap = OS.GetProcessHeap ();
+    int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
+    HDITEM hdItem = new HDITEM ();
+    hdItem.mask = OS.HDI_TEXT | OS.HDI_FORMAT;
+    hdItem.pszText = pszText;
+    if ((column.style & DWT.LEFT) is DWT.LEFT) hdItem.fmt = OS.HDF_LEFT;
+    if ((column.style & DWT.CENTER) is DWT.CENTER) hdItem.fmt = OS.HDF_CENTER;
+    if ((column.style & DWT.RIGHT) is DWT.RIGHT) hdItem.fmt = OS.HDF_RIGHT;
+    OS.SendMessage (hwndHeader, OS.HDM_INSERTITEM, index, hdItem);
+    if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+
+    /* When the first column is created, hide the horizontal scroll bar */
+    if (columnCount is 0) {
+        scrollWidth = 0;
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        bits |= OS.TVS_NOHSCROLL;
+        OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+        /*
+        * Bug in Windows.  When TVS_NOHSCROLL is set after items
+        * have been inserted into the tree, Windows shows the
+        * scroll bar.  The fix is to check for this case and
+        * explicitly hide the scroll bar explicitly.
+        */
+        int count = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
+        if (count !is 0) {
+            if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false);
+        }
+    }
+    setScrollWidth ();
+    updateImageList ();
+    updateScrollBar ();
+
+    /* Redraw to hide the items when the first column is created */
+    if (columnCount is 0 && OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0) !is 0) {
+        OS.InvalidateRect (handle, null, true);
+    }
+
+    /* Add the tool tip item for the header */
+    if (headerToolTipHandle !is 0) {
+        RECT rect = new RECT ();
+        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) !is 0) {
+            TOOLINFO lpti = new TOOLINFO ();
+            lpti.cbSize = TOOLINFO.sizeof;
+            lpti.uFlags = OS.TTF_SUBCLASS;
+            lpti.hwnd = hwndHeader;
+            lpti.uId = column.id = display.nextToolTipId++;
+            lpti.left = rect.left;
+            lpti.top = rect.top;
+            lpti.right = rect.right;
+            lpti.bottom = rect.bottom;
+            lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
+            OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
+        }
+    }
+}
+
+void createItem (TreeItem item, int hParent, int hInsertAfter, int hItem) {
+    int id = -1;
+    if (item !is null) {
+        id = lastID < items.length ? lastID : 0;
+        while (id < items.length && items [id] !is null) id++;
+        if (id is items.length) {
+            /*
+            * Grow the array faster when redraw is off or the
+            * table is not visible.  When the table is painted,
+            * the items array is resized to be smaller to reduce
+            * memory usage.
+            */
+            int length = 0;
+            if (drawCount is 0 && OS.IsWindowVisible (handle)) {
+                length = items.length + 4;
+            } else {
+                shrink = true;
+                length = Math.max (4, items.length * 3 / 2);
+            }
+            TreeItem [] newItems = new TreeItem [length];
+            System.arraycopy (items, 0, newItems, 0, items.length);
+            items = newItems;
+        }
+        lastID = id + 1;
+    }
+    int hNewItem = 0;
+    int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent);
+    bool fixParent = hFirstItem is 0;
+    if (hItem is 0) {
+        TVINSERTSTRUCT tvInsert = new TVINSERTSTRUCT ();
+        tvInsert.hParent = hParent;
+        tvInsert.hInsertAfter = hInsertAfter;
+        tvInsert.lParam = id;
+        tvInsert.pszText = OS.LPSTR_TEXTCALLBACK;
+        tvInsert.iImage = tvInsert.iSelectedImage = OS.I_IMAGECALLBACK;
+        tvInsert.mask = OS.TVIF_TEXT | OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE | OS.TVIF_HANDLE | OS.TVIF_PARAM;
+        if ((style & DWT.CHECK) !is 0) {
+            tvInsert.mask = tvInsert.mask | OS.TVIF_STATE;
+            tvInsert.state = 1 << 12;
+            tvInsert.stateMask = OS.TVIS_STATEIMAGEMASK;
+        }
+        ignoreCustomDraw = true;
+        hNewItem = OS.SendMessage (handle, OS.TVM_INSERTITEM, 0, tvInsert);
+        ignoreCustomDraw = false;
+        if (hNewItem is 0) error (DWT.ERROR_ITEM_NOT_ADDED);
+        /*
+        * This code is intentionally commented.
+        */
+//      if (hParent !is 0) {
+//          int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+//          bits |= OS.TVS_LINESATROOT;
+//          OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+//      }
+    } else {
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+        tvItem.hItem = hNewItem = hItem;
+        tvItem.lParam = id;
+        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+    }
+    if (item !is null) {
+        item.handle = hNewItem;
+        items [id] = item;
+    }
+    if (hFirstItem is 0) {
+        switch (hInsertAfter) {
+            case OS.TVI_FIRST:
+            case OS.TVI_LAST:
+                hFirstIndexOf = hLastIndexOf = hFirstItem = hNewItem;
+                itemCount = lastIndexOf = 0;
+        }
+    }
+    if (hFirstItem is hFirstIndexOf && itemCount !is -1) itemCount++;
+    if (hItem is 0) {
+        /*
+        * Bug in Windows.  When a child item is added to a parent item
+        * that has no children outside of WM_NOTIFY with control code
+        * TVN_ITEMEXPANDED, the tree widget does not redraw the + / -
+        * indicator.  The fix is to detect the case when the first
+        * child is added to a visible parent item and redraw the parent.
+        */
+        if (fixParent) {
+            if (drawCount is 0 && OS.IsWindowVisible (handle)) {
+                RECT rect = new RECT ();
+                rect.left = hParent;
+                if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect) !is 0) {
+                    OS.InvalidateRect (handle, rect, true);
+                }
+            }
+        }
+        /*
+        * Bug in Windows.  When a new item is added while Windows
+        * is requesting data a tree item using TVN_GETDISPINFO,
+        * outstanding damage for items that are below the new item
+        * is not scrolled.  The fix is to explicitly damage the
+        * new area.
+        */
+        if ((style & DWT.VIRTUAL) !is 0) {
+            if (currentItem !is null) {
+                RECT rect = new RECT ();
+                rect.left = hNewItem;
+                if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect) !is 0) {
+                    RECT damageRect = new RECT ();
+                    bool damaged = OS.GetUpdateRect (handle, damageRect, true);
+                    if (damaged && damageRect.top < rect.bottom) {
+                        if (OS.IsWinCE) {
+                            OS.OffsetRect (damageRect, 0, rect.bottom - rect.top);
+                            OS.InvalidateRect (handle, damageRect, true);
+                        } else {
+                            int rgn = OS.CreateRectRgn (0, 0, 0, 0);
+                            int result = OS.GetUpdateRgn (handle, rgn, true);
+                            if (result !is OS.NULLREGION) {
+                                OS.OffsetRgn (rgn, 0, rect.bottom - rect.top);
+                                OS.InvalidateRgn (handle, rgn, true);
+                            }
+                            OS.DeleteObject (rgn);
+                        }
+                    }
+                }
+            }
+        }
+        updateScrollBar ();
+    }
+}
+
+void createItemToolTips () {
+    if (OS.IsWinCE) return;
+    if (itemToolTipHandle !is 0) return;
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    bits |= OS.TVS_NOTOOLTIPS;
+    OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+    itemToolTipHandle = OS.CreateWindowEx (
+        0,
+        new TCHAR (0, OS.TOOLTIPS_CLASS, true),
+        null,
+        0,
+        OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
+        handle,
+        0,
+        OS.GetModuleHandle (null),
+        null);
+    if (itemToolTipHandle is 0) error (DWT.ERROR_NO_HANDLES);
+    /*
+    * Feature in Windows.  Despite the fact that the
+    * tool tip text contains \r\n, the tooltip will
+    * not honour the new line unless TTM_SETMAXTIPWIDTH
+    * is set.  The fix is to set TTM_SETMAXTIPWIDTH to
+    * a large value.
+    */
+    OS.SendMessage (itemToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
+    TOOLINFO lpti = new TOOLINFO ();
+    lpti.cbSize = TOOLINFO.sizeof;
+    lpti.hwnd = handle;
+    lpti.uId = handle;
+    lpti.uFlags = OS.TTF_SUBCLASS | OS.TTF_TRANSPARENT;
+    lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
+    OS.SendMessage (itemToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
+}
+
+void createParent () {
+    forceResize ();
+    RECT rect = new RECT ();
+    OS.GetWindowRect (handle, rect);
+    OS.MapWindowPoints (0, parent.handle, rect, 2);
+    int oldStyle = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    int newStyle = super.widgetStyle () & ~OS.WS_VISIBLE;
+    if ((oldStyle & OS.WS_DISABLED) !is 0) newStyle |= OS.WS_DISABLED;
+//  if ((oldStyle & OS.WS_VISIBLE) !is 0) newStyle |= OS.WS_VISIBLE;
+    hwndParent = OS.CreateWindowEx (
+        super.widgetExtStyle (),
+        super.windowClass (),
+        null,
+        newStyle,
+        rect.left,
+        rect.top,
+        rect.right - rect.left,
+        rect.bottom - rect.top,
+        parent.handle,
+        0,
+        OS.GetModuleHandle (null),
+        null);
+    if (hwndParent is 0) error (DWT.ERROR_NO_HANDLES);
+    OS.SetWindowLong (hwndParent, OS.GWL_ID, hwndParent);
+    int bits = 0;
+    if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
+        bits |= OS.WS_EX_NOINHERITLAYOUT;
+        if ((style & DWT.RIGHT_TO_LEFT) !is 0) bits |= OS.WS_EX_LAYOUTRTL;
+    }
+    hwndHeader = OS.CreateWindowEx (
+        bits,
+        HeaderClass,
+        null,
+        OS.HDS_BUTTONS | OS.HDS_FULLDRAG | OS.HDS_DRAGDROP | OS.HDS_HIDDEN | OS.WS_CHILD | OS.WS_CLIPSIBLINGS,
+        0, 0, 0, 0,
+        hwndParent,
+        0,
+        OS.GetModuleHandle (null),
+        null);
+    if (hwndHeader is 0) error (DWT.ERROR_NO_HANDLES);
+    OS.SetWindowLong (hwndHeader, OS.GWL_ID, hwndHeader);
+    if (OS.IsDBLocale) {
+        int hIMC = OS.ImmGetContext (handle);
+        OS.ImmAssociateContext (hwndParent, hIMC);
+        OS.ImmAssociateContext (hwndHeader, hIMC);
+        OS.ImmReleaseContext (handle, hIMC);
+    }
+    //This code is intentionally commented
+//  if (!OS.IsPPC) {
+//      if ((style & DWT.BORDER) !is 0) {
+//          int oldExStyle = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+//          oldExStyle &= ~OS.WS_EX_CLIENTEDGE;
+//          OS.SetWindowLong (handle, OS.GWL_EXSTYLE, oldExStyle);
+//      }
+//  }
+    int hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+    if (hFont !is 0) OS.SendMessage (hwndHeader, OS.WM_SETFONT, hFont, 0);
+    int hwndInsertAfter = OS.GetWindow (handle, OS.GW_HWNDPREV);
+    int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
+    SetWindowPos (hwndParent, hwndInsertAfter, 0, 0, 0, 0, flags);
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_RANGE | OS.SIF_PAGE;
+    OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info);
+    info.nPage = info.nMax + 1;
+    OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
+    OS.GetScrollInfo (hwndParent, OS.SB_VERT, info);
+    info.nPage = info.nMax + 1;
+    OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
+    customDraw = true;
+    deregister ();
+    if ((oldStyle & OS.WS_VISIBLE) !is 0) {
+        OS.ShowWindow (hwndParent, OS.SW_SHOW);
+    }
+    int hwndFocus = OS.GetFocus ();
+    if (hwndFocus is handle) OS.SetFocus (hwndParent);
+    OS.SetParent (handle, hwndParent);
+    if (hwndFocus is handle) OS.SetFocus (handle);
+    register ();
+    subclass ();
+    createItemToolTips ();
+}
+
+void createWidget () {
+    super.createWidget ();
+    items = new TreeItem [4];
+    columns = new TreeColumn [4];
+    itemCount = -1;
+}
+
+int defaultBackground () {
+    return OS.GetSysColor (OS.COLOR_WINDOW);
+}
+
+void deregister () {
+    super.deregister ();
+    if (hwndParent !is 0) display.removeControl (hwndParent);
+    if (hwndHeader !is 0) display.removeControl (hwndHeader);
+}
+
+void deselect (int hItem, TVITEM tvItem, int hIgnoreItem) {
+    while (hItem !is 0) {
+        if (hItem !is hIgnoreItem) {
+            tvItem.hItem = hItem;
+            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+        }
+        int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
+        deselect (hFirstItem, tvItem, hIgnoreItem);
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+    }
+}
+
+/**
+ * Deselects all selected items in the receiver.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselectAll () {
+    checkWidget ();
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+    tvItem.stateMask = OS.TVIS_SELECTED;
+    if ((style & DWT.SINGLE) !is 0) {
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+        if (hItem !is 0) {
+            tvItem.hItem = hItem;
+            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+        }
+    } else {
+        int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC);
+        OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc);
+        if ((style & DWT.VIRTUAL) !is 0) {
+            int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+            deselect (hItem, tvItem, 0);
+        } else {
+            for (int i=0; i<items.length; i++) {
+                TreeItem item = items [i];
+                if (item !is null) {
+                    tvItem.hItem = item.handle;
+                    OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                }
+            }
+        }
+        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc);
+    }
+}
+
+void destroyItem (TreeColumn column) {
+    if (hwndHeader is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
+    int columnCount = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    int index = 0;
+    while (index < columnCount) {
+        if (columns [index] is column) break;
+        index++;
+    }
+    int [] oldOrder = new int [columnCount];
+    OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, oldOrder);
+    int orderIndex = 0;
+    while (orderIndex < columnCount) {
+        if (oldOrder [orderIndex] is index) break;
+        orderIndex++;
+    }
+    RECT headerRect = new RECT ();
+    OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+    if (OS.SendMessage (hwndHeader, OS.HDM_DELETEITEM, index, 0) is 0) {
+        error (DWT.ERROR_ITEM_NOT_REMOVED);
+    }
+    System.arraycopy (columns, index + 1, columns, index, --columnCount - index);
+    columns [columnCount] = null;
+    for (int i=0; i<items.length; i++) {
+        TreeItem item = items [i];
+        if (item !is null) {
+            if (columnCount is 0) {
+                item.strings = null;
+                item.images = null;
+                item.cellBackground = null;
+                item.cellForeground = null;
+                item.cellFont = null;
+            } else {
+                if (item.strings !is null) {
+                    String [] strings = item.strings;
+                    if (index is 0) {
+                        item.text = strings [1] !is null ? strings [1] : "";
+                    }
+                    String [] temp = new String [columnCount];
+                    System.arraycopy (strings, 0, temp, 0, index);
+                    System.arraycopy (strings, index + 1, temp, index, columnCount - index);
+                    item.strings = temp;
+                } else {
+                    if (index is 0) item.text = "";
+                }
+                if (item.images !is null) {
+                    Image [] images = item.images;
+                    if (index is 0) item.image = images [1];
+                    Image [] temp = new Image [columnCount];
+                    System.arraycopy (images, 0, temp, 0, index);
+                    System.arraycopy (images, index + 1, temp, index, columnCount - index);
+                    item.images = temp;
+                } else {
+                    if (index is 0) item.image = null;
+                }
+                if (item.cellBackground !is null) {
+                    int [] cellBackground = item.cellBackground;
+                    int [] temp = new int [columnCount];
+                    System.arraycopy (cellBackground, 0, temp, 0, index);
+                    System.arraycopy (cellBackground, index + 1, temp, index, columnCount - index);
+                    item.cellBackground = temp;
+                }
+                if (item.cellForeground !is null) {
+                    int [] cellForeground = item.cellForeground;
+                    int [] temp = new int [columnCount];
+                    System.arraycopy (cellForeground, 0, temp, 0, index);
+                    System.arraycopy (cellForeground, index + 1, temp, index, columnCount - index);
+                    item.cellForeground = temp;
+                }
+                if (item.cellFont !is null) {
+                    int [] cellFont = item.cellFont;
+                    int [] temp = new int [columnCount];
+                    System.arraycopy (cellFont, 0, temp, 0, index);
+                    System.arraycopy (cellFont, index + 1, temp, index, columnCount - index);
+                    item.cellFont = temp;
+                }
+            }
+        }
+    }
+
+    /*
+    * When the last column is deleted, show the horizontal
+    * scroll bar.  Otherwise, left align the first column
+    * and redraw the columns to the right.
+    */
+    if (columnCount is 0) {
+        scrollWidth = 0;
+        if (!hooks (DWT.MeasureItem)) {
+            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+            bits &= ~OS.TVS_NOHSCROLL;
+            OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+            OS.InvalidateRect (handle, null, true);
+        }
+    } else {
+        if (index is 0) {
+            columns [0].style &= ~(DWT.LEFT | DWT.RIGHT | DWT.CENTER);
+            columns [0].style |= DWT.LEFT;
+            HDITEM hdItem = new HDITEM ();
+            hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE;
+            OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+            hdItem.fmt &= ~OS.HDF_JUSTIFYMASK;
+            hdItem.fmt |= OS.HDF_LEFT;
+            OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
+        }
+        RECT rect = new RECT ();
+        OS.GetClientRect (handle, rect);
+        rect.left = headerRect.left;
+        OS.InvalidateRect (handle, rect, true);
+    }
+    setScrollWidth ();
+    updateImageList ();
+    updateScrollBar ();
+    if (columnCount !is 0) {
+        int [] newOrder = new int [columnCount];
+        OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, newOrder);
+        TreeColumn [] newColumns = new TreeColumn [columnCount - orderIndex];
+        for (int i=orderIndex; i<newOrder.length; i++) {
+            newColumns [i - orderIndex] = columns [newOrder [i]];
+            newColumns [i - orderIndex].updateToolTip (newOrder [i]);
+        }
+        for (int i=0; i<newColumns.length; i++) {
+            if (!newColumns [i].isDisposed ()) {
+                newColumns [i].sendEvent (DWT.Move);
+            }
+        }
+    }
+
+    /* Remove the tool tip item for the header */
+    if (headerToolTipHandle !is 0) {
+        TOOLINFO lpti = new TOOLINFO ();
+        lpti.cbSize = TOOLINFO.sizeof;
+        lpti.uId = column.id;
+        lpti.hwnd = hwndHeader;
+        OS.SendMessage (headerToolTipHandle, OS.TTM_DELTOOL, 0, lpti);
+    }
+}
+
+void destroyItem (TreeItem item, int hItem) {
+    hFirstIndexOf = hLastIndexOf = 0;
+    itemCount = -1;
+    /*
+    * Feature in Windows.  When an item is removed that is not
+    * visible in the tree because it belongs to a collapsed branch,
+    * Windows redraws the tree causing a flash for each item that
+    * is removed.  The fix is to detect whether the item is visible,
+    * force the widget to be fully painted, turn off redraw, remove
+    * the item and validate the damage caused by the removing of
+    * the item.
+    *
+    * NOTE: This fix is not necessary when double buffering and
+    * can cause problems for virtual trees due to the call to
+    * UpdateWindow() that flushes outstanding WM_PAINT events,
+    * allowing application code to add or remove items during
+    * this remove operation.
+    */
+    int hParent = 0;
+    bool fixRedraw = false;
+    if ((style & DWT.DOUBLE_BUFFERED) is 0) {
+        if (drawCount is 0 && OS.IsWindowVisible (handle)) {
+            RECT rect = new RECT ();
+            rect.left = hItem;
+            fixRedraw = OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect) is 0;
+        }
+    }
+    if (fixRedraw) {
+        hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
+        OS.UpdateWindow (handle);
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+        /*
+        * This code is intentionally commented.
+        */
+//      OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+    }
+    if ((style & DWT.MULTI) !is 0) {
+        ignoreDeselect = ignoreSelect = lockSelection = true;
+    }
+
+    /*
+    * Feature in Windows.  When an item is deleted and a tool tip
+    * is showing, Window requests the new text for the new item
+    * that is under the cursor right away.  This means that when
+    * multiple items are deleted, the tool tip flashes, showing
+    * each new item in the tool tip as it is scrolled into view.
+    * The fix is to hide tool tips when any item is deleted.
+    *
+    * NOTE:  This only happens on Vista.
+    */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        int hwndToolTip = OS.SendMessage (handle, OS.TVM_GETTOOLTIPS, 0, 0);
+        if (hwndToolTip !is 0) OS.SendMessage (hwndToolTip, OS.TTM_POP, 0 ,0);
+    }
+
+    shrink = ignoreShrink = true;
+    OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, hItem);
+    ignoreShrink = false;
+    if ((style & DWT.MULTI) !is 0) {
+        ignoreDeselect = ignoreSelect = lockSelection = false;
+    }
+    if (fixRedraw) {
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+        OS.ValidateRect (handle, null);
+        /*
+        * If the item that was deleted was the last child of a tree item that
+        * is visible, redraw the parent item to force the + / - to be updated.
+        */
+        if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent) is 0) {
+            RECT rect = new RECT ();
+            rect.left = hParent;
+            if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect) !is 0) {
+                OS.InvalidateRect (handle, rect, true);
+            }
+        }
+    }
+    int count = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
+    if (count is 0) {
+        if (imageList !is null) {
+            OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0);
+            display.releaseImageList (imageList);
+        }
+        imageList = null;
+        if (hwndParent is 0 && !linesVisible) {
+            if (!hooks (DWT.MeasureItem) && !hooks (DWT.EraseItem) && !hooks (DWT.PaintItem)) {
+                customDraw = false;
+            }
+        }
+        items = new TreeItem [4];
+        scrollWidth = 0;
+        setScrollWidth ();
+    }
+    updateScrollBar ();
+}
+
+void enableDrag (bool enabled) {
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if (enabled && hooks (DWT.DragDetect)) {
+        bits &= ~OS.TVS_DISABLEDRAGDROP;
+    } else {
+        bits |= OS.TVS_DISABLEDRAGDROP;
+    }
+    OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
+}
+
+void enableWidget (bool enabled) {
+    super.enableWidget (enabled);
+    /*
+    * Feature in Windows.  When a tree is given a background color
+    * using TVM_SETBKCOLOR and the tree is disabled, Windows draws
+    * the tree using the background color rather than the disabled
+    * colors.  This is different from the table which draws grayed.
+    * The fix is to set the default background color while the tree
+    * is disabled and restore it when enabled.
+    */
+    Control control = findBackgroundControl ();
+    /*
+    * Bug in Windows.  On Vista only, Windows does not draw using
+    * the background color when the tree is disabled.  The fix is
+    * to set the default color, even when the color has not been
+    * changed, causing Windows to draw correctly.
+    */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        if (control is null) control = this;
+    }
+    if (control !is null) {
+        if (control.backgroundImage is null) {
+            _setBackgroundPixel (enabled ? control.getBackgroundPixel () : -1);
+        }
+    }
+    if (hwndParent !is 0) OS.EnableWindow (hwndParent, enabled);
+
+    /*
+    * Feature in Windows.  When the tree has the style
+    * TVS_FULLROWSELECT, the background color for the
+    * entire row is filled when an item is painted,
+    * drawing on top of the sort column color.  The fix
+    * is to clear TVS_FULLROWSELECT when a their is
+    * as sort column.
+    */
+    updateFullSelection ();
+}
+
+int findIndex (int hFirstItem, int hItem) {
+    if (hFirstItem is 0) return -1;
+    if (hFirstItem is hFirstIndexOf) {
+        if (hFirstIndexOf is hItem) {
+            hLastIndexOf = hFirstIndexOf;
+            return lastIndexOf = 0;
+        }
+        if (hLastIndexOf is hItem) return lastIndexOf;
+        int hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf);
+        if (hPrevItem is hItem) {
+            hLastIndexOf = hPrevItem;
+            return --lastIndexOf;
+        }
+        int hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf);
+        if (hNextItem is hItem) {
+            hLastIndexOf = hNextItem;
+            return ++lastIndexOf;
+        }
+        int previousIndex = lastIndexOf - 1;
+        while (hPrevItem !is 0 && hPrevItem !is hItem) {
+            hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hPrevItem);
+            --previousIndex;
+        }
+        if (hPrevItem is hItem) {
+            hLastIndexOf = hPrevItem;
+            return lastIndexOf = previousIndex;
+        }
+        int nextIndex = lastIndexOf + 1;
+        while (hNextItem !is 0 && hNextItem !is hItem) {
+            hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem);
+            nextIndex++;
+        }
+        if (hNextItem is hItem) {
+            hLastIndexOf = hNextItem;
+            return lastIndexOf = nextIndex;
+        }
+        return -1;
+    }
+    int index = 0, hNextItem = hFirstItem;
+    while (hNextItem !is 0 && hNextItem !is hItem) {
+        hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem);
+        index++;
+    }
+    if (hNextItem is hItem) {
+        itemCount = -1;
+        hFirstIndexOf = hFirstItem;
+        hLastIndexOf = hNextItem;
+        return lastIndexOf = index;
+    }
+    return -1;
+}
+
+Widget findItem (int hItem) {
+    return _getItem (hItem);
+}
+
+int findItem (int hFirstItem, int index) {
+    if (hFirstItem is 0) return 0;
+    if (hFirstItem is hFirstIndexOf) {
+        if (index is 0) {
+            lastIndexOf = 0;
+            return hLastIndexOf = hFirstIndexOf;
+        }
+        if (lastIndexOf is index) return hLastIndexOf;
+        if (lastIndexOf - 1 is index) {
+            --lastIndexOf;
+            return hLastIndexOf = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf);
+        }
+        if (lastIndexOf + 1 is index) {
+            lastIndexOf++;
+            return hLastIndexOf = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf);
+        }
+        if (index < lastIndexOf) {
+            int previousIndex = lastIndexOf - 1;
+            int hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf);
+            while (hPrevItem !is 0 && index < previousIndex) {
+                hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hPrevItem);
+                --previousIndex;
+            }
+            if (index is previousIndex) {
+                lastIndexOf = previousIndex;
+                return hLastIndexOf = hPrevItem;
+            }
+        } else {
+            int nextIndex = lastIndexOf + 1;
+            int hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf);
+            while (hNextItem !is 0 && nextIndex < index) {
+                hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem);
+                nextIndex++;
+            }
+            if (index is nextIndex) {
+                lastIndexOf = nextIndex;
+                return hLastIndexOf = hNextItem;
+            }
+        }
+        return 0;
+    }
+    int nextIndex = 0, hNextItem = hFirstItem;
+    while (hNextItem !is 0 && nextIndex < index) {
+        hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem);
+        nextIndex++;
+    }
+    if (index is nextIndex) {
+        itemCount = -1;
+        lastIndexOf = nextIndex;
+        hFirstIndexOf = hFirstItem;
+        return hLastIndexOf = hNextItem;
+    }
+    return 0;
+}
+
+TreeItem getFocusItem () {
+//  checkWidget ();
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+    return hItem !is 0 ? _getItem (hItem) : null;
+}
+
+/**
+ * Returns the width in pixels of a grid line.
+ *
+ * @return the width of a grid line in pixels
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getGridLineWidth () {
+    checkWidget ();
+    return GRID_WIDTH;
+}
+
+/**
+ * Returns the height of the receiver's header
+ *
+ * @return the height of the header or zero if the header is not visible
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getHeaderHeight () {
+    checkWidget ();
+    if (hwndHeader is 0) return 0;
+    RECT rect = new RECT ();
+    OS.GetWindowRect (hwndHeader, rect);
+    return rect.bottom - rect.top;
+}
+
+/**
+ * Returns <code>true</code> if the receiver's header is visible,
+ * and <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's header's visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public bool getHeaderVisible () {
+    checkWidget ();
+    if (hwndHeader is 0) return false;
+    int bits = OS.GetWindowLong (hwndHeader, OS.GWL_STYLE);
+    return (bits & OS.WS_VISIBLE) !is 0;
+}
+
+Point getImageSize () {
+    if (imageList !is null) return imageList.getImageSize ();
+    return new Point (0, getItemHeight ());
+}
+
+int getBottomItem () {
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+    if (hItem is 0) return 0;
+    int index = 0, count = OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0);
+    while (index < count) {
+        int hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
+        if (hNextItem is 0) return hItem;
+        hItem = hNextItem;
+        index++;
+    }
+    return hItem;
+}
+
+/**
+ * Returns the column at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ * Columns are returned in the order that they were created.
+ * If no <code>TreeColumn</code>s were created by the programmer,
+ * this method will throw <code>ERROR_INVALID_RANGE</code> despite
+ * the fact that a single column of data may be visible in the tree.
+ * This occurs when the programmer uses the tree like a list, adding
+ * items but never creating a column.
+ *
+ * @param index the index of the column to return
+ * @return the column at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#getColumnOrder()
+ * @see Tree#setColumnOrder(int[])
+ * @see TreeColumn#getMoveable()
+ * @see TreeColumn#setMoveable(bool)
+ * @see DWT#Move
+ *
+ * @since 3.1
+ */
+public TreeColumn getColumn (int index) {
+    checkWidget ();
+    if (hwndHeader is 0) error (DWT.ERROR_INVALID_RANGE);
+    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    if (!(0 <= index && index < count)) error (DWT.ERROR_INVALID_RANGE);
+    return columns [index];
+}
+
+/**
+ * Returns the number of columns contained in the receiver.
+ * If no <code>TreeColumn</code>s were created by the programmer,
+ * this value is zero, despite the fact that visually, one column
+ * of items may be visible. This occurs when the programmer uses
+ * the tree like a list, adding items but never creating a column.
+ *
+ * @return the number of columns
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getColumnCount () {
+    checkWidget ();
+    if (hwndHeader is 0) return 0;
+    return OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+}
+
+/**
+ * Returns an array of zero-relative integers that map
+ * the creation order of the receiver's items to the
+ * order in which they are currently being displayed.
+ * <p>
+ * Specifically, the indices of the returned array represent
+ * the current visual order of the items, and the contents
+ * of the array represent the creation order of the items.
+ * </p><p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the current visual order of the receiver's items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#setColumnOrder(int[])
+ * @see TreeColumn#getMoveable()
+ * @see TreeColumn#setMoveable(bool)
+ * @see DWT#Move
+ *
+ * @since 3.2
+ */
+public int[] getColumnOrder () {
+    checkWidget ();
+    if (hwndHeader is 0) return new int [0];
+    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    int [] order = new int [count];
+    OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
+    return order;
+}
+
+/**
+ * Returns an array of <code>TreeColumn</code>s which are the
+ * columns in the receiver. Columns are returned in the order
+ * that they were created.  If no <code>TreeColumn</code>s were
+ * created by the programmer, the array is empty, despite the fact
+ * that visually, one column of items may be visible. This occurs
+ * when the programmer uses the tree like a list, adding items but
+ * never creating a column.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#getColumnOrder()
+ * @see Tree#setColumnOrder(int[])
+ * @see TreeColumn#getMoveable()
+ * @see TreeColumn#setMoveable(bool)
+ * @see DWT#Move
+ *
+ * @since 3.1
+ */
+public TreeColumn [] getColumns () {
+    checkWidget ();
+    if (hwndHeader is 0) return new TreeColumn [0];
+    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    TreeColumn [] result = new TreeColumn [count];
+    System.arraycopy (columns, 0, result, 0, count);
+    return result;
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public TreeItem getItem (int index) {
+    checkWidget ();
+    if (index < 0) error (DWT.ERROR_INVALID_RANGE);
+    int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    if (hFirstItem is 0) error (DWT.ERROR_INVALID_RANGE);
+    int hItem = findItem (hFirstItem, index);
+    if (hItem is 0) error (DWT.ERROR_INVALID_RANGE);
+    return _getItem (hItem);
+}
+
+TreeItem getItem (NMTVCUSTOMDRAW nmcd) {
+    /*
+    * Bug in Windows.  If the lParam field of TVITEM
+    * is changed during custom draw using TVM_SETITEM,
+    * the lItemlParam field of the NMTVCUSTOMDRAW struct
+    * is not updated until the next custom draw.  The
+    * fix is to query the field from the item instead
+    * of using the struct.
+    */
+    int id = nmcd.lItemlParam;
+    if ((style & DWT.VIRTUAL) !is 0) {
+        if (id is -1) {
+            TVITEM tvItem = new TVITEM ();
+            tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+            tvItem.hItem = nmcd.dwItemSpec;
+            OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+            id = tvItem.lParam;
+        }
+    }
+    return _getItem (nmcd.dwItemSpec, id);
+}
+
+/**
+ * Returns the item at the given point in the receiver
+ * or null if no such item exists. The point is in the
+ * coordinate system of the receiver.
+ * <p>
+ * The item that is returned represents an item that could be selected by the user.
+ * For example, if selection only occurs in items in the first column, then null is
+ * returned if the point is outside of the item.
+ * Note that the DWT.FULL_SELECTION style hint, which specifies the selection policy,
+ * determines the extent of the selection.
+ * </p>
+ *
+ * @param point the point used to locate the item
+ * @return the item at the given point, or null if the point is not in a selectable item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem getItem (Point point) {
+    checkWidget ();
+    if (point is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TVHITTESTINFO lpht = new TVHITTESTINFO ();
+    lpht.x = point.x;
+    lpht.y = point.y;
+    OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
+    if (lpht.hItem !is 0) {
+        if ((style & DWT.FULL_SELECTION) !is 0 || (lpht.flags & OS.TVHT_ONITEM) !is 0) {
+            return _getItem (lpht.hItem);
+        }
+    }
+    return null;
+}
+
+/**
+ * Returns the number of items contained in the receiver
+ * that are direct item children of the receiver.  The
+ * number that is returned is the number of roots in the
+ * tree.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    if (hItem is 0) return 0;
+    return getItemCount (hItem);
+}
+
+int getItemCount (int hItem) {
+    int count = 0, hFirstItem = hItem;
+    if (hItem is hFirstIndexOf) {
+        if (itemCount !is -1) return itemCount;
+        hFirstItem = hLastIndexOf;
+        count = lastIndexOf;
+    }
+    while (hFirstItem !is 0) {
+        hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hFirstItem);
+        count++;
+    }
+    if (hItem is hFirstIndexOf) itemCount = count;
+    return count;
+}
+
+/**
+ * Returns the height of the area which would be used to
+ * display <em>one</em> of the items in the tree.
+ *
+ * @return the height of one item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemHeight () {
+    checkWidget ();
+    return OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0);
+}
+
+/**
+ * Returns a (possibly empty) array of items contained in the
+ * receiver that are direct item children of the receiver.  These
+ * are the roots of the tree.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem [] getItems () {
+    checkWidget ();
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    if (hItem is 0) return new TreeItem [0];
+    return getItems (hItem);
+}
+
+TreeItem [] getItems (int hTreeItem) {
+    int count = 0, hItem = hTreeItem;
+    while (hItem !is 0) {
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+        count++;
+    }
+    int index = 0;
+    TreeItem [] result = new TreeItem [count];
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    tvItem.hItem = hTreeItem;
+    /*
+    * Feature in Windows.  In some cases an expand or collapse message
+    * can occur from within TVM_DELETEITEM.  When this happens, the item
+    * being destroyed has been removed from the list of items but has not
+    * been deleted from the tree.  The fix is to check for null items and
+    * remove them from the list.
+    */
+    while (tvItem.hItem !is 0) {
+        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+        TreeItem item = _getItem (tvItem.hItem, tvItem.lParam);
+        if (item !is null) result [index++] = item;
+        tvItem.hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, tvItem.hItem);
+    }
+    if (index !is count) {
+        TreeItem [] newResult = new TreeItem [index];
+        System.arraycopy (result, 0, newResult, 0, index);
+        result = newResult;
+    }
+    return result;
+}
+
+/**
+ * Returns <code>true</code> if the receiver's lines are visible,
+ * and <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the visibility state of the lines
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public bool getLinesVisible () {
+    checkWidget ();
+    return linesVisible;
+}
+
+int getNextSelection (int hItem, TVITEM tvItem) {
+    while (hItem !is 0) {
+        int state = 0;
+        if (OS.IsWinCE) {
+            tvItem.hItem = hItem;
+            OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+            state = tvItem.state;
+        } else {
+            state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
+        }
+        if ((state & OS.TVIS_SELECTED) !is 0) return hItem;
+        int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
+        int hSelected = getNextSelection (hFirstItem, tvItem);
+        if (hSelected !is 0) return hSelected;
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+    }
+    return 0;
+}
+
+/**
+ * Returns the receiver's parent item, which must be a
+ * <code>TreeItem</code> or null when the receiver is a
+ * root.
+ *
+ * @return the receiver's parent item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem getParentItem () {
+    checkWidget ();
+    return null;
+}
+
+int getSelection (int hItem, TVITEM tvItem, TreeItem [] selection, int index, int count, bool bigSelection, bool all) {
+    while (hItem !is 0) {
+        if (OS.IsWinCE || bigSelection) {
+            tvItem.hItem = hItem;
+            OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+            if ((tvItem.state & OS.TVIS_SELECTED) !is 0) {
+                if (selection !is null && index < selection.length) {
+                    selection [index] = _getItem (hItem, tvItem.lParam);
+                }
+                index++;
+            }
+        } else {
+            int state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
+            if ((state & OS.TVIS_SELECTED) !is 0) {
+                if (tvItem !is null && selection !is null && index < selection.length) {
+                    tvItem.hItem = hItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                    selection [index] = _getItem (hItem, tvItem.lParam);
+                }
+                index++;
+            }
+        }
+        if (index is count) break;
+        if (all) {
+            int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
+            if ((index = getSelection (hFirstItem, tvItem, selection, index, count, bigSelection, all)) is count) {
+                break;
+            }
+            hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+        } else {
+            hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
+        }
+    }
+    return index;
+}
+
+/**
+ * Returns an array of <code>TreeItem</code>s that are currently
+ * selected in the receiver. The order of the items is unspecified.
+ * An empty array indicates that no items are selected.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its selection, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ * @return an array representing the selection
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem [] getSelection () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) {
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+        if (hItem is 0) return new TreeItem [0];
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
+        tvItem.hItem = hItem;
+        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+        if ((tvItem.state & OS.TVIS_SELECTED) is 0) return new TreeItem [0];
+        return new TreeItem [] {_getItem (tvItem.hItem, tvItem.lParam)};
+    }
+    int count = 0;
+    TreeItem [] guess = new TreeItem [(style & DWT.VIRTUAL) !is 0 ? 8 : 1];
+    int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC);
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc);
+    if ((style & DWT.VIRTUAL) !is 0) {
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+        count = getSelection (hItem, tvItem, guess, 0, -1, false, true);
+    } else {
+        TVITEM tvItem = null;
+        if (OS.IsWinCE) {
+            tvItem = new TVITEM ();
+            tvItem.mask = OS.TVIF_STATE;
+        }
+        for (int i=0; i<items.length; i++) {
+            TreeItem item = items [i];
+            if (item !is null) {
+                int hItem = item.handle, state = 0;
+                if (OS.IsWinCE) {
+                    tvItem.hItem = hItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                    state = tvItem.state;
+                } else {
+                    state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
+                }
+                if ((state & OS.TVIS_SELECTED) !is 0) {
+                    if (count < guess.length) guess [count] = item;
+                    count++;
+                }
+            }
+        }
+    }
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc);
+    if (count is 0) return new TreeItem [0];
+    if (count is guess.length) return guess;
+    TreeItem [] result = new TreeItem [count];
+    if (count < guess.length) {
+        System.arraycopy (guess, 0, result, 0, count);
+        return result;
+    }
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc);
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    int itemCount = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
+    bool bigSelection = result.length > itemCount / 2;
+    if (count !is getSelection (hItem, tvItem, result, 0, count, bigSelection, false)) {
+        getSelection (hItem, tvItem, result, 0, count, bigSelection, true);
+    }
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc);
+    return result;
+}
+
+/**
+ * Returns the number of selected items contained in the receiver.
+ *
+ * @return the number of selected items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionCount () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) {
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+        if (hItem is 0) return 0;
+        int state = 0;
+        if (OS.IsWinCE) {
+            TVITEM tvItem = new TVITEM ();
+            tvItem.hItem = hItem;
+            tvItem.mask = OS.TVIF_STATE;
+            OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+            state = tvItem.state;
+        } else {
+            state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
+        }
+        return (state & OS.TVIS_SELECTED) is 0 ? 0 : 1;
+    }
+    int count = 0;
+    int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC);
+    TVITEM tvItem = null;
+    if (OS.IsWinCE) {
+        tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_STATE;
+    }
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc);
+    if ((style & DWT.VIRTUAL) !is 0) {
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+        count = getSelection (hItem, tvItem, null, 0, -1, false, true);
+    } else {
+        for (int i=0; i<items.length; i++) {
+            TreeItem item = items [i];
+            if (item !is null) {
+                int hItem = item.handle, state = 0;
+                if (OS.IsWinCE) {
+                    tvItem.hItem = hItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                    state = tvItem.state;
+                } else {
+                    state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
+                }
+                if ((state & OS.TVIS_SELECTED) !is 0) count++;
+            }
+        }
+    }
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc);
+    return count;
+}
+
+/**
+ * Returns the column which shows the sort indicator for
+ * the receiver. The value may be null if no column shows
+ * the sort indicator.
+ *
+ * @return the sort indicator
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setSortColumn(TreeColumn)
+ *
+ * @since 3.2
+ */
+public TreeColumn getSortColumn () {
+    checkWidget ();
+    return sortColumn;
+}
+
+int getSortColumnPixel () {
+    int pixel = OS.IsWindowEnabled (handle) ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE);
+    int red = pixel & 0xFF;
+    int green = (pixel & 0xFF00) >> 8;
+    int blue = (pixel & 0xFF0000) >> 16;
+    if (red > 240 && green > 240 && blue > 240) {
+        red -= 8;
+        green -= 8;
+        blue -= 8;
+    } else {
+        red = Math.min (0xFF, (red / 10) + red);
+        green = Math.min (0xFF, (green / 10) + green);
+        blue = Math.min (0xFF, (blue / 10) + blue);
+    }
+    return (red & 0xFF) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16);
+}
+
+/**
+ * Returns the direction of the sort indicator for the receiver.
+ * The value will be one of <code>UP</code>, <code>DOWN</code>
+ * or <code>NONE</code>.
+ *
+ * @return the sort direction
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setSortDirection(int)
+ *
+ * @since 3.2
+ */
+public int getSortDirection () {
+    checkWidget ();
+    return sortDirection;
+}
+
+/**
+ * Returns the item which is currently at the top of the receiver.
+ * This item can change when items are expanded, collapsed, scrolled
+ * or new items are added or removed.
+ *
+ * @return the item at the top of the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public TreeItem getTopItem () {
+    checkWidget ();
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+    return hItem !is 0 ? _getItem (hItem) : null;
+}
+
+int imageIndex (Image image, int index) {
+    if (image is null) return OS.I_IMAGENONE;
+    if (imageList is null) {
+        Rectangle bounds = image.getBounds ();
+        imageList = display.getImageList (style & DWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
+    }
+    int imageIndex = imageList.indexOf (image);
+    if (imageIndex is -1) imageIndex = imageList.add (image);
+    if (hwndHeader is 0 || OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0) is index) {
+        int hImageList = imageList.getHandle ();
+        int hOldImageList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0);
+        if (hOldImageList !is hImageList) {
+            OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList);
+            updateScrollBar ();
+        }
+    }
+    return imageIndex;
+}
+
+int imageIndexHeader (Image image) {
+    if (image is null) return OS.I_IMAGENONE;
+    if (headerImageList is null) {
+        Rectangle bounds = image.getBounds ();
+        headerImageList = display.getImageList (style & DWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
+        int index = headerImageList.indexOf (image);
+        if (index is -1) index = headerImageList.add (image);
+        int hImageList = headerImageList.getHandle ();
+        if (hwndHeader !is 0) {
+            OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hImageList);
+        }
+        updateScrollBar ();
+        return index;
+    }
+    int index = headerImageList.indexOf (image);
+    if (index !is -1) return index;
+    return headerImageList.add (image);
+}
+
+/**
+ * Searches the receiver's list starting at the first column
+ * (index 0) until a column is found that is equal to the
+ * argument, and returns the index of that column. If no column
+ * is found, returns -1.
+ *
+ * @param column the search column
+ * @return the index of the column
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the column is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int indexOf (TreeColumn column) {
+    checkWidget ();
+    if (column is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (column.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    if (hwndHeader is 0) return -1;
+    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<count; i++) {
+        if (columns [i] is column) return i;
+    }
+    return -1;
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int indexOf (TreeItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (item.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    return hItem is 0 ? -1 : findIndex (hItem, item.handle);
+}
+
+bool isItemSelected (NMTVCUSTOMDRAW nmcd) {
+    bool selected = false;
+    if (OS.IsWindowEnabled (handle)) {
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+        tvItem.hItem = nmcd.dwItemSpec;
+        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+        if ((tvItem.state & (OS.TVIS_SELECTED | OS.TVIS_DROPHILITED)) !is 0) {
+            selected = true;
+            /*
+            * Feature in Windows.  When the mouse is pressed and the
+            * selection is first drawn for a tree, the previously
+            * selected item is redrawn but the the TVIS_SELECTED bits
+            * are not cleared.  When the user moves the mouse slightly
+            * and a drag and drop operation is not started, the item is
+            * drawn again and this time with TVIS_SELECTED is cleared.
+            * This means that an item that contains colored cells will
+            * not draw with the correct background until the mouse is
+            * moved.  The fix is to test for the selection colors and
+            * guess that the item is not selected.
+            *
+            * NOTE: This code does not work when the foreground and
+            * background of the tree are set to the selection colors
+            * but this does not happen in a regular application.
+            */
+            if (handle is OS.GetFocus ()) {
+                if (OS.GetTextColor (nmcd.hdc) !is OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT)) {
+                    selected = false;
+                } else {
+                    if (OS.GetBkColor (nmcd.hdc) !is OS.GetSysColor (OS.COLOR_HIGHLIGHT)) {
+                        selected = false;
+                    }
+                }
+            }
+        } else {
+            if (nmcd.dwDrawStage is OS.CDDS_ITEMPOSTPAINT) {
+                /*
+                * Feature in Windows.  When the mouse is pressed and the
+                * selection is first drawn for a tree, the item is drawn
+                * selected, but the TVIS_SELECTED bits for the item are
+                * not set.  When the user moves the mouse slightly and
+                * a drag and drop operation is not started, the item is
+                * drawn again and this time TVIS_SELECTED is set.  This
+                * means that an item that is in a tree that has the style
+                * TVS_FULLROWSELECT and that also contains colored cells
+                * will not draw the entire row selected until the user
+                * moves the mouse.  The fix is to test for the selection
+                * colors and guess that the item is selected.
+                *
+                * NOTE: This code does not work when the foreground and
+                * background of the tree are set to the selection colors
+                * but this does not happen in a regular application.
+                */
+                if (OS.GetTextColor (nmcd.hdc) is OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT)) {
+                    if (OS.GetBkColor (nmcd.hdc) is OS.GetSysColor (OS.COLOR_HIGHLIGHT)) {
+                        selected = true;
+                    }
+                }
+            }
+        }
+    }
+    return selected;
+}
+
+void redrawSelection () {
+    if ((style & DWT.SINGLE) !is 0) {
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+        if (hItem !is 0) {
+            RECT rect = new RECT ();
+            rect.left = hItem;
+            OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect);
+            OS.InvalidateRect (handle, rect, true);
+        }
+    } else {
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+        if (hItem !is 0) {
+            TVITEM tvItem = null;
+            if (OS.IsWinCE) {
+                tvItem = new TVITEM ();
+                tvItem.mask = OS.TVIF_STATE;
+            }
+            RECT rect = new RECT ();
+            int index = 0, count = OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0);
+            while (index < count && hItem !is 0) {
+                int state = 0;
+                if (OS.IsWinCE) {
+                    tvItem.hItem = hItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                    state = tvItem.state;
+                } else {
+                    state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
+                }
+                if ((state & OS.TVIS_SELECTED) !is 0) {
+                    rect.left = hItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect);
+                    OS.InvalidateRect (handle, rect, true);
+                }
+                hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
+                index++;
+            }
+        }
+    }
+}
+
+void register () {
+    super.register ();
+    if (hwndParent !is 0) display.addControl (hwndParent, this);
+    if (hwndHeader !is 0) display.addControl (hwndHeader, this);
+}
+
+void releaseItem (int hItem, TVITEM tvItem, bool release) {
+    if (hItem is hAnchor) hAnchor = 0;
+    if (hItem is hInsert) hInsert = 0;
+    tvItem.hItem = hItem;
+    if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) !is 0) {
+        if (tvItem.lParam !is -1) {
+            if (tvItem.lParam < lastID) lastID = tvItem.lParam;
+            if (release) {
+                TreeItem item = items [tvItem.lParam];
+                if (item !is null) item.release (false);
+            }
+            items [tvItem.lParam] = null;
+        }
+    }
+}
+
+void releaseItems (int hItem, TVITEM tvItem) {
+    hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
+    while (hItem !is 0) {
+        releaseItems (hItem, tvItem);
+        releaseItem (hItem, tvItem, true);
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+    }
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    hwndParent = hwndHeader = 0;
+}
+
+void releaseChildren (bool destroy) {
+    if (items !is null) {
+        for (int i=0; i<items.length; i++) {
+            TreeItem item = items [i];
+            if (item !is null && !item.isDisposed ()) {
+                item.release (false);
+            }
+        }
+        items = null;
+    }
+    if (columns !is null) {
+        for (int i=0; i<columns.length; i++) {
+            TreeColumn column = columns [i];
+            if (column !is null && !column.isDisposed ()) {
+                column.release (false);
+            }
+        }
+        columns = null;
+    }
+    super.releaseChildren (destroy);
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    /*
+    * Feature in Windows.  For some reason, when TVM_GETIMAGELIST
+    * or TVM_SETIMAGELIST is sent, the tree issues NM_CUSTOMDRAW
+    * messages.  This behavior is unwanted when the tree is being
+    * disposed.  The fix is to ignore NM_CUSTOMDRAW messages by
+    * clearing the custom draw flag.
+    *
+    * NOTE: This only happens on Windows XP.
+    */
+    customDraw = false;
+    if (imageList !is null) {
+        OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, 0);
+        display.releaseImageList (imageList);
+    }
+    if (headerImageList !is null) {
+        if (hwndHeader !is 0) {
+            OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, 0);
+        }
+        display.releaseImageList (headerImageList);
+    }
+    imageList = headerImageList = null;
+    int hStateList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0);
+    OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, 0);
+    if (hStateList !is 0) OS.ImageList_Destroy (hStateList);
+    if (itemToolTipHandle !is 0) OS.DestroyWindow (itemToolTipHandle);
+    if (headerToolTipHandle !is 0) OS.DestroyWindow (headerToolTipHandle);
+    itemToolTipHandle = headerToolTipHandle = 0;
+    if (display.isXMouseActive ()) {
+        Shell shell = getShell ();
+        if (shell.lockToolTipControl is this) {
+            shell.lockToolTipControl = null;
+        }
+    }
+}
+
+/**
+ * Removes all of the items from the receiver.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void removeAll () {
+    checkWidget ();
+    hFirstIndexOf = hLastIndexOf = 0;
+    itemCount = -1;
+    for (int i=0; i<items.length; i++) {
+        TreeItem item = items [i];
+        if (item !is null && !item.isDisposed ()) {
+            item.release (false);
+        }
+    }
+    ignoreDeselect = ignoreSelect = true;
+    bool redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+    if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+    shrink = ignoreShrink = true;
+    int result = OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, OS.TVI_ROOT);
+    ignoreShrink = false;
+    if (redraw) {
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+        OS.InvalidateRect (handle, null, true);
+    }
+    ignoreDeselect = ignoreSelect = false;
+    if (result is 0) error (DWT.ERROR_ITEM_NOT_REMOVED);
+    if (imageList !is null) {
+        OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0);
+        display.releaseImageList (imageList);
+    }
+    imageList = null;
+    if (hwndParent is 0 && !linesVisible) {
+        if (!hooks (DWT.MeasureItem) && !hooks (DWT.EraseItem) && !hooks (DWT.PaintItem)) {
+            customDraw = false;
+        }
+    }
+    hAnchor = hInsert = hFirstIndexOf = hLastIndexOf = 0;
+    itemCount = -1;
+    items = new TreeItem [4];
+    scrollWidth = 0;
+    setScrollWidth ();
+    updateScrollBar ();
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's selection.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when items in the receiver are expanded or collapsed.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see TreeListener
+ * @see #addTreeListener
+ */
+public void removeTreeListener(TreeListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Expand, listener);
+    eventTable.unhook (DWT.Collapse, listener);
+}
+
+/**
+ * Display a mark indicating the point at which an item will be inserted.
+ * The drop insert item has a visual hint to show where a dragged item
+ * will be inserted when dropped on the tree.
+ *
+ * @param item the insert item.  Null will clear the insertion mark.
+ * @param before true places the insert mark above 'item'. false places
+ *  the insert mark below 'item'.
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setInsertMark (TreeItem item, bool before) {
+    checkWidget ();
+    int hItem = 0;
+    if (item !is null) {
+        if (item.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+        hItem = item.handle;
+    }
+    hInsert = hItem;
+    insertAfter = !before;
+    OS.SendMessage (handle, OS.TVM_SETINSERTMARK, insertAfter ? 1 : 0, hInsert);
+}
+
+/**
+ * Sets the number of root-level items contained in the receiver.
+ *
+ * @param count the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setItemCount (int count) {
+    checkWidget ();
+    count = Math.max (0, count);
+    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    setItemCount (count, OS.TVGN_ROOT, hItem);
+}
+
+void setItemCount (int count, int hParent, int hItem) {
+    bool redraw = false;
+    if (OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0) is 0) {
+        redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+        if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+    }
+    int itemCount = 0;
+    while (hItem !is 0 && itemCount < count) {
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+        itemCount++;
+    }
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    while (hItem !is 0) {
+        tvItem.hItem = hItem;
+        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+        TreeItem item = tvItem.lParam !is -1 ? items [tvItem.lParam] : null;
+        if (item !is null && !item.isDisposed ()) {
+            item.dispose ();
+        } else {
+            releaseItem (tvItem.hItem, tvItem, false);
+            destroyItem (null, tvItem.hItem);
+        }
+    }
+    if ((style & DWT.VIRTUAL) !is 0) {
+        for (int i=itemCount; i<count; i++) {
+            createItem (null, hParent, OS.TVI_LAST, 0);
+        }
+    } else {
+        shrink = true;
+        int extra = Math.max (4, (count + 3) / 4 * 4);
+        TreeItem [] newItems = new TreeItem [items.length + extra];
+        System.arraycopy (items, 0, newItems, 0, items.length);
+        items = newItems;
+        for (int i=itemCount; i<count; i++) {
+            new TreeItem (this, DWT.NONE, hParent, OS.TVI_LAST, 0);
+        }
+    }
+    if (redraw) {
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+        OS.InvalidateRect (handle, null, true);
+    }
+}
+
+/**
+ * Sets the height of the area which would be used to
+ * display <em>one</em> of the items in the tree.
+ *
+ * @param itemHeight the height of one item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+/*public*/ void setItemHeight (int itemHeight) {
+    checkWidget ();
+    if (itemHeight < -1) error (DWT.ERROR_INVALID_ARGUMENT);
+    OS.SendMessage (handle, OS.TVM_SETITEMHEIGHT, itemHeight, 0);
+}
+
+/**
+ * Marks the receiver's lines as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param show the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setLinesVisible (bool show) {
+    checkWidget ();
+    if (linesVisible is show) return;
+    linesVisible = show;
+    if (hwndParent is 0 && linesVisible) customDraw = true;
+    OS.InvalidateRect (handle, null, true);
+}
+
+int scrolledHandle () {
+    if (hwndHeader is 0) return handle;
+    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    return count is 0 ? handle : hwndParent;
+}
+
+void select (int hItem, TVITEM tvItem) {
+    while (hItem !is 0) {
+        tvItem.hItem = hItem;
+        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+        int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
+        select (hFirstItem, tvItem);
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+    }
+}
+
+/**
+ * Selects all of the items in the receiver.
+ * <p>
+ * If the receiver is single-select, do nothing.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void selectAll () {
+    checkWidget ();
+    if ((style & DWT.SINGLE) !is 0) return;
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+    tvItem.state = OS.TVIS_SELECTED;
+    tvItem.stateMask = OS.TVIS_SELECTED;
+    int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC);
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc);
+    if ((style & DWT.VIRTUAL) !is 0) {
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+        select (hItem, tvItem);
+    } else {
+        for (int i=0; i<items.length; i++) {
+            TreeItem item = items [i];
+            if (item !is null) {
+                tvItem.hItem = item.handle;
+                OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+            }
+        }
+    }
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc);
+}
+
+void setBackgroundImage (int hBitmap) {
+    super.setBackgroundImage (hBitmap);
+    if (hBitmap !is 0) {
+        /*
+        * Feature in Windows.  If TVM_SETBKCOLOR is never
+        * used to set the background color of a tree, the
+        * background color of the lines and the plus/minus
+        * will be drawn using the default background color,
+        * not the HBRUSH returned from WM_CTLCOLOR.  The fix
+        * is to set the background color to the default (when
+        * it is already the default) to make Windows use the
+        * brush.
+        */
+        if (OS.SendMessage (handle, OS.TVM_GETBKCOLOR, 0, 0) is -1) {
+            OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, -1);
+        }
+        _setBackgroundPixel (-1);
+    } else {
+        Control control = findBackgroundControl ();
+        if (control is null) control = this;
+        if (control.backgroundImage is null) {
+            setBackgroundPixel (control.getBackgroundPixel ());
+        }
+    }
+    /*
+    * Feature in Windows.  When the tree has the style
+    * TVS_FULLROWSELECT, the background color for the
+    * entire row is filled when an item is painted,
+    * drawing on top of the background image.  The fix
+    * is to clear TVS_FULLROWSELECT when a background
+    * image is set.
+    */
+    updateFullSelection ();
+}
+
+void setBackgroundPixel (int pixel) {
+    Control control = findImageControl ();
+    if (control !is null) {
+        setBackgroundImage (control.backgroundImage);
+        return;
+    }
+    /*
+    * Feature in Windows.  When a tree is given a background color
+    * using TVM_SETBKCOLOR and the tree is disabled, Windows draws
+    * the tree using the background color rather than the disabled
+    * colors.  This is different from the table which draws grayed.
+    * The fix is to set the default background color while the tree
+    * is disabled and restore it when enabled.
+    */
+    if (OS.IsWindowEnabled (handle)) _setBackgroundPixel (pixel);
+
+    /*
+    * Feature in Windows.  When the tree has the style
+    * TVS_FULLROWSELECT, the background color for the
+    * entire row is filled when an item is painted,
+    * drawing on top of the background image.  The fix
+    * is to restore TVS_FULLROWSELECT when a background
+    * color is set.
+    */
+    updateFullSelection ();
+}
+
+void setBounds (int x, int y, int width, int height, int flags) {
+    /*
+    * Ensure that the selection is visible when the tree is resized
+    * from a zero size to a size that can show the selection.
+    */
+    bool fixSelection = false;
+    if ((flags & OS.SWP_NOSIZE) is 0 && (width !is 0 || height !is 0)) {
+        if (OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0) is 0) {
+            fixSelection = true;
+        }
+    }
+    super.setBounds (x, y, width, height, flags);
+    if (fixSelection) {
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+        if (hItem !is 0) showItem (hItem);
+    }
+}
+
+void setCursor () {
+    /*
+    * Bug in Windows.  Under certain circumstances, when WM_SETCURSOR
+    * is sent from SendMessage(), Windows GP's in the window proc for
+    * the tree.  The fix is to avoid calling the tree window proc and
+    * set the cursor for the tree outside of WM_SETCURSOR.
+    *
+    * NOTE:  This code assumes that the default cursor for the tree
+    * is IDC_ARROW.
+    */
+    Cursor cursor = findCursor ();
+    int hCursor = cursor is null ? OS.LoadCursor (0, OS.IDC_ARROW) : cursor.handle;
+    OS.SetCursor (hCursor);
+}
+
+/**
+ * Sets the order that the items in the receiver should
+ * be displayed in to the given argument which is described
+ * in terms of the zero-relative ordering of when the items
+ * were added.
+ *
+ * @param order the new order to display the items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
+ * </ul>
+ *
+ * @see Tree#getColumnOrder()
+ * @see TreeColumn#getMoveable()
+ * @see TreeColumn#setMoveable(bool)
+ * @see DWT#Move
+ *
+ * @since 3.2
+ */
+public void setColumnOrder (int [] order) {
+    checkWidget ();
+    if (order is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int count = 0;
+    if (hwndHeader !is 0) {
+        count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    }
+    if (count is 0) {
+        if (order.length !is 0) error (DWT.ERROR_INVALID_ARGUMENT);
+        return;
+    }
+    if (order.length !is count) error (DWT.ERROR_INVALID_ARGUMENT);
+    int [] oldOrder = new int [count];
+    OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, oldOrder);
+    bool reorder = false;
+    bool [] seen = new bool [count];
+    for (int i=0; i<order.length; i++) {
+        int index = order [i];
+        if (index < 0 || index >= count) error (DWT.ERROR_INVALID_RANGE);
+        if (seen [index]) error (DWT.ERROR_INVALID_ARGUMENT);
+        seen [index] = true;
+        if (index !is oldOrder [i]) reorder = true;
+    }
+    if (reorder) {
+        RECT [] oldRects = new RECT [count];
+        for (int i=0; i<count; i++) {
+            oldRects [i] = new RECT ();
+            OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, oldRects [i]);
+        }
+        OS.SendMessage (hwndHeader, OS.HDM_SETORDERARRAY, order.length, order);
+        OS.InvalidateRect (handle, null, true);
+        updateImageList ();
+        TreeColumn [] newColumns = new TreeColumn [count];
+        System.arraycopy (columns, 0, newColumns, 0, count);
+        RECT newRect = new RECT ();
+        for (int i=0; i<count; i++) {
+            TreeColumn column = newColumns [i];
+            if (!column.isDisposed ()) {
+                OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, newRect);
+                if (newRect.left !is oldRects [i].left) {
+                    column.updateToolTip (i);
+                    column.sendEvent (DWT.Move);
+                }
+            }
+        }
+    }
+}
+
+void setCheckboxImageList () {
+    if ((style & DWT.CHECK) is 0) return;
+    int count = 5, flags = 0;
+    if (OS.IsWinCE) {
+        flags |= OS.ILC_COLOR;
+    } else {
+        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+            flags |= OS.ILC_COLOR32;
+        } else {
+            int hDC = OS.GetDC (handle);
+            int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL);
+            int planes = OS.GetDeviceCaps (hDC, OS.PLANES);
+            OS.ReleaseDC (handle, hDC);
+            int depth = bits * planes;
+            switch (depth) {
+                case 4: flags |= OS.ILC_COLOR4; break;
+                case 8: flags |= OS.ILC_COLOR8; break;
+                case 16: flags |= OS.ILC_COLOR16; break;
+                case 24: flags |= OS.ILC_COLOR24; break;
+                case 32: flags |= OS.ILC_COLOR32; break;
+                default: flags |= OS.ILC_COLOR; break;
+            }
+            flags |= OS.ILC_MASK;
+        }
+    }
+    if ((style & DWT.RIGHT_TO_LEFT) !is 0) flags |= OS.ILC_MIRROR;
+    int height = OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0), width = height;
+    int hStateList = OS.ImageList_Create (width, height, flags, count, count);
+    int hDC = OS.GetDC (handle);
+    int memDC = OS.CreateCompatibleDC (hDC);
+    int hBitmap = OS.CreateCompatibleBitmap (hDC, width * count, height);
+    int hOldBitmap = OS.SelectObject (memDC, hBitmap);
+    RECT rect = new RECT ();
+    OS.SetRect (rect, 0, 0, width * count, height);
+    /*
+    * NOTE: DrawFrameControl() draws a black and white
+    * mask when not drawing a push button.  In order to
+    * make the box surrounding the check mark transparent,
+    * fill it with a color that is neither black or white.
+    */
+    int clrBackground = 0;
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        Control control = findBackgroundControl ();
+        if (control is null) control = this;
+        clrBackground = control.getBackgroundPixel ();
+    } else {
+        clrBackground = 0x020000FF;
+        if ((clrBackground & 0xFFFFFF) is OS.GetSysColor (OS.COLOR_WINDOW)) {
+            clrBackground = 0x0200FF00;
+        }
+    }
+    int hBrush = OS.CreateSolidBrush (clrBackground);
+    OS.FillRect (memDC, rect, hBrush);
+    OS.DeleteObject (hBrush);
+    int oldFont = OS.SelectObject (hDC, defaultFont ());
+    TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
+    OS.GetTextMetrics (hDC, tm);
+    OS.SelectObject (hDC, oldFont);
+    int itemWidth = Math.min (tm.tmHeight, width);
+    int itemHeight = Math.min (tm.tmHeight, height);
+    int left = (width - itemWidth) / 2, top = (height - itemHeight) / 2 + 1;
+    OS.SetRect (rect, left + width, top, left + width + itemWidth, top + itemHeight);
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        int hTheme = display.hButtonTheme ();
+        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
+        rect.left += width;  rect.right += width;
+        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_CHECKEDNORMAL, rect, null);
+        rect.left += width;  rect.right += width;
+        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
+        rect.left += width;  rect.right += width;
+        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_MIXEDNORMAL, rect, null);
+    } else {
+        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_FLAT);
+        rect.left += width;  rect.right += width;
+        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_FLAT);
+        rect.left += width;  rect.right += width;
+        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
+        rect.left += width;  rect.right += width;
+        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
+    }
+    OS.SelectObject (memDC, hOldBitmap);
+    OS.DeleteDC (memDC);
+    OS.ReleaseDC (handle, hDC);
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+        OS.ImageList_Add (hStateList, hBitmap, 0);
+    } else {
+        OS.ImageList_AddMasked (hStateList, hBitmap, clrBackground);
+    }
+    OS.DeleteObject (hBitmap);
+    int hOldStateList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0);
+    OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, hStateList);
+    if (hOldStateList !is 0) OS.ImageList_Destroy (hOldStateList);
+}
+
+public void setFont (Font font) {
+    checkWidget ();
+    super.setFont (font);
+    if ((style & DWT.CHECK) !is 0) setCheckboxImageList ();
+}
+
+void setForegroundPixel (int pixel) {
+    /*
+    * Bug in Windows.  When the tree is using the explorer
+    * theme, it does not use COLOR_WINDOW_TEXT for the
+    * foreground.  When TVM_SETTEXTCOLOR is called with -1,
+    * it resets the color to black, not COLOR_WINDOW_TEXT.
+    * The fix is to explicitly set the color.
+    */
+    if (explorerTheme) {
+        if (pixel is -1) pixel = defaultForeground ();
+    }
+    OS.SendMessage (handle, OS.TVM_SETTEXTCOLOR, 0, pixel);
+}
+
+/**
+ * Marks the receiver's header as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param show the new visibility state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setHeaderVisible (bool show) {
+    checkWidget ();
+    if (hwndHeader is 0) {
+        if (!show) return;
+        createParent ();
+    }
+    int bits = OS.GetWindowLong (hwndHeader, OS.GWL_STYLE);
+    if (show) {
+        if ((bits & OS.HDS_HIDDEN) is 0) return;
+        bits &= ~OS.HDS_HIDDEN;
+        OS.SetWindowLong (hwndHeader, OS.GWL_STYLE, bits);
+        OS.ShowWindow (hwndHeader, OS.SW_SHOW);
+    } else {
+        if ((bits & OS.HDS_HIDDEN) !is 0) return;
+        bits |= OS.HDS_HIDDEN;
+        OS.SetWindowLong (hwndHeader, OS.GWL_STYLE, bits);
+        OS.ShowWindow (hwndHeader, OS.SW_HIDE);
+    }
+    setScrollWidth ();
+    updateHeaderToolTips ();
+    updateScrollBar ();
+}
+
+public void setRedraw (bool redraw) {
+    checkWidget ();
+    /*
+    * Feature in Windows.  When WM_SETREDRAW is used to
+    * turn off redraw, the scroll bars are updated when
+    * items are added and removed.  The fix is to call
+    * the default window proc to stop all drawing.
+    *
+    * Bug in Windows.  For some reason, when WM_SETREDRAW
+    * is used to turn redraw on for a tree and the tree
+    * contains no items, the last item in the tree does
+    * not redraw properly.  If the tree has only one item,
+    * that item is not drawn.  If another window is dragged
+    * on top of the item, parts of the item are redrawn
+    * and erased at random.  The fix is to ensure that this
+    * case doesn't happen by inserting and deleting an item
+    * when redraw is turned on and there are no items in
+    * the tree.
+    */
+    int hItem = 0;
+    if (redraw) {
+        if (drawCount is 1) {
+            int count = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
+            if (count is 0) {
+                TVINSERTSTRUCT tvInsert = new TVINSERTSTRUCT ();
+                tvInsert.hInsertAfter = OS.TVI_FIRST;
+                hItem = OS.SendMessage (handle, OS.TVM_INSERTITEM, 0, tvInsert);
+            }
+            OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+        }
+    }
+    super.setRedraw (redraw);
+    if (!redraw) {
+        if (drawCount is 1) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+    }
+    if (hItem !is 0) {
+        ignoreShrink = true;
+        OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, hItem);
+        ignoreShrink = false;
+    }
+}
+
+void setScrollWidth () {
+    if (hwndHeader is 0 || hwndParent is 0) return;
+    int width = 0;
+    HDITEM hdItem = new HDITEM ();
+    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<count; i++) {
+        hdItem.mask = OS.HDI_WIDTH;
+        OS.SendMessage (hwndHeader, OS.HDM_GETITEM, i, hdItem);
+        width += hdItem.cxy;
+    }
+    setScrollWidth (Math.max (scrollWidth, width));
+}
+
+void setScrollWidth (int width) {
+    if (hwndHeader is 0 || hwndParent is 0) return;
+    //TEMPORARY CODE
+    //scrollWidth = width;
+    int left = 0;
+    RECT rect = new RECT ();
+    SCROLLINFO info = new SCROLLINFO ();
+    info.cbSize = SCROLLINFO.sizeof;
+    info.fMask = OS.SIF_RANGE | OS.SIF_PAGE;
+    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    if (count is 0 && width is 0) {
+        OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info);
+        info.nPage = info.nMax + 1;
+        OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
+        OS.GetScrollInfo (hwndParent, OS.SB_VERT, info);
+        info.nPage = info.nMax + 1;
+        OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
+    } else {
+        OS.GetClientRect (hwndParent, rect);
+        OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info);
+        info.nMax = width;
+        info.nPage = rect.right - rect.left + 1;
+        OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
+        info.fMask = OS.SIF_POS;
+        OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info);
+        left = info.nPos;
+    }
+    if (horizontalBar !is null) {
+        horizontalBar.setIncrement (INCREMENT);
+        horizontalBar.setPageIncrement (info.nPage);
+    }
+    OS.GetClientRect (hwndParent, rect);
+    int hHeap = OS.GetProcessHeap ();
+    HDLAYOUT playout = new HDLAYOUT ();
+    playout.prc = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, RECT.sizeof);
+    playout.pwpos = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, WINDOWPOS.sizeof);
+    OS.MoveMemory (playout.prc, rect, RECT.sizeof);
+    OS.SendMessage (hwndHeader, OS.HDM_LAYOUT, 0, playout);
+    WINDOWPOS pos = new WINDOWPOS ();
+    OS.MoveMemory (pos, playout.pwpos, WINDOWPOS.sizeof);
+    if (playout.prc !is 0) OS.HeapFree (hHeap, 0, playout.prc);
+    if (playout.pwpos !is 0) OS.HeapFree (hHeap, 0, playout.pwpos);
+    SetWindowPos (hwndHeader, OS.HWND_TOP, pos.x - left, pos.y, pos.cx + left, pos.cy, OS.SWP_NOACTIVATE);
+    int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
+    int b = (bits & OS.WS_EX_CLIENTEDGE) !is 0 ? OS.GetSystemMetrics (OS.SM_CXEDGE) : 0;
+    int w = pos.cx + (count is 0 && width is 0 ? 0 : OS.GetSystemMetrics (OS.SM_CXVSCROLL));
+    int h = rect.bottom - rect.top - pos.cy;
+    bool oldIgnore = ignoreResize;
+    ignoreResize = true;
+    SetWindowPos (handle, 0, pos.x - left - b, pos.y + pos.cy - b, w + left + b * 2, h + b * 2, OS.SWP_NOACTIVATE | OS.SWP_NOZORDER);
+    ignoreResize = oldIgnore;
+}
+
+void setSelection (int hItem, TVITEM tvItem, TreeItem [] selection) {
+    while (hItem !is 0) {
+        int index = 0;
+        while (index < selection.length) {
+            TreeItem item = selection [index];
+            if (item !is null && item.handle is hItem) break;
+            index++;
+        }
+        tvItem.hItem = hItem;
+        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+        if ((tvItem.state & OS.TVIS_SELECTED) !is 0) {
+            if (index is selection.length) {
+                tvItem.state = 0;
+                OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+            }
+        } else {
+            if (index !is selection.length) {
+                tvItem.state = OS.TVIS_SELECTED;
+                OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+            }
+        }
+        int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
+        setSelection (hFirstItem, tvItem, selection);
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
+    }
+}
+
+/**
+ * Sets the receiver's selection to the given item.
+ * The current selection is cleared before the new item is selected.
+ * <p>
+ * If the item is not in the receiver, then it is ignored.
+ * </p>
+ *
+ * @param item the item to select
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSelection (TreeItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    setSelection (new TreeItem [] {item});
+}
+
+/**
+ * Sets the receiver's selection to be the given array of items.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Items that are not in the receiver are ignored.
+ * If the receiver is single-select and multiple items are specified,
+ * then all items are ignored.
+ * </p>
+ *
+ * @param items the array of items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#deselectAll()
+ */
+public void setSelection (TreeItem [] items) {
+    checkWidget ();
+    if (items is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int length = items.length;
+    if (length is 0 || ((style & DWT.SINGLE) !is 0 && length > 1)) {
+        deselectAll();
+        return;
+    }
+
+    /* Select/deselect the first item */
+    TreeItem item = items [0];
+    if (item !is null) {
+        if (item.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+        int hOldItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+        int hNewItem = hAnchor = item.handle;
+
+        /*
+        * Bug in Windows.  When TVM_SELECTITEM is used to select and
+        * scroll an item to be visible and the client area of the tree
+        * is smaller that the size of one item, TVM_SELECTITEM makes
+        * the next item in the tree visible by making it the top item
+        * instead of making the desired item visible.  The fix is to
+        * detect the case when the client area is too small and make
+        * the desired visible item be the top item in the tree.
+        *
+        * Note that TVM_SELECTITEM when called with TVGN_FIRSTVISIBLE
+        * also requires the work around for scrolling.
+        */
+        bool fixScroll = checkScroll (hNewItem);
+        if (fixScroll) {
+            OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+            OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+        }
+        ignoreSelect = true;
+        OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hNewItem);
+        ignoreSelect = false;
+        if (OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0) is 0) {
+            OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hNewItem);
+            int hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hNewItem);
+            if (hParent is 0) OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0);
+        }
+        if (fixScroll) {
+            OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+            OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+        }
+
+        /*
+        * Feature in Windows.  When the old and new focused item
+        * are the same, Windows does not check to make sure that
+        * the item is actually selected, not just focused.  The
+        * fix is to force the item to draw selected by setting
+        * the state mask, and to ensure that it is visible.
+        */
+        if (hOldItem is hNewItem) {
+            TVITEM tvItem = new TVITEM ();
+            tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+            tvItem.state = OS.TVIS_SELECTED;
+            tvItem.stateMask = OS.TVIS_SELECTED;
+            tvItem.hItem = hNewItem;
+            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+            showItem (hNewItem);
+        }
+    }
+    if ((style & DWT.SINGLE) !is 0) return;
+
+    /* Select/deselect the rest of the items */
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+    tvItem.stateMask = OS.TVIS_SELECTED;
+    int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC);
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc);
+    if ((style & DWT.VIRTUAL) !is 0) {
+        int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+        setSelection (hItem, tvItem, items);
+    } else {
+        for (int i=0; i<this.items.length; i++) {
+            item = this.items [i];
+            if (item !is null) {
+                int index = 0;
+                while (index < length) {
+                    if (items [index] is item) break;
+                    index++;
+                }
+                tvItem.hItem = item.handle;
+                OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                if ((tvItem.state & OS.TVIS_SELECTED) !is 0) {
+                    if (index is length) {
+                        tvItem.state = 0;
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                    }
+                } else {
+                    if (index !is length) {
+                        tvItem.state = OS.TVIS_SELECTED;
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                    }
+                }
+            }
+        }
+    }
+    OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc);
+}
+
+/**
+ * Sets the column used by the sort indicator for the receiver. A null
+ * value will clear the sort indicator.  The current sort column is cleared
+ * before the new column is set.
+ *
+ * @param column the column used by the sort indicator or <code>null</code>
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSortColumn (TreeColumn column) {
+    checkWidget ();
+    if (column !is null && column.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+    if (sortColumn !is null && !sortColumn.isDisposed ()) {
+        sortColumn.setSortDirection (DWT.NONE);
+    }
+    sortColumn = column;
+    if (sortColumn !is null && sortDirection !is DWT.NONE) {
+        sortColumn.setSortDirection (sortDirection);
+    }
+}
+
+/**
+ * Sets the direction of the sort indicator for the receiver. The value
+ * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>.
+ *
+ * @param direction the direction of the sort indicator
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSortDirection (int direction) {
+    checkWidget ();
+    if ((direction & (DWT.UP | DWT.DOWN)) is 0 && direction !is DWT.NONE) return;
+    sortDirection = direction;
+    if (sortColumn !is null && !sortColumn.isDisposed ()) {
+        sortColumn.setSortDirection (direction);
+    }
+}
+
+/**
+ * Sets the item which is currently at the top of the receiver.
+ * This item can change when items are expanded, collapsed, scrolled
+ * or new items are added or removed.
+ *
+ * @param item the item to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#getTopItem()
+ *
+ * @since 2.1
+ */
+public void setTopItem (TreeItem item) {
+    checkWidget ();
+    if (item is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    if (item.isDisposed ()) DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    int hItem = item.handle;
+    int hTopItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+    if (hItem is hTopItem) return;
+    bool fixScroll = checkScroll (hItem), redraw = false;
+    if (fixScroll) {
+        OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+    } else {
+        redraw = drawCount is 0 && OS.IsWindowVisible (handle);
+        if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+    }
+    OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hItem);
+    int hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
+    if (hParent is 0) OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0);
+    if (fixScroll) {
+        OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+        OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+    } else {
+        if (redraw) {
+            OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+            OS.InvalidateRect (handle, null, true);
+        }
+    }
+    updateScrollBar ();
+}
+
+void showItem (int hItem) {
+    /*
+    * Bug in Windows.  When TVM_ENSUREVISIBLE is used to ensure
+    * that an item is visible and the client area of the tree is
+    * smaller that the size of one item, TVM_ENSUREVISIBLE makes
+    * the next item in the tree visible by making it the top item
+    * instead of making the desired item visible.  The fix is to
+    * detect the case when the client area is too small and make
+    * the desired visible item be the top item in the tree.
+    */
+    if (OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0) is 0) {
+        bool fixScroll = checkScroll (hItem);
+        if (fixScroll) {
+            OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+            OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+        }
+        OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hItem);
+        /* This code is intentionally commented */
+        //int hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
+        //if (hParent is 0) OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0);
+        OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0);
+        if (fixScroll) {
+            OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+            OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+        }
+    } else {
+        bool scroll = true;
+        RECT itemRect = new RECT ();
+        itemRect.left = hItem;
+        if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, itemRect) !is 0) {
+            forceResize ();
+            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) {
+            bool fixScroll = checkScroll (hItem);
+            if (fixScroll) {
+                OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+            }
+            OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hItem);
+            if (fixScroll) {
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+                OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
+            }
+        }
+    }
+    if (hwndParent !is 0) {
+        RECT itemRect = new RECT ();
+        itemRect.left = hItem;
+        if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, itemRect) !is 0) {
+            forceResize ();
+            RECT rect = new RECT ();
+            OS.GetClientRect (hwndParent, rect);
+            OS.MapWindowPoints (hwndParent, handle, rect, 2);
+            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)) {
+                    SCROLLINFO info = new SCROLLINFO ();
+                    info.cbSize = SCROLLINFO.sizeof;
+                    info.fMask = OS.SIF_POS;
+                    info.nPos = Math.max (0, pt.x - Tree.INSET / 2);
+                    OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
+                    setScrollWidth ();
+                }
+            }
+        }
+    }
+    updateScrollBar ();
+}
+
+/**
+ * Shows the column.  If the column is already showing in the receiver,
+ * this method simply returns.  Otherwise, the columns are scrolled until
+ * the column is visible.
+ *
+ * @param column the column to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void showColumn (TreeColumn column) {
+    checkWidget ();
+    if (column is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (column.isDisposed ()) error(DWT.ERROR_INVALID_ARGUMENT);
+    if (column.parent !is this) return;
+    int index = indexOf (column);
+    if (index is -1) return;
+    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    if (0 <= index && index < count) {
+        if (hwndParent !is 0) {
+            forceResize ();
+            RECT rect = new RECT ();
+            OS.GetClientRect (hwndParent, rect);
+            OS.MapWindowPoints (hwndParent, handle, rect, 2);
+            RECT headerRect = new RECT ();
+            OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+            bool scroll = headerRect.left < rect.left;
+            if (!scroll) {
+                int width = Math.min (rect.right - rect.left, headerRect.right - headerRect.left);
+                scroll = headerRect.left + width > rect.right;
+            }
+            if (scroll) {
+                SCROLLINFO info = new SCROLLINFO ();
+                info.cbSize = SCROLLINFO.sizeof;
+                info.fMask = OS.SIF_POS;
+                info.nPos = Math.max (0, headerRect.left - Tree.INSET / 2);
+                OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
+                setScrollWidth ();
+            }
+        }
+    }
+}
+
+/**
+ * Shows the item.  If the item is already showing in the receiver,
+ * this method simply returns.  Otherwise, the items are scrolled
+ * and expanded until the item is visible.
+ *
+ * @param item the item to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#showSelection()
+ */
+public void showItem (TreeItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (item.isDisposed ()) error(DWT.ERROR_INVALID_ARGUMENT);
+    showItem (item.handle);
+}
+
+/**
+ * Shows the selection.  If the selection is already showing in the receiver,
+ * this method simply returns.  Otherwise, the items are scrolled until
+ * the selection is visible.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#showItem(TreeItem)
+ */
+public void showSelection () {
+    checkWidget ();
+    int hItem = 0;
+    if ((style & DWT.SINGLE) !is 0) {
+        hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+        if (hItem is 0) return;
+        int state = 0;
+        if (OS.IsWinCE) {
+            TVITEM tvItem = new TVITEM ();
+            tvItem.hItem = hItem;
+            tvItem.mask = OS.TVIF_STATE;
+            OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+            state = tvItem.state;
+        } else {
+            state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
+        }
+        if ((state & OS.TVIS_SELECTED) is 0) return;
+    } else {
+        int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC);
+        OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc);
+        TVITEM tvItem = null;
+        if (OS.IsWinCE) {
+            tvItem = new TVITEM ();
+            tvItem.mask = OS.TVIF_STATE;
+        }
+        if ((style & DWT.VIRTUAL) !is 0) {
+            int hRoot = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+            hItem = getNextSelection (hRoot, tvItem);
+        } else {
+            //FIXME - this code expands first selected item it finds
+            int index = 0;
+            while (index <items.length) {
+                TreeItem item = items [index];
+                if (item !is null) {
+                    int state = 0;
+                    if (OS.IsWinCE) {
+                        tvItem.hItem = item.handle;
+                        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                        state = tvItem.state;
+                    } else {
+                        state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, item.handle, OS.TVIS_SELECTED);
+                    }
+                    if ((state & OS.TVIS_SELECTED) !is 0) {
+                        hItem = item.handle;
+                        break;
+                    }
+                }
+                index++;
+            }
+        }
+        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc);
+    }
+    if (hItem !is 0) showItem (hItem);
+}
+
+/*public*/ void sort () {
+    checkWidget ();
+    if ((style & DWT.VIRTUAL) !is 0) return;
+    sort (OS.TVI_ROOT, false);
+}
+
+void sort (int hParent, bool all) {
+    int itemCount = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
+    if (itemCount is 0 || itemCount is 1) return;
+    hFirstIndexOf = hLastIndexOf = 0;
+    itemCount = -1;
+    if (sortDirection is DWT.UP || sortDirection is DWT.NONE) {
+        OS.SendMessage (handle, OS.TVM_SORTCHILDREN, all ? 1 : 0, hParent);
+    } else {
+        Callback compareCallback = new Callback (this, "CompareFunc", 3);
+        int lpfnCompare = compareCallback.getAddress ();
+        TVSORTCB psort = new TVSORTCB ();
+        psort.hParent = hParent;
+        psort.lpfnCompare = lpfnCompare;
+        psort.lParam = sortColumn is null ? 0 : indexOf (sortColumn);
+        OS.SendMessage (handle, OS.TVM_SORTCHILDRENCB, all ? 1 : 0, psort);
+        compareCallback.dispose ();
+    }
+}
+
+void subclass () {
+    super.subclass ();
+    if (hwndHeader !is 0) {
+        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, display.windowProc);
+    }
+}
+
+String toolTipText (NMTTDISPINFO hdr) {
+    int hwndToolTip = OS.SendMessage (handle, OS.TVM_GETTOOLTIPS, 0, 0);
+    if (hwndToolTip is hdr.hwndFrom && toolTipText !is null) return ""; //$NON-NLS-1$
+    if (headerToolTipHandle is hdr.hwndFrom) {
+        int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+        for (int i=0; i<count; i++) {
+            TreeColumn column = columns [i];
+            if (column.id is hdr.idFrom) return column.toolTipText;
+        }
+        return super.toolTipText (hdr);
+    }
+    if (itemToolTipHandle is hdr.hwndFrom && hwndHeader !is 0) {
+        if (toolTipText !is null) return "";
+        if (!hooks (DWT.EraseItem) && !hooks (DWT.PaintItem)) {
+            int pos = OS.GetMessagePos ();
+            POINT pt = new POINT();
+            pt.x = (short) (pos & 0xFFFF);
+            pt.y = (short) (pos >> 16);
+            OS.ScreenToClient (handle, pt);
+            TVHITTESTINFO lpht = new TVHITTESTINFO ();
+            lpht.x = pt.x;
+            lpht.y = pt.y;
+            OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
+            if (lpht.hItem !is 0) {
+                int hDC = OS.GetDC (handle);
+                int oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+                if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+                RECT rect = new RECT ();
+                OS.GetClientRect (hwndParent, rect);
+                OS.MapWindowPoints (hwndParent, handle, rect, 2);
+                TreeItem item = _getItem (lpht.hItem);
+                String text = null;
+                int index = 0, count = Math.max (1, OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0));
+                int [] order = new int [count];
+                OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
+                while (index < count) {
+                    int hFont = item.cellFont !is null ? item.cellFont [order [index]] : -1;
+                    if (hFont is -1) hFont = item.font;
+                    if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+                    RECT cellRect = item.getBounds (order [index], true, false, true, false, true, hDC);
+                    if (hFont !is -1) OS.SelectObject (hDC, hFont);
+                    if (cellRect.left > rect.right) break;
+                    cellRect.right = Math.min (cellRect.right, rect.right);
+                    if (OS.PtInRect (cellRect, pt)) {
+                        RECT textRect = item.getBounds (order [index], true, false, false, false, false, hDC);
+                        if (textRect.right > cellRect.right) {
+                            if (order [index] is 0) {
+                                text = item.text;
+                            } else {
+                                String[] strings = item.strings;
+                                if (strings !is null) text = strings [order [index]];
+                            }
+                        }
+                        break;
+                    }
+                    index++;
+                }
+                if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+                OS.ReleaseDC (handle, hDC);
+                if (text !is null) return text;
+            }
+        }
+    }
+    return super.toolTipText (hdr);
+}
+
+int topHandle () {
+    return hwndParent !is 0 ? hwndParent : handle;
+}
+
+void updateFullSelection () {
+    if ((style & DWT.FULL_SELECTION) !is 0) {
+        int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
+        if ((newBits & OS.TVS_FULLROWSELECT) !is 0) {
+            if (!OS.IsWindowEnabled (handle) || findImageControl () !is null) {
+                if (!explorerTheme) newBits &= ~OS.TVS_FULLROWSELECT;
+            }
+        } else {
+            if (OS.IsWindowEnabled (handle) && findImageControl () is null) {
+                if (!hooks (DWT.EraseItem) && !hooks (DWT.PaintItem)) {
+                    newBits |= OS.TVS_FULLROWSELECT;
+                }
+            }
+        }
+        if (newBits !is oldBits) {
+            OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
+            OS.InvalidateRect (handle, null, true);
+        }
+    }
+}
+
+void updateHeaderToolTips () {
+    if (headerToolTipHandle is 0) return;
+    RECT rect = new RECT ();
+    TOOLINFO lpti = new TOOLINFO ();
+    lpti.cbSize = TOOLINFO.sizeof;
+    lpti.uFlags = OS.TTF_SUBCLASS;
+    lpti.hwnd = hwndHeader;
+    lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
+    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+    for (int i=0; i<count; i++) {
+        TreeColumn column = columns [i];
+        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, rect) !is 0) {
+            lpti.uId = column.id = display.nextToolTipId++;
+            lpti.left = rect.left;
+            lpti.top = rect.top;
+            lpti.right = rect.right;
+            lpti.bottom = rect.bottom;
+            OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
+        }
+    }
+}
+
+void updateImageList () {
+    if (imageList is null) return;
+    if (hwndHeader is 0) return;
+    int i = 0, index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
+    while (i < items.length) {
+        TreeItem item = items [i];
+        if (item !is null) {
+            Image image = null;
+            if (index is 0) {
+                image = item.image;
+            } else {
+                Image [] images = item.images;
+                if (images !is null) image = images [index];
+            }
+            if (image !is null) break;
+        }
+        i++;
+    }
+    /*
+    * Feature in Windows.  When setting the same image list multiple
+    * times, Windows does work making this operation slow.  The fix
+    * is to test for the same image list before setting the new one.
+    */
+    int hImageList = i is items.length ? 0 : imageList.getHandle ();
+    int hOldImageList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0);
+    if (hImageList !is hOldImageList) {
+        OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList);
+    }
+}
+
+void updateImages () {
+    if (sortColumn !is null && !sortColumn.isDisposed ()) {
+        if (OS.COMCTL32_MAJOR < 6) {
+            switch (sortDirection) {
+                case DWT.UP:
+                case DWT.DOWN:
+                    sortColumn.setImage (display.getSortImage (sortDirection), true, true);
+                    break;
+            }
+        }
+    }
+}
+
+void updateScrollBar () {
+    if (hwndParent !is 0) {
+        int columnCount = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+        if (columnCount !is 0 || scrollWidth !is 0) {
+            SCROLLINFO info = new SCROLLINFO ();
+            info.cbSize = SCROLLINFO.sizeof;
+            info.fMask = OS.SIF_ALL;
+            int itemCount = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
+            if (itemCount is 0) {
+                OS.GetScrollInfo (hwndParent, OS.SB_VERT, info);
+                info.nPage = info.nMax + 1;
+                OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
+            } else {
+                OS.GetScrollInfo (handle, OS.SB_VERT, info);
+                if (info.nPage is 0) info.nPage = info.nMax + 1;
+                OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
+            }
+        }
+    }
+}
+
+void unsubclass () {
+    super.unsubclass ();
+    if (hwndHeader !is 0) {
+        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
+    }
+}
+
+int widgetStyle () {
+    int bits = super.widgetStyle () | OS.TVS_SHOWSELALWAYS | OS.TVS_LINESATROOT | OS.TVS_HASBUTTONS | OS.TVS_NONEVENHEIGHT;
+    if (EXPLORER_THEME && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        bits |= OS.TVS_TRACKSELECT;
+        if ((style & DWT.FULL_SELECTION) !is 0) bits |= OS.TVS_FULLROWSELECT;
+    } else {
+        if ((style & DWT.FULL_SELECTION) !is 0) {
+            bits |= OS.TVS_FULLROWSELECT;
+        } else {
+            bits |= OS.TVS_HASLINES;
+        }
+    }
+//  bits |= OS.TVS_NOTOOLTIPS | OS.TVS_DISABLEDRAGDROP;
+    return bits | OS.TVS_DISABLEDRAGDROP;
+}
+
+TCHAR windowClass () {
+    return TreeClass;
+}
+
+int windowProc () {
+    return TreeProc;
+}
+
+int windowProc (int hwnd, int msg, int wParam, int lParam) {
+    if (hwndHeader !is 0 && hwnd is hwndHeader) {
+        switch (msg) {
+            /* This code is intentionally commented */
+//          case OS.WM_CONTEXTMENU: {
+//              LRESULT result = wmContextMenu (hwnd, wParam, lParam);
+//              if (result !is null) return result.value;
+//              break;
+//          }
+            case OS.WM_CAPTURECHANGED: {
+                /*
+                * Bug in Windows.  When the capture changes during a
+                * header drag, Windows does not redraw the header item
+                * such that the header remains pressed.  For example,
+                * when focus is assigned to a push button, the mouse is
+                * pressed (but not released), then the SPACE key is
+                * pressed to activate the button, the capture changes,
+                * the header not notified and NM_RELEASEDCAPTURE is not
+                * sent.  The fix is to redraw the header when the capture
+                * changes to another control.
+                *
+                * This does not happen on XP.
+                */
+                if (OS.COMCTL32_MAJOR < 6) {
+                    if (lParam !is 0 && lParam !is hwndHeader) {
+                        OS.InvalidateRect (hwndHeader, null, true);
+                    }
+                }
+                break;
+            }
+            case OS.WM_MOUSELEAVE: {
+                /*
+                * Bug in Windows.  On XP, when a tooltip is hidden
+                * due to a time out or mouse press, the tooltip
+                * remains active although no longer visible and
+                * won't show again until another tooltip becomes
+                * active.  The fix is to reset the tooltip bounds.
+                */
+                if (OS.COMCTL32_MAJOR >= 6) updateHeaderToolTips ();
+                updateHeaderToolTips ();
+                break;
+            }
+            case OS.WM_NOTIFY: {
+                NMHDR hdr = new NMHDR ();
+                OS.MoveMemory (hdr, lParam, NMHDR.sizeof);
+                switch (hdr.code) {
+                    case OS.TTN_SHOW:
+                    case OS.TTN_POP:
+                    case OS.TTN_GETDISPINFOA:
+                    case OS.TTN_GETDISPINFOW:
+                        return OS.SendMessage (handle, msg, wParam, lParam);
+                }
+                break;
+            }
+            case OS.WM_SETCURSOR: {
+                if (wParam is hwnd) {
+                    int hitTest = (short) (lParam & 0xFFFF);
+                    if (hitTest is OS.HTCLIENT) {
+                        HDHITTESTINFO pinfo = new HDHITTESTINFO ();
+                        int pos = OS.GetMessagePos ();
+                        POINT pt = new POINT ();
+                        pt.x = (short) (pos & 0xFFFF);
+                        pt.y = (short) (pos >> 16);
+                        OS.ScreenToClient (hwnd, pt);
+                        pinfo.x = pt.x;
+                        pinfo.y = pt.y;
+                        int columnCount = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                        int index = OS.SendMessage (hwndHeader, OS.HDM_HITTEST, 0, pinfo);
+                        if (0 <= index && index < columnCount && !columns [index].resizable) {
+                            if ((pinfo.flags & (OS.HHT_ONDIVIDER | OS.HHT_ONDIVOPEN)) !is 0) {
+                                OS.SetCursor (OS.LoadCursor (0, OS.IDC_ARROW));
+                                return 1;
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+        }
+        return callWindowProc (hwnd, msg, wParam, lParam);
+    }
+    if (hwndParent !is 0 && hwnd is hwndParent) {
+        switch (msg) {
+            case OS.WM_MOVE: {
+                sendEvent (DWT.Move);
+                return 0;
+            }
+            case OS.WM_SIZE: {
+                setScrollWidth ();
+                if (ignoreResize) return 0;
+                setResizeChildren (false);
+                int code = callWindowProc (hwnd, OS.WM_SIZE, wParam, lParam);
+                sendEvent (DWT.Resize);
+                if (isDisposed ()) return 0;
+                if (layout !is null) {
+                    markLayout (false, false);
+                    updateLayout (false, false);
+                }
+                setResizeChildren (true);
+                updateScrollBar ();
+                return code;
+            }
+            case OS.WM_NCPAINT: {
+                LRESULT result = wmNCPaint (hwnd, wParam, lParam);
+                if (result !is null) return result.value;
+                break;
+            }
+            case OS.WM_PRINT: {
+                LRESULT result = wmPrint (hwnd, wParam, lParam);
+                if (result !is null) return result.value;
+                break;
+            }
+            case OS.WM_COMMAND:
+            case OS.WM_NOTIFY:
+            case OS.WM_SYSCOLORCHANGE: {
+                return OS.SendMessage (handle, msg, wParam, lParam);
+            }
+            case OS.WM_HSCROLL: {
+                /*
+                * Bug on WinCE.  lParam should be NULL when the message is not sent
+                * by a scroll bar control, but it contains the handle to the window.
+                * When the message is sent by a scroll bar control, it correctly
+                * contains the handle to the scroll bar.  The fix is to check for
+                * both.
+                */
+                if (horizontalBar !is null && (lParam is 0 || lParam is hwndParent)) {
+                    wmScroll (horizontalBar, true, hwndParent, OS.WM_HSCROLL, wParam, lParam);
+                }
+                setScrollWidth ();
+                break;
+            }
+            case OS.WM_VSCROLL: {
+                SCROLLINFO info = new SCROLLINFO ();
+                info.cbSize = SCROLLINFO.sizeof;
+                info.fMask = OS.SIF_ALL;
+                OS.GetScrollInfo (hwndParent, OS.SB_VERT, info);
+                /*
+                * Update the nPos field to match the nTrackPos field
+                * so that the tree scrolls when the scroll bar of the
+                * parent is dragged.
+                *
+                * NOTE: For some reason, this code is only necessary
+                * on Windows Vista.
+                */
+                if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                    if ((wParam & 0xFFFF) is OS.SB_THUMBTRACK) {
+                        info.nPos = info.nTrackPos;
+                    }
+                }
+                OS.SetScrollInfo (handle, OS.SB_VERT, info, true);
+                int code = OS.SendMessage (handle, OS.WM_VSCROLL, wParam, lParam);
+                OS.GetScrollInfo (handle, OS.SB_VERT, info);
+                OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
+                return code;
+            }
+        }
+        return callWindowProc (hwnd, msg, wParam, lParam);
+    }
+    return super.windowProc (hwnd, msg, wParam, lParam);
+}
+
+LRESULT WM_CHAR (int wParam, int lParam) {
+    LRESULT result = super.WM_CHAR (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  The tree control beeps
+    * in WM_CHAR when the search for the item that
+    * matches the key stroke fails.  This is the
+    * standard tree behavior but is unexpected when
+    * the key that was typed was ESC, CR or SPACE.
+    * The fix is to avoid calling the tree window
+    * proc in these cases.
+    */
+    switch (wParam) {
+        case ' ': {
+            int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+            if (hItem !is 0) {
+                hAnchor = hItem;
+                OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hItem);
+                TVITEM tvItem = new TVITEM ();
+                tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE | OS.TVIF_PARAM;
+                tvItem.hItem = hItem;
+                if ((style & DWT.CHECK) !is 0) {
+                    tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+                    OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                    int state = tvItem.state >> 12;
+                    if ((state & 0x1) !is 0) {
+                        state++;
+                    } else  {
+                        --state;
+                    }
+                    tvItem.state = state << 12;
+                    OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                    if (!OS.IsWinCE) {
+                        int id = hItem;
+                        if (OS.COMCTL32_MAJOR >= 6) {
+                            id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, hItem, 0);
+                        }
+                        OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, id);
+                    }
+                }
+                tvItem.stateMask = OS.TVIS_SELECTED;
+                OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                if ((style & DWT.MULTI) !is 0 && OS.GetKeyState (OS.VK_CONTROL) < 0) {
+                    if ((tvItem.state & OS.TVIS_SELECTED) !is 0) {
+                        tvItem.state &= ~OS.TVIS_SELECTED;
+                    } else {
+                        tvItem.state |= OS.TVIS_SELECTED;
+                    }
+                } else {
+                    tvItem.state |= OS.TVIS_SELECTED;
+                }
+                OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                TreeItem item = _getItem (hItem, tvItem.lParam);
+                Event event = new Event ();
+                event.item = item;
+                postEvent (DWT.Selection, event);
+                if ((style & DWT.CHECK) !is 0) {
+                    event = new Event ();
+                    event.item = item;
+                    event.detail = DWT.CHECK;
+                    postEvent (DWT.Selection, event);
+                }
+            }
+            return LRESULT.ZERO;
+        }
+        case DWT.CR: {
+            /*
+            * Feature in Windows.  Windows sends NM_RETURN from WM_KEYDOWN
+            * instead of using WM_CHAR.  This means that application code
+            * that expects to consume the key press and therefore avoid a
+            * DWT.DefaultSelection event from WM_CHAR will fail.  The fix
+            * is to implement DWT.DefaultSelection in WM_CHAR instead of
+            * using NM_RETURN.
+            */
+            Event event = new Event ();
+            int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+            if (hItem !is 0) event.item = _getItem (hItem);
+            postEvent (DWT.DefaultSelection, event);
+            return LRESULT.ZERO;
+        }
+        case DWT.ESC:
+            return LRESULT.ZERO;
+    }
+    return result;
+}
+
+LRESULT WM_ERASEBKGND (int wParam, int lParam) {
+    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
+    if ((style & DWT.DOUBLE_BUFFERED) !is 0) return LRESULT.ONE;
+    if (findImageControl () !is null) return LRESULT.ONE;
+    return result;
+}
+
+LRESULT WM_GETOBJECT (int wParam, int lParam) {
+    /*
+    * Ensure that there is an accessible object created for this
+    * control because support for checked item and tree column
+    * accessibility is temporarily implemented in the accessibility
+    * package.
+    */
+    if ((style & DWT.CHECK) !is 0 || hwndParent !is 0) {
+        if (accessible is null) accessible = new_Accessible (this);
+    }
+    return super.WM_GETOBJECT (wParam, lParam);
+}
+
+LRESULT WM_KEYDOWN (int wParam, int lParam) {
+    LRESULT result = super.WM_KEYDOWN (wParam, lParam);
+    if (result !is null) return result;
+    switch (wParam) {
+        case OS.VK_SPACE:
+            /*
+            * Ensure that the window proc does not process VK_SPACE
+            * so that it can be handled in WM_CHAR.  This allows the
+            * application to cancel an operation that is normally
+            * performed in WM_KEYDOWN from WM_CHAR.
+            */
+            return LRESULT.ZERO;
+        case OS.VK_ADD:
+            if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
+                if (hwndHeader !is 0) {
+                    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                    TreeColumn [] newColumns = new TreeColumn [count];
+                    System.arraycopy (columns, 0, newColumns, 0, count);
+                    for (int i=0; i<count; i++) {
+                        TreeColumn column = newColumns [i];
+                        if (!column.isDisposed () && column.getResizable ()) {
+                            column.pack ();
+                        }
+                    }
+                }
+            }
+            break;
+        case OS.VK_UP:
+        case OS.VK_DOWN:
+        case OS.VK_PRIOR:
+        case OS.VK_NEXT:
+        case OS.VK_HOME:
+        case OS.VK_END: {
+            OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
+            if ((style & DWT.SINGLE) !is 0) break;
+            if (OS.GetKeyState (OS.VK_SHIFT) < 0) {
+                int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+                if (hItem !is 0) {
+                    if (hAnchor is 0) hAnchor = hItem;
+                    ignoreSelect = ignoreDeselect = true;
+                    int code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
+                    ignoreSelect = ignoreDeselect = false;
+                    int hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+                    TVITEM tvItem = new TVITEM ();
+                    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+                    tvItem.stateMask = OS.TVIS_SELECTED;
+                    int hDeselectItem = hItem;
+                    RECT rect1 = new RECT ();
+                    rect1.left = hAnchor;
+                    OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect1);
+                    RECT rect2 = new RECT ();
+                    rect2.left = hDeselectItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect2);
+                    int flags = rect1.top < rect2.top ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE;
+                    while (hDeselectItem !is hAnchor) {
+                        tvItem.hItem = hDeselectItem;
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                        hDeselectItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hDeselectItem);
+                    }
+                    int hSelectItem = hAnchor;
+                    rect1.left = hNewItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect1);
+                    rect2.left = hSelectItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect2);
+                    tvItem.state = OS.TVIS_SELECTED;
+                    flags = rect1.top < rect2.top ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE;
+                    while (hSelectItem !is hNewItem) {
+                        tvItem.hItem = hSelectItem;
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                        hSelectItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hSelectItem);
+                    }
+                    tvItem.hItem = hNewItem;
+                    OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+                    tvItem.hItem = hNewItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                    Event event = new Event ();
+                    event.item = _getItem (hNewItem, tvItem.lParam);
+                    postEvent (DWT.Selection, event);
+                    return new LRESULT (code);
+                }
+            }
+            if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
+                int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+                if (hItem !is 0) {
+                    TVITEM tvItem = new TVITEM ();
+                    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+                    tvItem.stateMask = OS.TVIS_SELECTED;
+                    tvItem.hItem = hItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                    bool oldSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0;
+                    int hNewItem = 0;
+                    switch (wParam) {
+                        case OS.VK_UP:
+                            hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUSVISIBLE, hItem);
+                            break;
+                        case OS.VK_DOWN:
+                            hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
+                            break;
+                        case OS.VK_HOME:
+                            hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+                            break;
+                        case OS.VK_PRIOR:
+                            hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+                            if (hNewItem is hItem) {
+                                OS.SendMessage (handle, OS.WM_VSCROLL, OS.SB_PAGEUP, 0);
+                                hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+                            }
+                            break;
+                        case OS.VK_NEXT:
+                            RECT rect = new RECT (), clientRect = new RECT ();
+                            OS.GetClientRect (handle, clientRect);
+                            hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+                            do {
+                                int hVisible = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hNewItem);
+                                if (hVisible is 0) break;
+                                rect.left = hVisible;
+                                OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect);
+                                if (rect.bottom > clientRect.bottom) break;
+                                if ((hNewItem = hVisible) is hItem) {
+                                    OS.SendMessage (handle, OS.WM_VSCROLL, OS.SB_PAGEDOWN, 0);
+                                }
+                            } while (hNewItem !is 0);
+                            break;
+                        case OS.VK_END:
+                            hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0);
+                            break;
+                    }
+                    if (hNewItem !is 0) {
+                        OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hNewItem);
+                        tvItem.hItem = hNewItem;
+                        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                        bool newSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0;
+                        bool redraw = !newSelected && drawCount is 0 && OS.IsWindowVisible (handle);
+                        if (redraw) {
+                            OS.UpdateWindow (handle);
+                            OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+                        }
+                        hSelect = hNewItem;
+                        ignoreSelect = true;
+                        OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hNewItem);
+                        ignoreSelect = false;
+                        hSelect = 0;
+                        if (oldSelected) {
+                            tvItem.state = OS.TVIS_SELECTED;
+                            tvItem.hItem = hItem;
+                            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                        }
+                        if (!newSelected) {
+                            tvItem.state = 0;
+                            tvItem.hItem = hNewItem;
+                            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                        }
+                        if (redraw) {
+                            RECT rect1 = new RECT (), rect2 = new RECT ();
+                            rect1.left = hItem;  rect2.left = hNewItem;
+                            int fItemRect = (style & DWT.FULL_SELECTION) !is 0 ? 0 : 1;
+                            if (hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) fItemRect = 0;
+                            if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) fItemRect = 0;
+                            OS.SendMessage (handle, OS.TVM_GETITEMRECT, fItemRect, rect1);
+                            OS.SendMessage (handle, OS.TVM_GETITEMRECT, fItemRect, rect2);
+                            OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+                            OS.InvalidateRect (handle, rect1, true);
+                            OS.InvalidateRect (handle, rect2, true);
+                            OS.UpdateWindow (handle);
+                        }
+                        return LRESULT.ZERO;
+                    }
+                }
+            }
+            int code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
+            hAnchor = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+            return new LRESULT (code);
+        }
+    }
+    return result;
+}
+
+LRESULT WM_KILLFOCUS (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  When a tree item that has an image
+    * with alpha is expanded or collapsed, the area where
+    * the image is drawn is not erased before it is drawn.
+    * This means that the image gets darker each time.
+    * The fix is to redraw the selection.
+    *
+    * Feature in Windows.  When multiple item have
+    * the TVIS_SELECTED state, Windows redraws only
+    * the focused item in the color used to show the
+    * selection when the tree loses or gains focus.
+    * The fix is to force Windows to redraw the
+    * selection when focus is gained or lost.
+    */
+    bool redraw = (style & DWT.MULTI) !is 0;
+    if (!redraw) {
+        if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) {
+            if (imageList !is null) {
+                int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+                if ((bits & OS.TVS_FULLROWSELECT) is 0) {
+                    redraw = true;
+                }
+            }
+        }
+    }
+    if (redraw) redrawSelection ();
+    return super.WM_KILLFOCUS (wParam, lParam);
+}
+
+LRESULT WM_LBUTTONDBLCLK (int wParam, int lParam) {
+    TVHITTESTINFO lpht = new TVHITTESTINFO ();
+    lpht.x = (short) (lParam & 0xFFFF);
+    lpht.y = (short) (lParam >> 16);
+    OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
+    if (lpht.hItem !is 0) {
+        if ((style & DWT.CHECK) !is 0) {
+            if ((lpht.flags & OS.TVHT_ONITEMSTATEICON) !is 0) {
+                Display display = this.display;
+                display.captureChanged = false;
+                sendMouseEvent (DWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam);
+                if (!sendMouseEvent (DWT.MouseDoubleClick, 1, handle, OS.WM_LBUTTONDBLCLK, wParam, lParam)) {
+                    if (!display.captureChanged && !isDisposed ()) {
+                        if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+                    }
+                    return LRESULT.ZERO;
+                }
+                if (!display.captureChanged && !isDisposed ()) {
+                    if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+                }
+                OS.SetFocus (handle);
+                TVITEM tvItem = new TVITEM ();
+                tvItem.hItem = lpht.hItem;
+                tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
+                tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+                OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                int state = tvItem.state >> 12;
+                if ((state & 0x1) !is 0) {
+                    state++;
+                } else  {
+                    --state;
+                }
+                tvItem.state = state << 12;
+                OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                if (!OS.IsWinCE) {
+                    int id = tvItem.hItem;
+                    if (OS.COMCTL32_MAJOR >= 6) {
+                        id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0);
+                    }
+                    OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, id);
+                }
+                Event event = new Event ();
+                event.item = _getItem (tvItem.hItem, tvItem.lParam);
+                event.detail = DWT.CHECK;
+                postEvent (DWT.Selection, event);
+                return LRESULT.ZERO;
+            }
+        }
+    }
+    LRESULT result = super.WM_LBUTTONDBLCLK (wParam, lParam);
+    if (result is LRESULT.ZERO) return result;
+    if (lpht.hItem !is 0) {
+        if ((style & DWT.FULL_SELECTION) !is 0 || (lpht.flags & OS.TVHT_ONITEM) !is 0) {
+            Event event = new Event ();
+            event.item = _getItem (lpht.hItem);
+            postEvent (DWT.DefaultSelection, event);
+        }
+    }
+    return result;
+}
+
+LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
+    /*
+    * In a multi-select tree, if the user is collapsing a subtree that
+    * contains selected items, clear the selection from these items and
+    * issue a selection event.  Only items that are selected and visible
+    * are cleared.  This code also runs in the case when the white space
+    * below the last item is selected.
+    */
+    TVHITTESTINFO lpht = new TVHITTESTINFO ();
+    lpht.x = (short) (lParam & 0xFFFF);
+    lpht.y = (short) (lParam >> 16);
+    OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
+    if (lpht.hItem is 0 || (lpht.flags & OS.TVHT_ONITEMBUTTON) !is 0) {
+        Display display = this.display;
+        display.captureChanged = false;
+        if (!sendMouseEvent (DWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) {
+            if (!display.captureChanged && !isDisposed ()) {
+                if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+            }
+            return LRESULT.ZERO;
+        }
+        bool fixSelection = false, deselected = false;
+        if (lpht.hItem !is 0 && (style & DWT.MULTI) !is 0) {
+            int hSelection = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+            if (hSelection !is 0) {
+                TVITEM tvItem = new TVITEM ();
+                tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+                tvItem.hItem = lpht.hItem;
+                OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                if ((tvItem.state & OS.TVIS_EXPANDED) !is 0) {
+                    fixSelection = true;
+                    tvItem.stateMask = OS.TVIS_SELECTED;
+                    int hNext = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, lpht.hItem);
+                    while (hNext !is 0) {
+                        tvItem.hItem = hNext;
+                        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                        if ((tvItem.state & OS.TVIS_SELECTED) !is 0) deselected = true;
+                        tvItem.state = 0;
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                        int hItem = hNext = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hNext);
+                        while (hItem !is 0 && hItem !is lpht.hItem) {
+                            hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
+                        }
+                        if (hItem is 0) break;
+                    }
+                }
+            }
+        }
+        dragStarted = gestureCompleted = false;
+        if (fixSelection) ignoreDeselect = ignoreSelect = lockSelection = true;
+        int code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam);
+        if (fixSelection) ignoreDeselect = ignoreSelect = lockSelection = false;
+        if (dragStarted) {
+            if (!display.captureChanged && !isDisposed ()) {
+                if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+            }
+        }
+        if (deselected) {
+            Event event = new Event ();
+            event.item = _getItem (lpht.hItem);
+            postEvent (DWT.Selection, event);
+        }
+        return new LRESULT (code);
+    }
+
+    /* Look for check/uncheck */
+    if ((style & DWT.CHECK) !is 0) {
+        if ((lpht.flags & OS.TVHT_ONITEMSTATEICON) !is 0) {
+            Display display = this.display;
+            display.captureChanged = false;
+            if (!sendMouseEvent (DWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) {
+                if (!display.captureChanged && !isDisposed ()) {
+                    if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+                }
+                return LRESULT.ZERO;
+            }
+            if (!display.captureChanged && !isDisposed ()) {
+                if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+            }
+            OS.SetFocus (handle);
+            TVITEM tvItem = new TVITEM ();
+            tvItem.hItem = lpht.hItem;
+            tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
+            tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+            OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+            int state = tvItem.state >> 12;
+            if ((state & 0x1) !is 0) {
+                state++;
+            } else  {
+                --state;
+            }
+            tvItem.state = state << 12;
+            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+            if (!OS.IsWinCE) {
+                int id = tvItem.hItem;
+                if (OS.COMCTL32_MAJOR >= 6) {
+                    id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0);
+                }
+                OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, id);
+            }
+            Event event = new Event ();
+            event.item = _getItem (tvItem.hItem, tvItem.lParam);
+            event.detail = DWT.CHECK;
+            postEvent (DWT.Selection, event);
+            return LRESULT.ZERO;
+        }
+    }
+
+    /* Process the mouse when an item is not selected */
+    if ((style & DWT.FULL_SELECTION) is 0) {
+        if ((lpht.flags & OS.TVHT_ONITEM) is 0) {
+            Display display = this.display;
+            display.captureChanged = false;
+            if (!sendMouseEvent (DWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) {
+                if (!display.captureChanged && !isDisposed ()) {
+                    if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+                }
+                return LRESULT.ZERO;
+            }
+            int code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam);
+            if (!display.captureChanged && !isDisposed ()) {
+                if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+            }
+            return new LRESULT (code);
+        }
+    }
+
+    /* Get the selected state of the item under the mouse */
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+    tvItem.stateMask = OS.TVIS_SELECTED;
+    bool hittestSelected = false, focused = false;
+    if ((style & DWT.MULTI) !is 0) {
+        tvItem.hItem = lpht.hItem;
+        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+        hittestSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0;
+        focused = OS.GetFocus () is handle;
+    }
+
+    /* Get the selected state of the last selected item */
+    bool redraw = false;
+    int hOldItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+    if ((style & DWT.MULTI) !is 0) {
+        tvItem.hItem = hOldItem;
+        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+
+        /* Check for CONTROL or drag selection */
+        if (hittestSelected || (wParam & OS.MK_CONTROL) !is 0) {
+            /*
+            * Feature in Windows.  When the tree is not drawing focus
+            * and the user selects a tree item using while the CONTROL
+            * key is down, the tree window proc sends WM_UPDATEUISTATE
+            * to the top level window, causing controls within the shell
+            * to redraw.  When drag detect is enabled, the tree window
+            * proc runs a modal loop that allows WM_PAINT messages to be
+            * delivered during WM_LBUTTONDOWN.  When WM_SETREDRAW is used
+            * to disable drawing for the tree and a WM_PAINT happens for
+            * a parent of the tree (or a sibling that overlaps), the parent
+            * will draw on top of the tree.  If WM_SETREDRAW is turned back
+            * on without redrawing the entire tree, pixel corruption occurs.
+            * This case only seems to happen when the tree has been given
+            * focus from WM_MOUSEACTIVATE of the shell.  The fix is to
+            * detect that WM_UPDATEUISTATE will be sent and avoid using
+            * WM_SETREDRAW to disable drawing.
+            *
+            * NOTE:  Any redraw of a parent (or sibling) will be dispatched
+            * during the modal drag detect loop.  This code only fixes the
+            * case where the tree causes a redraw from WM_UPDATEUISTATE.
+            * In DWT, the InvalidateRect() that causes the pixel corruption
+            * is found in Composite.WM_UPDATEUISTATE().
+            */
+            int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
+            if ((uiState & OS.UISF_HIDEFOCUS) is 0) {
+                redraw = focused && drawCount is 0 && OS.IsWindowVisible (handle);
+            }
+            if (redraw) {
+                OS.UpdateWindow (handle);
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
+            }
+        } else {
+            deselectAll ();
+        }
+    }
+
+    /* Do the selection */
+    Display display = this.display;
+    display.captureChanged = false;
+    if (!sendMouseEvent (DWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) {
+        if (!display.captureChanged && !isDisposed ()) {
+            if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+        }
+        return LRESULT.ZERO;
+    }
+    hSelect = lpht.hItem;
+    dragStarted = gestureCompleted = false;
+    ignoreDeselect = ignoreSelect = true;
+    int code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam);
+    int hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+    /*
+    * Feature in Windows.  When the tree has the style
+    * TVS_FULLROWSELECT, the background color for the
+    * entire row is filled when an item is painted,
+    * drawing on top of any custom drawing.  The fix
+    * is to emulate TVS_FULLROWSELECT.
+    */
+    if ((style & DWT.FULL_SELECTION) !is 0) {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if ((bits & OS.TVS_FULLROWSELECT) is 0) {
+            if (hNewItem is hOldItem && lpht.hItem !is hOldItem) {
+                OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, lpht.hItem);
+                hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+            }
+            if (!dragStarted && lpht.hItem !is 0 && (state & DRAG_DETECT) !is 0 && hooks (DWT.DragDetect)) {
+                dragStarted = dragDetect (handle, lpht.x, lpht.y, false, null, null);
+            }
+        }
+    }
+    ignoreDeselect = ignoreSelect = false;
+    hSelect = 0;
+    if (dragStarted) {
+        if (!display.captureChanged && !isDisposed ()) {
+            if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+        }
+    }
+
+    /*
+    * Feature in Windows.  When the old and new focused item
+    * are the same, Windows does not check to make sure that
+    * the item is actually selected, not just focused.  The
+    * fix is to force the item to draw selected by setting
+    * the state mask.  This is only necessary when the tree
+    * is single select.
+    */
+    if ((style & DWT.SINGLE) !is 0) {
+        if (hOldItem is hNewItem) {
+            tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+            tvItem.state = OS.TVIS_SELECTED;
+            tvItem.stateMask = OS.TVIS_SELECTED;
+            tvItem.hItem = hNewItem;
+            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+        }
+    }
+
+    /* Reselect the last item that was unselected */
+    if ((style & DWT.MULTI) !is 0) {
+
+        /* Check for CONTROL and reselect the last item */
+        if (hittestSelected || (wParam & OS.MK_CONTROL) !is 0) {
+            if (hOldItem is hNewItem && hOldItem is lpht.hItem) {
+                if ((wParam & OS.MK_CONTROL) !is 0) {
+                    tvItem.state ^= OS.TVIS_SELECTED;
+                    if (dragStarted) tvItem.state = OS.TVIS_SELECTED;
+                    OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                }
+            } else {
+                if ((tvItem.state & OS.TVIS_SELECTED) !is 0) {
+                    tvItem.state = OS.TVIS_SELECTED;
+                    OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                }
+                if ((wParam & OS.MK_CONTROL) !is 0 && !dragStarted) {
+                    if (hittestSelected) {
+                        tvItem.state = 0;
+                        tvItem.hItem = lpht.hItem;
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                    }
+                }
+            }
+            if (redraw) {
+                RECT rect1 = new RECT (), rect2 = new RECT ();
+                rect1.left = hOldItem;  rect2.left = hNewItem;
+                int fItemRect = (style & DWT.FULL_SELECTION) !is 0 ? 0 : 1;
+                if (hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) fItemRect = 0;
+                if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) fItemRect = 0;
+                OS.SendMessage (handle, OS.TVM_GETITEMRECT, fItemRect, rect1);
+                OS.SendMessage (handle, OS.TVM_GETITEMRECT, fItemRect, rect2);
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+                OS.InvalidateRect (handle, rect1, true);
+                OS.InvalidateRect (handle, rect2, true);
+                OS.UpdateWindow (handle);
+            }
+        }
+
+        /* Check for SHIFT or normal select and deselect/reselect items */
+        if ((wParam & OS.MK_CONTROL) is 0) {
+            if (!hittestSelected || !dragStarted) {
+                tvItem.state = 0;
+                int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC);
+                OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc);
+                if ((style & DWT.VIRTUAL) !is 0) {
+                    int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+                    deselect (hItem, tvItem, hNewItem);
+                } else {
+                    for (int i=0; i<items.length; i++) {
+                        TreeItem item = items [i];
+                        if (item !is null && item.handle !is hNewItem) {
+                            tvItem.hItem = item.handle;
+                            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                        }
+                    }
+                }
+                tvItem.hItem = hNewItem;
+                tvItem.state = OS.TVIS_SELECTED;
+                OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc);
+                if ((wParam & OS.MK_SHIFT) !is 0) {
+                    RECT rect1 = new RECT ();
+                    if (hAnchor is 0) hAnchor = hNewItem;
+                    rect1.left = hAnchor;
+                    if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect1) !is 0) {
+                        RECT rect2 = new RECT ();
+                        rect2.left = hNewItem;
+                        OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect2);
+                        int flags = rect1.top < rect2.top ? OS.TVGN_NEXTVISIBLE : OS.TVGN_PREVIOUSVISIBLE;
+                        tvItem.state = OS.TVIS_SELECTED;
+                        int hItem = tvItem.hItem = hAnchor;
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                        while (hItem !is hNewItem) {
+                            tvItem.hItem = hItem;
+                            OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                            hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hItem);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if ((wParam & OS.MK_SHIFT) is 0) hAnchor = hNewItem;
+
+    /* Issue notification */
+    if (!gestureCompleted) {
+        tvItem.hItem = hNewItem;
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+        OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+        Event event = new Event ();
+        event.item = _getItem (tvItem.hItem, tvItem.lParam);
+        postEvent (DWT.Selection, event);
+    }
+    gestureCompleted = false;
+
+    /*
+    * Feature in Windows.  Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
+    * the widget starts a modal loop to determine if the user wants
+    * to begin a drag/drop operation or marquee select.  Unfortunately,
+    * this modal loop eats the corresponding mouse up.  The fix is to
+    * detect the cases when the modal loop has eaten the mouse up and
+    * issue a fake mouse up.
+    */
+    if (dragStarted) {
+        sendDragEvent (1, (short) (lParam & 0xFFFF), (short) (lParam >> 16));
+    } else {
+        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+        if ((bits & OS.TVS_DISABLEDRAGDROP) is 0) {
+            sendMouseEvent (DWT.MouseUp, 1, handle, OS.WM_LBUTTONUP, wParam, lParam);
+        }
+    }
+    dragStarted = false;
+    return new LRESULT (code);
+}
+
+LRESULT WM_MOUSEMOVE (int wParam, int lParam) {
+    Display display = this.display;
+    LRESULT result = super.WM_MOUSEMOVE (wParam, lParam);
+    if (result !is null) return result;
+    if (itemToolTipHandle !is 0 && hwndHeader !is 0) {
+        /*
+        * 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 (((wParam & 0xFFFF) & mask) is 0) {
+            TVHITTESTINFO lpht = new TVHITTESTINFO ();
+            lpht.x = (short) (lParam & 0xFFFF);
+            lpht.y = (short) (lParam >> 16);
+            OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
+            if (lpht.hItem !is 0) {
+                int hDC = OS.GetDC (handle);
+                int oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+                if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+                POINT pt = new POINT();
+                pt.x = lpht.x;
+                pt.y = lpht.y;
+                RECT rect = new RECT ();
+                OS.GetClientRect (hwndParent, rect);
+                OS.MapWindowPoints (hwndParent, handle, rect, 2);
+                TreeItem item = _getItem (lpht.hItem);
+                int index = 0, count = Math.max (1, OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0));
+                int [] order = new int [count];
+                OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
+                while (index < count) {
+                    int hFont = item.cellFont !is null ? item.cellFont [order [index]] : -1;
+                    if (hFont is -1) hFont = item.font;
+                    if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+                    RECT cellRect = item.getBounds (order [index], true, false, true, false, true, hDC);
+                    if (hFont !is -1) OS.SelectObject (hDC, hFont);
+                    if (cellRect.left > rect.right) break;
+                    cellRect.right = Math.min (cellRect.right, rect.right);
+                    if (OS.PtInRect (cellRect, pt)) {
+                        RECT textRect = item.getBounds (order [index], true, false, false, false, false, hDC);
+                        if (textRect.right > cellRect.right) {
+                            TOOLINFO lpti = new TOOLINFO ();
+                            lpti.cbSize = TOOLINFO.sizeof;
+                            lpti.hwnd = handle;
+                            lpti.uId = handle;
+                            lpti.uFlags = OS.TTF_SUBCLASS | OS.TTF_TRANSPARENT;
+                            lpti.left = cellRect.left;
+                            lpti.top = cellRect.top;
+                            lpti.right = cellRect.right;
+                            lpti.bottom = cellRect.bottom;
+                            OS.SendMessage (itemToolTipHandle, OS.TTM_NEWTOOLRECT, 0, lpti);
+                        }
+                        break;
+                    }
+                    index++;
+                }
+                if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+                OS.ReleaseDC (handle, hDC);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT WM_MOVE (int wParam, int lParam) {
+    if (ignoreResize) return null;
+    return super.WM_MOVE (wParam, lParam);
+}
+
+LRESULT WM_RBUTTONDOWN (int wParam, int lParam) {
+    /*
+    * Feature in Windows.  The receiver uses WM_RBUTTONDOWN
+    * to initiate a drag/drop operation depending on how the
+    * user moves the mouse.  If the user clicks the right button,
+    * without moving the mouse, the tree consumes the corresponding
+    * WM_RBUTTONUP.  The fix is to avoid calling the window proc for
+    * the tree.
+    */
+    Display display = this.display;
+    display.captureChanged = false;
+    if (!sendMouseEvent (DWT.MouseDown, 3, handle, OS.WM_RBUTTONDOWN, wParam, lParam)) {
+        if (!display.captureChanged && !isDisposed ()) {
+            if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+        }
+        return LRESULT.ZERO;
+    }
+    /*
+    * This code is intentionally commented.
+    */
+//  if (OS.GetCapture () !is handle) OS.SetCapture (handle);
+    setFocus ();
+
+    /*
+    * Feature in Windows.  When the user selects a tree item
+    * with the right mouse button, the item remains selected
+    * only as long as the user does not release or move the
+    * mouse.  As soon as this happens, the selection snaps
+    * back to the previous selection.  This behavior can be
+    * observed in the Explorer but is not instantly apparent
+    * because the Explorer explicitly sets the selection when
+    * the user chooses a menu item.  If the user cancels the
+    * menu, the selection snaps back.  The fix is to avoid
+    * calling the window proc and do the selection ourselves.
+    * This behavior is consistent with the table.
+    */
+    TVHITTESTINFO lpht = new TVHITTESTINFO ();
+    lpht.x = (short) (lParam & 0xFFFF);
+    lpht.y = (short) (lParam >> 16);
+    OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
+    if (lpht.hItem !is 0) {
+        int flags = OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL;
+        if ((style & DWT.FULL_SELECTION) !is 0 || (lpht.flags & flags) !is 0) {
+            if ((wParam & (OS.MK_CONTROL | OS.MK_SHIFT)) is 0) {
+                TVITEM tvItem = new TVITEM ();
+                tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+                tvItem.stateMask = OS.TVIS_SELECTED;
+                tvItem.hItem = lpht.hItem;
+                OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                if ((tvItem.state & OS.TVIS_SELECTED) is 0) {
+                    ignoreSelect = true;
+                    OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, 0);
+                    ignoreSelect = false;
+                    OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, lpht.hItem);
+                }
+            }
+        }
+    }
+    return LRESULT.ZERO;
+}
+
+LRESULT WM_PAINT (int wParam, int lParam) {
+    if (shrink && !ignoreShrink) {
+        /* Resize the item array to fit the last item */
+        int count = items.length - 1;
+        while (count >= 0) {
+            if (items [count] !is null) break;
+            --count;
+        }
+        count++;
+        if (items.length > 4 && items.length - count > 3) {
+            int length = Math.max (4, (count + 3) / 4 * 4);
+            TreeItem [] newItems = new TreeItem [length];
+            System.arraycopy (items, 0, newItems, 0, count);
+            items = newItems;
+        }
+        shrink = false;
+    }
+    if ((style & DWT.DOUBLE_BUFFERED) !is 0 || findImageControl () !is null) {
+        bool doubleBuffer = true;
+        if (EXPLORER_THEME) {
+            if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                int exStyle = OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0);
+                if ((exStyle & OS.TVS_EX_DOUBLEBUFFER) !is 0) doubleBuffer = false;
+            }
+        }
+        if (doubleBuffer) {
+            GC gc = null;
+            int paintDC = 0;
+            PAINTSTRUCT ps = new PAINTSTRUCT ();
+            bool hooksPaint = hooks (DWT.Paint);
+            if (hooksPaint) {
+                GCData data = new GCData ();
+                data.ps = ps;
+                data.hwnd = handle;
+                gc = GC.win32_new (this, data);
+                paintDC = gc.handle;
+            } else {
+                paintDC = OS.BeginPaint (handle, ps);
+            }
+            int width = ps.right - ps.left;
+            int height = ps.bottom - ps.top;
+            if (width !is 0 && height !is 0) {
+                int hDC = OS.CreateCompatibleDC (paintDC);
+                POINT lpPoint1 = new POINT (), lpPoint2 = new POINT ();
+                OS.SetWindowOrgEx (hDC, ps.left, ps.top, lpPoint1);
+                OS.SetBrushOrgEx (hDC, ps.left, ps.top, lpPoint2);
+                int hBitmap = OS.CreateCompatibleBitmap (paintDC, width, height);
+                int hOldBitmap = OS.SelectObject (hDC, hBitmap);
+                RECT rect = new RECT ();
+                OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
+                drawBackground (hDC, rect);
+                callWindowProc (handle, OS.WM_PAINT, hDC, 0);
+                OS.SetWindowOrgEx (hDC, lpPoint1.x, lpPoint1.y, null);
+                OS.SetBrushOrgEx (hDC, lpPoint2.x, lpPoint2.y, null);
+                OS.BitBlt (paintDC, ps.left, ps.top, width, height, hDC, 0, 0, OS.SRCCOPY);
+                OS.SelectObject (hDC, hOldBitmap);
+                OS.DeleteObject (hBitmap);
+                OS.DeleteObject (hDC);
+                if (hooksPaint) {
+                    Event event = new Event ();
+                    event.gc = gc;
+                    event.x = ps.left;
+                    event.y = ps.top;
+                    event.width = ps.right - ps.left;
+                    event.height = ps.bottom - ps.top;
+                    sendEvent (DWT.Paint, event);
+                    // widget could be disposed at this point
+                    event.gc = null;
+                }
+            }
+            if (hooksPaint) {
+                gc.dispose ();
+            } else {
+                OS.EndPaint (handle, ps);
+            }
+            return LRESULT.ZERO;
+        }
+    }
+    return super.WM_PAINT (wParam, lParam);
+}
+
+LRESULT WM_PRINTCLIENT (int wParam, int lParam) {
+    LRESULT result = super.WM_PRINTCLIENT (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Feature in Windows.  For some reason, when WM_PRINT is used
+    * to capture an image of a hierarchy that contains a tree with
+    * columns, the clipping that is used to stop the first column
+    * from drawing on top of subsequent columns stops the first
+    * column and the tree lines from drawing.  This does not happen
+    * during WM_PAINT.  The fix is to draw without clipping and
+    * then draw the rest of the columns on top.  Since the drawing
+    * is happening in WM_PRINTCLIENT, the redrawing is not visible.
+    */
+    printClient = true;
+    int code = callWindowProc (handle, OS.WM_PRINTCLIENT, wParam, lParam);
+    printClient = false;
+    return new LRESULT (code);
+}
+
+LRESULT WM_SETFOCUS (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  When a tree item that has an image
+    * with alpha is expanded or collapsed, the area where
+    * the image is drawn is not erased before it is drawn.
+    * This means that the image gets darker each time.
+    * The fix is to redraw the selection.
+    *
+    * Feature in Windows.  When multiple item have
+    * the TVIS_SELECTED state, Windows redraws only
+    * the focused item in the color used to show the
+    * selection when the tree loses or gains focus.
+    * The fix is to force Windows to redraw the
+    * selection when focus is gained or lost.
+    */
+    bool redraw = (style & DWT.MULTI) !is 0;
+    if (!redraw) {
+        if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) {
+            if (imageList !is null) {
+                int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+                if ((bits & OS.TVS_FULLROWSELECT) is 0) {
+                    redraw = true;
+                }
+            }
+        }
+    }
+    if (redraw) redrawSelection ();
+    return super.WM_SETFOCUS (wParam, lParam);
+}
+
+LRESULT WM_SETFONT (int wParam, int lParam) {
+    LRESULT result = super.WM_SETFONT (wParam, lParam);
+    if (result !is null) return result;
+    if (hwndHeader !is 0) {
+        /*
+        * Bug in Windows.  When a header has a sort indicator
+        * triangle, Windows resizes the indicator based on the
+        * size of the n-1th font.  The fix is to always make
+        * the n-1th font be the default.  This makes the sort
+        * indicator always be the default size.
+        */
+        OS.SendMessage (hwndHeader, OS.WM_SETFONT, 0, lParam);
+        OS.SendMessage (hwndHeader, OS.WM_SETFONT, wParam, lParam);
+    }
+    if (itemToolTipHandle !is 0) {
+        OS.SendMessage (itemToolTipHandle, OS.WM_SETFONT, wParam, lParam);
+    }
+    if (headerToolTipHandle !is 0) {
+        OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam);
+    }
+    return result;
+}
+
+LRESULT WM_SETREDRAW (int wParam, int lParam) {
+    LRESULT result = super.WM_SETREDRAW (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug in Windows.  Under certain circumstances, when
+    * WM_SETREDRAW is used to turn off drawing and then
+    * TVM_GETITEMRECT is sent to get the bounds of an item
+    * that is not inside the client area, Windows segment
+    * faults.  The fix is to call the default window proc
+    * rather than the default tree proc.
+    *
+    * NOTE:  This problem is intermittent and happens on
+    * Windows Vista running under the theme manager.
+    */
+    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+        int code = OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
+        return code is 0 ? LRESULT.ZERO : new LRESULT (code);
+    }
+    return result;
+}
+
+LRESULT WM_SIZE (int wParam, int lParam) {
+    /*
+    * Bug in Windows.  When TVS_NOHSCROLL is set when the
+    * size of the tree is zero, the scroll bar is shown the
+    * next time the tree resizes.  The fix is to hide the
+    * scroll bar every time the tree is resized.
+    */
+    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+    if ((bits & OS.TVS_NOHSCROLL) !is 0) {
+        if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false);
+    }
+    /*
+    * Bug in Windows.  On Vista, when the Explorer theme
+    * is used with a full selection tree, when the tree
+    * is resized to be smaller, the rounded right edge
+    * of the selected items is not drawn.  The fix is the
+    * redraw the entire tree.
+    */
+    if (EXPLORER_THEME) {
+        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+            if ((style & DWT.FULL_SELECTION) !is 0) {
+                OS.InvalidateRect (handle, null, false);
+            }
+        }
+    }
+    if (ignoreResize) return null;
+    return super.WM_SIZE (wParam, lParam);
+}
+
+LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) {
+    LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
+    if (result !is null) return result;
+    /*
+    * Bug in Windows.  When the tree is using the explorer
+    * theme, it does not use COLOR_WINDOW_TEXT for the
+    * default foreground color.  The fix is to explicitly
+    * set the foreground.
+    */
+    if (explorerTheme) {
+        if (foreground is -1) setForegroundPixel (-1);
+    }
+    if ((style & DWT.CHECK) !is 0) setCheckboxImageList ();
+    return result;
+}
+
+LRESULT wmColorChild (int wParam, int lParam) {
+    if (findImageControl () !is null) {
+        if (OS.COMCTL32_MAJOR < 6) {
+            return super.wmColorChild (wParam, lParam);
+        }
+        return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH));
+    }
+    /*
+    * Feature in Windows.  Tree controls send WM_CTLCOLOREDIT
+    * to allow application code to change the default colors.
+    * This is undocumented and conflicts with TVM_SETTEXTCOLOR
+    * and TVM_SETBKCOLOR, the documented way to do this.  The
+    * fix is to ignore WM_CTLCOLOREDIT messages from trees.
+    */
+    return null;
+}
+
+LRESULT wmNotify (NMHDR hdr, int wParam, int lParam) {
+    if (hdr.hwndFrom is itemToolTipHandle && hwndHeader !is 0) {
+        if (!OS.IsWinCE) {
+            switch (hdr.code) {
+                case OS.TTN_POP: {
+                    if (display.isXMouseActive ()) {
+                        Shell shell = getShell ();
+                        shell.lockToolTipControl = null;
+                    }
+                    break;
+                }
+                case OS.TTN_SHOW: {
+                    if (display.isXMouseActive ()) {
+                        Shell shell = getShell ();
+                        shell.lockToolTipControl = this;
+                    }
+                    int pos = OS.GetMessagePos ();
+                    POINT pt = new POINT();
+                    pt.x = (short) (pos & 0xFFFF);
+                    pt.y = (short) (pos >> 16);
+                    OS.ScreenToClient (handle, pt);
+                    TVHITTESTINFO lpht = new TVHITTESTINFO ();
+                    lpht.x = pt.x;
+                    lpht.y = pt.y;
+                    OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
+                    if (lpht.hItem !is 0) {
+                        int hDC = OS.GetDC (handle);
+                        int oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
+                        if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+                        LRESULT result = null;
+                        RECT rect = new RECT ();
+                        OS.GetClientRect (hwndParent, rect);
+                        OS.MapWindowPoints (hwndParent, handle, rect, 2);
+                        TreeItem item = _getItem (lpht.hItem);
+                        int index = 0, count = Math.max (1, OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0));
+                        int [] order = new int [count];
+                        OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
+                        while (index < count) {
+                            int hFont = item.cellFont !is null ? item.cellFont [order [index]] : -1;
+                            if (hFont is -1) hFont = item.font;
+                            if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+                            RECT cellRect = item.getBounds (order [index], true, false, true, false, true, hDC);
+                            if (hFont !is -1) OS.SelectObject (hDC, hFont);
+                            if (cellRect.left > rect.right) break;
+                            cellRect.right = Math.min (cellRect.right, rect.right);
+                            if (OS.PtInRect (cellRect, pt)) {
+                                RECT textRect = item.getBounds (order [index], true, false, false, false, false, hDC);
+                                if (textRect.right > cellRect.right) {
+                                    OS.MapWindowPoints (handle, 0, textRect, 2);
+                                    int flags = OS.SWP_NOACTIVATE | OS.SWP_NOSIZE | OS.SWP_NOZORDER;
+                                    SetWindowPos (itemToolTipHandle, 0, textRect.left, textRect.top, 0, 0, flags);
+                                    result = LRESULT.ONE;
+                                }
+                                break;
+                            }
+                            index++;
+                        }
+                        if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+                        OS.ReleaseDC (handle, hDC);
+                        if (result !is null) return result;
+                    }
+                }
+            }
+        }
+    }
+    if (hdr.hwndFrom is hwndHeader) {
+        /*
+        * Feature in Windows.  On NT, the automatically created
+        * header control is created as a UNICODE window, not an
+        * ANSI window despite the fact that the parent is created
+        * as an ANSI window.  This means that it sends UNICODE
+        * notification messages to the parent window on NT for
+        * no good reason.  The data and size in the NMHEADER and
+        * HDITEM structs is identical between the platforms so no
+        * different message is actually necessary.  Despite this,
+        * Windows sends different messages.  The fix is to look
+        * for both messages, despite the platform.  This works
+        * because only one will be sent on either platform, never
+        * both.
+        */
+        switch (hdr.code) {
+            case OS.HDN_BEGINTRACKW:
+            case OS.HDN_BEGINTRACKA:
+            case OS.HDN_DIVIDERDBLCLICKW:
+            case OS.HDN_DIVIDERDBLCLICKA: {
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                TreeColumn column = columns [phdn.iItem];
+                if (column !is null && !column.getResizable ()) {
+                    return LRESULT.ONE;
+                }
+                ignoreColumnMove = true;
+                switch (hdr.code) {
+                    case OS.HDN_DIVIDERDBLCLICKW:
+                    case OS.HDN_DIVIDERDBLCLICKA:
+                        if (column !is null) column.pack ();
+                }
+                break;
+            }
+            case OS.NM_RELEASEDCAPTURE: {
+                if (!ignoreColumnMove) {
+                    int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                    for (int i=0; i<count; i++) {
+                        TreeColumn column = columns [i];
+                        column.updateToolTip (i);
+                    }
+                    updateImageList ();
+                }
+                ignoreColumnMove = false;
+                break;
+            }
+            case OS.HDN_BEGINDRAG: {
+                if (ignoreColumnMove) return LRESULT.ONE;
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                if (phdn.iItem !is -1) {
+                    TreeColumn column = columns [phdn.iItem];
+                    if (column !is null && !column.getMoveable ()) {
+                        ignoreColumnMove = true;
+                        return LRESULT.ONE;
+                    }
+                }
+                break;
+            }
+            case OS.HDN_ENDDRAG: {
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                if (phdn.iItem !is -1 && phdn.pitem !is 0) {
+                    HDITEM pitem = new HDITEM ();
+                    OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
+                    if ((pitem.mask & OS.HDI_ORDER) !is 0 && pitem.iOrder !is -1) {
+                        int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                        int [] order = new int [count];
+                        OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
+                        int index = 0;
+                        while (index < order.length) {
+                            if (order [index] is phdn.iItem) break;
+                            index++;
+                        }
+                        if (index is order.length) index = 0;
+                        if (index is pitem.iOrder) break;
+                        int start = Math.min (index, pitem.iOrder);
+                        int end = Math.max (index, pitem.iOrder);
+                        RECT rect = new RECT (), headerRect = new RECT ();
+                        OS.GetClientRect (handle, rect);
+                        OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, order [start], headerRect);
+                        rect.left = Math.max (rect.left, headerRect.left);
+                        OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, order [end], headerRect);
+                        rect.right = Math.min (rect.right, headerRect.right);
+                        OS.InvalidateRect (handle, rect, true);
+                        ignoreColumnMove = false;
+                        for (int i=start; i<=end; i++) {
+                            TreeColumn column = columns [order [i]];
+                            if (!column.isDisposed ()) {
+                                column.postEvent (DWT.Move);
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+            case OS.HDN_ITEMCHANGINGW:
+            case OS.HDN_ITEMCHANGINGA: {
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                if (phdn.pitem !is 0) {
+                    HDITEM newItem = new HDITEM ();
+                    OS.MoveMemory (newItem, phdn.pitem, HDITEM.sizeof);
+                    if ((newItem.mask & OS.HDI_WIDTH) !is 0) {
+                        RECT rect = new RECT ();
+                        OS.GetClientRect (handle, rect);
+                        HDITEM oldItem = new HDITEM ();
+                        oldItem.mask = OS.HDI_WIDTH;
+                        OS.SendMessage (hwndHeader, OS.HDM_GETITEM, phdn.iItem, oldItem);
+                        int deltaX = newItem.cxy - oldItem.cxy;
+                        RECT headerRect = new RECT ();
+                        OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, phdn.iItem, headerRect);
+                        int gridWidth = linesVisible ? GRID_WIDTH : 0;
+                        rect.left = headerRect.right - gridWidth;
+                        int newX = rect.left + deltaX;
+                        rect.right = Math.max (rect.right, rect.left + Math.abs (deltaX));
+                        if (explorerTheme || (findImageControl () !is null || hooks (DWT.EraseItem) || hooks (DWT.PaintItem))) {
+                            OS.InvalidateRect (handle, rect, true);
+                            OS.OffsetRect (rect, deltaX, 0);
+                            OS.InvalidateRect (handle, rect, true);
+                        } else {
+                            int flags = OS.SW_INVALIDATE | OS.SW_ERASE;
+                            OS.ScrollWindowEx (handle, deltaX, 0, rect, null, 0, null, flags);
+                        }
+                        if (OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, phdn.iItem, 0) !is 0) {
+                            rect.left = headerRect.left;
+                            rect.right = newX;
+                            OS.InvalidateRect (handle, rect, true);
+                        }
+                        setScrollWidth ();
+                    }
+                }
+                break;
+            }
+            case OS.HDN_ITEMCHANGEDW:
+            case OS.HDN_ITEMCHANGEDA: {
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                if (phdn.pitem !is 0) {
+                    HDITEM pitem = new HDITEM ();
+                    OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
+                    if ((pitem.mask & OS.HDI_WIDTH) !is 0) {
+                        if (ignoreColumnMove) {
+                            if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                                int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN;
+                                OS.RedrawWindow (handle, null, 0, flags);
+                            } else {
+                                if ((style & DWT.DOUBLE_BUFFERED) is 0) {
+                                    int oldStyle = style;
+                                    style |= DWT.DOUBLE_BUFFERED;
+                                    OS.UpdateWindow (handle);
+                                    style = oldStyle;
+                                }
+                            }
+                        }
+                        TreeColumn column = columns [phdn.iItem];
+                        if (column !is null) {
+                            column.updateToolTip (phdn.iItem);
+                            column.sendEvent (DWT.Resize);
+                            if (isDisposed ()) return LRESULT.ZERO;
+                            int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+                            TreeColumn [] newColumns = new TreeColumn [count];
+                            System.arraycopy (columns, 0, newColumns, 0, count);
+                            int [] order = new int [count];
+                            OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
+                            bool moved = false;
+                            for (int i=0; i<count; i++) {
+                                TreeColumn nextColumn = newColumns [order [i]];
+                                if (moved && !nextColumn.isDisposed ()) {
+                                    nextColumn.updateToolTip (order [i]);
+                                    nextColumn.sendEvent (DWT.Move);
+                                }
+                                if (nextColumn is column) moved = true;
+                            }
+                        }
+                    }
+                    setScrollWidth ();
+                }
+                break;
+            }
+            case OS.HDN_ITEMCLICKW:
+            case OS.HDN_ITEMCLICKA: {
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                TreeColumn column = columns [phdn.iItem];
+                if (column !is null) {
+                    column.postEvent (DWT.Selection);
+                }
+                break;
+            }
+            case OS.HDN_ITEMDBLCLICKW:
+            case OS.HDN_ITEMDBLCLICKA: {
+                NMHEADER phdn = new NMHEADER ();
+                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
+                TreeColumn column = columns [phdn.iItem];
+                if (column !is null) {
+                    column.postEvent (DWT.DefaultSelection);
+                }
+                break;
+            }
+        }
+    }
+    return super.wmNotify (hdr, wParam, lParam);
+}
+
+LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
+    switch (hdr.code) {
+        case OS.TVN_GETDISPINFOA:
+        case OS.TVN_GETDISPINFOW: {
+            NMTVDISPINFO lptvdi = new NMTVDISPINFO ();
+            OS.MoveMemory (lptvdi, lParam, NMTVDISPINFO.sizeof);
+            if ((style & DWT.VIRTUAL) !is 0) {
+                /*
+                * Feature in Windows.  When a new tree item is inserted
+                * using TVM_INSERTITEM, a TVN_GETDISPINFO is sent before
+                * TVM_INSERTITEM returns and before the item is added to
+                * the items array.  The fix is to check for null.
+                *
+                * NOTE: This only happens on XP with the version 6.00 of
+                * COMCTL32.DLL.
+                */
+                bool checkVisible = true;
+                /*
+                * When an item is being deleted from a virtual tree, do not
+                * allow the application to provide data for a new item that
+                * becomes visible until the item has been removed from the
+                * items array.  Because arbitrary application code can run
+                * during the callback, the items array might be accessed
+                * in an inconsistent state.  Rather than answering the data
+                * right away, queue a redraw for later.
+                */
+                if (!ignoreShrink) {
+                    if (items !is null && lptvdi.lParam !is -1) {
+                        if (items [lptvdi.lParam] !is null && items [lptvdi.lParam].cached) {
+                            checkVisible = false;
+                        }
+                    }
+                }
+                if (checkVisible) {
+                    if (drawCount !is 0 || !OS.IsWindowVisible (handle)) break;
+                    RECT itemRect = new RECT ();
+                    itemRect.left = lptvdi.hItem;
+                    if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, itemRect) is 0) {
+                        break;
+                    }
+                    RECT rect = new RECT ();
+                    OS.GetClientRect (handle, rect);
+                    if (!OS.IntersectRect (rect, rect, itemRect)) break;
+                    if (ignoreShrink) {
+                        OS.InvalidateRect (handle, rect, true);
+                        break;
+                    }
+                }
+            }
+            if (items is null) break;
+            /*
+            * Bug in Windows.  If the lParam field of TVITEM
+            * is changed during custom draw using TVM_SETITEM,
+            * the lItemlParam field of the NMTVCUSTOMDRAW struct
+            * is not updated until the next custom draw.  The
+            * fix is to query the field from the item instead
+            * of using the struct.
+            */
+            int id = lptvdi.lParam;
+            if ((style & DWT.VIRTUAL) !is 0) {
+                if (id is -1) {
+                    TVITEM tvItem = new TVITEM ();
+                    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+                    tvItem.hItem = lptvdi.hItem;
+                    OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
+                    id = tvItem.lParam;
+                }
+            }
+            TreeItem item = _getItem (lptvdi.hItem, id);
+            /*
+            * Feature in Windows.  When a new tree item is inserted
+            * using TVM_INSERTITEM, a TVN_GETDISPINFO is sent before
+            * TVM_INSERTITEM returns and before the item is added to
+            * the items array.  The fix is to check for null.
+            *
+            * NOTE: This only happens on XP with the version 6.00 of
+            * COMCTL32.DLL.
+            *
+            * Feature in Windows.  When TVM_DELETEITEM is called with
+            * TVI_ROOT to remove all items from a tree, under certain
+            * circumstances, the tree sends TVN_GETDISPINFO for items
+            * that are about to be disposed.  The fix is to check for
+            * disposed items.
+            */
+            if (item is null) break;
+            if (item.isDisposed ()) break;
+            if (!item.cached) {
+                if ((style & DWT.VIRTUAL) !is 0) {
+                    if (!checkData (item, false)) break;
+                }
+                if (painted) item.cached = true;
+            }
+            int index = 0;
+            if (hwndHeader !is 0) {
+                index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
+            }
+            if ((lptvdi.mask & OS.TVIF_TEXT) !is 0) {
+                String string = null;
+                if (index is 0) {
+                    string = item.text;
+                } else {
+                    String [] strings  = item.strings;
+                    if (strings !is null) string = strings [index];
+                }
+                if (string !is null) {
+                    TCHAR buffer = new TCHAR (getCodePage (), string, false);
+                    int byteCount = Math.min (buffer.length (), lptvdi.cchTextMax - 1) * TCHAR.sizeof;
+                    OS.MoveMemory (lptvdi.pszText, buffer, byteCount);
+                    OS.MoveMemory (lptvdi.pszText + byteCount, new byte [TCHAR.sizeof], TCHAR.sizeof);
+                    lptvdi.cchTextMax = Math.min (lptvdi.cchTextMax, string.length () + 1);
+                }
+            }
+            if ((lptvdi.mask & (OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE)) !is 0) {
+                Image image = null;
+                if (index is 0) {
+                    image = item.image;
+                } else {
+                    Image [] images  = item.images;
+                    if (images !is null) image = images [index];
+                }
+                lptvdi.iImage = lptvdi.iSelectedImage = OS.I_IMAGENONE;
+                if (image !is null) {
+                    lptvdi.iImage = lptvdi.iSelectedImage = imageIndex (image, index);
+                }
+                if (explorerTheme && OS.IsWindowEnabled (handle)) {
+                    if (findImageControl () !is null) {
+                        lptvdi.iImage = lptvdi.iSelectedImage = OS.I_IMAGENONE;
+                    }
+                }
+            }
+            OS.MoveMemory (lParam, lptvdi, NMTVDISPINFO.sizeof);
+            break;
+        }
+        case OS.NM_CUSTOMDRAW: {
+            if (hdr.hwndFrom is hwndHeader) break;
+            if (hooks (DWT.MeasureItem)) {
+                if (hwndHeader is 0) createParent ();
+            }
+            if (!customDraw && findImageControl () is null) {
+                if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                    if (sortColumn is null || sortDirection is DWT.NONE) {
+                        break;
+                    }
+                }
+            }
+            NMTVCUSTOMDRAW nmcd = new NMTVCUSTOMDRAW ();
+            OS.MoveMemory (nmcd, lParam, NMTVCUSTOMDRAW.sizeof);
+            switch (nmcd.dwDrawStage) {
+                case OS.CDDS_PREPAINT: return CDDS_PREPAINT (nmcd, wParam, lParam);
+                case OS.CDDS_ITEMPREPAINT: return CDDS_ITEMPREPAINT (nmcd, wParam, lParam);
+                case OS.CDDS_ITEMPOSTPAINT: return CDDS_ITEMPOSTPAINT (nmcd, wParam, lParam);
+                case OS.CDDS_POSTPAINT: return CDDS_POSTPAINT (nmcd, wParam, lParam);
+            }
+            break;
+        }
+        case OS.NM_DBLCLK: {
+            /*
+            * When the user double clicks on a tree item
+            * or a line beside the item, the window proc
+            * for the tree collapses or expand the branch.
+            * When application code associates an action
+            * with double clicking, then the tree expand
+            * is unexpected and unwanted.  The fix is to
+            * avoid the operation by testing to see whether
+            * the mouse was inside a tree item.
+            */
+            if (hooks (DWT.DefaultSelection)) {
+                POINT pt = new POINT ();
+                int pos = OS.GetMessagePos ();
+                pt.x = (short) (pos & 0xFFFF);
+                pt.y = (short) (pos >> 16);
+                OS.ScreenToClient (handle, pt);
+                TVHITTESTINFO lpht = new TVHITTESTINFO ();
+                lpht.x = pt.x;
+                lpht.y = pt.y;
+                OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
+                if (lpht.hItem !is 0 && (lpht.flags & OS.TVHT_ONITEM) !is 0) {
+                    return LRESULT.ONE;
+                }
+            }
+            break;
+        }
+        /*
+        * Bug in Windows.  On Vista, when TVM_SELECTITEM is called
+        * with TVGN_CARET in order to set the selection, for some
+        * reason, Windows deselects the previous two items that
+        * were selected.  The fix is to stop the selection from
+        * changing on all but the item that is supposed to be
+        * selected.
+        */
+        case OS.TVN_ITEMCHANGINGA:
+        case OS.TVN_ITEMCHANGINGW: {
+            if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
+                if ((style & DWT.MULTI) !is 0) {
+                    if (hSelect !is 0) {
+                        NMTVITEMCHANGE pnm = new NMTVITEMCHANGE ();
+                        OS.MoveMemory (pnm, lParam, NMTVITEMCHANGE.sizeof);
+                        if (hSelect is pnm.hItem) break;
+                        return LRESULT.ONE;
+                    }
+                }
+            }
+            break;
+        }
+        case OS.TVN_SELCHANGINGA:
+        case OS.TVN_SELCHANGINGW: {
+            if ((style & DWT.MULTI) !is 0) {
+                if (lockSelection) {
+                    /* Save the old selection state for both items */
+                    TVITEM tvItem = new TVITEM ();
+                    int offset1 = NMHDR.sizeof + 4;
+                    OS.MoveMemory (tvItem, lParam + offset1, TVITEM.sizeof);
+                    oldSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0;
+                    int offset2 = NMHDR.sizeof + 4 + TVITEM.sizeof;
+                    OS.MoveMemory (tvItem, lParam + offset2, TVITEM.sizeof);
+                    newSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0;
+                }
+            }
+            if (!ignoreSelect && !ignoreDeselect) {
+                hAnchor = 0;
+                if ((style & DWT.MULTI) !is 0) deselectAll ();
+            }
+            break;
+        }
+        case OS.TVN_SELCHANGEDA:
+        case OS.TVN_SELCHANGEDW: {
+            if ((style & DWT.MULTI) !is 0) {
+                if (lockSelection) {
+                    /* Restore the old selection state of both items */
+                    if (oldSelected) {
+                        TVITEM tvItem = new TVITEM ();
+                        int offset = NMHDR.sizeof + 4;
+                        OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);
+                        tvItem.mask = OS.TVIF_STATE;
+                        tvItem.stateMask = OS.TVIS_SELECTED;
+                        tvItem.state = OS.TVIS_SELECTED;
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                    }
+                    if (!newSelected && ignoreSelect) {
+                        TVITEM tvItem = new TVITEM ();
+                        int offset = NMHDR.sizeof + 4 + TVITEM.sizeof;
+                        OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);
+                        tvItem.mask = OS.TVIF_STATE;
+                        tvItem.stateMask = OS.TVIS_SELECTED;
+                        tvItem.state = 0;
+                        OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
+                    }
+                }
+            }
+            if (!ignoreSelect) {
+                TVITEM tvItem = new TVITEM ();
+                int offset = NMHDR.sizeof + 4 + TVITEM.sizeof;
+                OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);
+                hAnchor = tvItem.hItem;
+                Event event = new Event ();
+                event.item = _getItem (tvItem.hItem, tvItem.lParam);
+                postEvent (DWT.Selection, event);
+            }
+            updateScrollBar ();
+            break;
+        }
+        case OS.TVN_ITEMEXPANDINGA:
+        case OS.TVN_ITEMEXPANDINGW: {
+            bool runExpanded = false;
+            if ((style & DWT.VIRTUAL) !is 0) style &= ~DWT.DOUBLE_BUFFERED;
+            if (hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) style &= ~DWT.DOUBLE_BUFFERED;
+            if (findImageControl () !is null && drawCount is 0 && OS.IsWindowVisible (handle)) {
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 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.
+            */
+            if (hInsert !is 0) {
+                OS.SendMessage (handle, OS.TVM_SETINSERTMARK, 0, 0);
+            }
+            if (!ignoreExpand) {
+                TVITEM tvItem = new TVITEM ();
+                int offset = NMHDR.sizeof + 4 + TVITEM.sizeof;
+                OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);
+                /*
+                * Feature in Windows.  In some cases, TVM_ITEMEXPANDING
+                * is sent from within TVM_DELETEITEM for the tree item
+                * being destroyed.  By the time the message is sent,
+                * the item has already been removed from the list of
+                * items.  The fix is to check for null.
+                */
+                if (items is null) break;
+                TreeItem item = _getItem (tvItem.hItem, tvItem.lParam);
+                if (item is null) break;
+                Event event = new Event ();
+                event.item = item;
+                int [] action = new int [1];
+                OS.MoveMemory (action, lParam + NMHDR.sizeof, 4);
+                switch (action [0]) {
+                    case OS.TVE_EXPAND:
+                        /*
+                        * Bug in Windows.  When the numeric keypad asterisk
+                        * key is used to expand every item in the tree, Windows
+                        * sends TVN_ITEMEXPANDING to items in the tree that
+                        * have already been expanded.  The fix is to detect
+                        * that the item is already expanded and ignore the
+                        * notification.
+                        */
+                        if ((tvItem.state & OS.TVIS_EXPANDED) is 0) {
+                            sendEvent (DWT.Expand, event);
+                            if (isDisposed ()) return LRESULT.ZERO;
+                        }
+                        break;
+                    case OS.TVE_COLLAPSE:
+                        sendEvent (DWT.Collapse, event);
+                        if (isDisposed ()) return LRESULT.ZERO;
+                        break;
+                }
+                /*
+                * Bug in Windows.  When all of the items are deleted during
+                * TVN_ITEMEXPANDING, Windows does not send TVN_ITEMEXPANDED.
+                * The fix is to detect this case and run the TVN_ITEMEXPANDED
+                * code in this method.
+                */
+                int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, tvItem.hItem);
+                runExpanded = hFirstItem is 0;
+            }
+            if (!runExpanded) break;
+            //FALL THROUGH
+        }
+        case OS.TVN_ITEMEXPANDEDA:
+        case OS.TVN_ITEMEXPANDEDW: {
+            if ((style & DWT.VIRTUAL) !is 0) style |= DWT.DOUBLE_BUFFERED;
+            if (hooks (DWT.EraseItem) || hooks (DWT.PaintItem)) style |= DWT.DOUBLE_BUFFERED;
+            if (findImageControl () !is null && drawCount is 0 /*&& OS.IsWindowVisible (handle)*/) {
+                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
+                OS.InvalidateRect (handle, null, true);
+            }
+            /*
+            * 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.
+            */
+            if (hInsert !is 0) {
+                OS.SendMessage (handle, OS.TVM_SETINSERTMARK, insertAfter ? 1 : 0, hInsert);
+            }
+            /*
+            * Bug in Windows.  When a tree item that has an image
+            * with alpha is expanded or collapsed, the area where
+            * the image is drawn is not erased before it is drawn.
+            * This means that the image gets darker each time.
+            * The fix is to redraw the item.
+            */
+            if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) {
+                if (imageList !is null) {
+                    TVITEM tvItem = new TVITEM ();
+                    int offset = NMHDR.sizeof + 4 + TVITEM.sizeof;
+                    OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);
+                    if (tvItem.hItem !is 0) {
+                        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
+                        if ((bits & OS.TVS_FULLROWSELECT) is 0) {
+                            RECT rect = new RECT ();
+                            rect.left = tvItem.hItem;
+                            OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect);
+                            OS.InvalidateRect (handle, rect, true);
+                        }
+                    }
+                }
+            }
+            updateScrollBar ();
+            break;
+        }
+        case OS.TVN_BEGINDRAGA:
+        case OS.TVN_BEGINDRAGW:
+        case OS.TVN_BEGINRDRAGA:
+        case OS.TVN_BEGINRDRAGW: {
+            TVITEM tvItem = new TVITEM ();
+            int offset = NMHDR.sizeof + 4 + TVITEM.sizeof;
+            OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);
+            if (tvItem.hItem !is 0 && (tvItem.state & OS.TVIS_SELECTED) is 0) {
+                hSelect = tvItem.hItem;
+                ignoreSelect = ignoreDeselect = true;
+                OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, tvItem.hItem);
+                ignoreSelect = ignoreDeselect = false;
+                hSelect = 0;
+            }
+            dragStarted = true;
+            break;
+        }
+        case OS.NM_RECOGNIZEGESTURE: {
+            /*
+            * Feature in Pocket PC.  The tree and table controls detect the tap
+            * and hold gesture by default. They send a GN_CONTEXTMENU message to show
+            * the popup menu.  This default behaviour is unwanted on Pocket PC 2002
+            * when no menu has been set, as it still draws a red circle.  The fix
+            * is to disable this default behaviour when no menu is set by returning
+            * TRUE when receiving the Pocket PC 2002 specific NM_RECOGNIZEGESTURE
+            * message.
+            */
+            if (OS.IsPPC) {
+                bool hasMenu = menu !is null && !menu.isDisposed ();
+                if (!hasMenu && !hooks (DWT.MenuDetect)) return LRESULT.ONE;
+            }
+            break;
+        }
+        case OS.GN_CONTEXTMENU: {
+            if (OS.IsPPC) {
+                bool hasMenu = menu !is null && !menu.isDisposed ();
+                if (hasMenu || hooks (DWT.MenuDetect)) {
+                    NMRGINFO nmrg = new NMRGINFO ();
+                    OS.MoveMemory (nmrg, lParam, NMRGINFO.sizeof);
+                    showMenu (nmrg.x, nmrg.y);
+                    gestureCompleted = true;
+                    return LRESULT.ONE;
+                }
+            }
+            break;
+        }
+    }
+    return super.wmNotifyChild (hdr, wParam, lParam);
+}
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/TreeColumn.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,772 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.TreeColumn;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+class TreeColumn : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.ControlListener;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.graphics.Image;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.HDITEM;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TOOLINFO;
+import dwt.internal.win32.TVITEM;
+
+/**
+ * Instances of this class represent a column in a tree widget.
+ * <p><dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>LEFT, RIGHT, CENTER</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd> Move, Resize, Selection</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles LEFT, RIGHT and CENTER may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class TreeColumn extends Item {
+    Tree parent;
+    bool resizable, moveable;
+    String toolTipText;
+    int id;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#LEFT
+ * @see DWT#RIGHT
+ * @see DWT#CENTER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeColumn (Tree parent, int style) {
+    super (parent, checkStyle (style));
+    resizable = true;
+    this.parent = parent;
+    parent.createItem (this, parent.getColumnCount ());
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT#LEFT
+ * @see DWT#RIGHT
+ * @see DWT#CENTER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeColumn (Tree parent, int style, int index) {
+    super (parent, checkStyle (style));
+    resizable = true;
+    this.parent = parent;
+    parent.createItem (this, index);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is moved or resized, by sending
+ * it one of the messages defined in the <code>ControlListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #removeControlListener
+ */
+public void addControlListener(ControlListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Resize,typedListener);
+    addListener (DWT.Move,typedListener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the column header is selected.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the control is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Selection,typedListener);
+    addListener (DWT.DefaultSelection,typedListener);
+}
+
+static int checkStyle (int style) {
+    return checkBits (style, DWT.LEFT, DWT.CENTER, DWT.RIGHT, 0, 0, 0);
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+void destroyWidget () {
+    parent.destroyItem (this);
+    releaseHandle ();
+}
+
+/**
+ * Returns a value which describes the position of the
+ * text or image in the receiver. The value will be one of
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>.
+ *
+ * @return the alignment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getAlignment () {
+    checkWidget ();
+    if ((style & DWT.LEFT) !is 0) return DWT.LEFT;
+    if ((style & DWT.CENTER) !is 0) return DWT.CENTER;
+    if ((style & DWT.RIGHT) !is 0) return DWT.RIGHT;
+    return DWT.LEFT;
+}
+
+/**
+ * Gets the moveable attribute. A column that is
+ * not moveable cannot be reordered by the user
+ * by dragging the header but may be reordered
+ * by the programmer.
+ *
+ * @return the moveable attribute
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#getColumnOrder()
+ * @see Tree#setColumnOrder(int[])
+ * @see TreeColumn#setMoveable(bool)
+ * @see DWT#Move
+ *
+ * @since 3.2
+ */
+public bool getMoveable () {
+    checkWidget ();
+    return moveable;
+}
+
+String getNameText () {
+    return getText ();
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Tree</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Tree getParent () {
+    checkWidget ();
+    return parent;
+}
+
+/**
+ * Gets the resizable attribute. A column that is
+ * not resizable cannot be dragged by the user but
+ * may be resized by the programmer.
+ *
+ * @return the resizable attribute
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getResizable () {
+    checkWidget ();
+    return resizable;
+}
+
+/**
+ * Returns the receiver's tool tip text, or null if it has
+ * not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public String getToolTipText () {
+    checkWidget();
+    return toolTipText;
+}
+
+/**
+ * Gets the width of the receiver.
+ *
+ * @return the width
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getWidth () {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return 0;
+    int hwndHeader = parent.hwndHeader;
+    if (hwndHeader is 0) return 0;
+    HDITEM hdItem = new HDITEM ();
+    hdItem.mask = OS.HDI_WIDTH;
+    OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+    return hdItem.cxy;
+}
+
+/**
+ * Causes the receiver to be resized to its preferred size.
+ * For a composite, this involves computing the preferred size
+ * from its layout, if there is one.
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ */
+public void pack () {
+    checkWidget ();
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    int columnWidth = 0;
+    int hwnd = parent.handle, hwndHeader = parent.hwndHeader;
+    RECT headerRect = new RECT ();
+    OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+    int hDC = OS.GetDC (hwnd);
+    int oldFont = 0, newFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0);
+    if (newFont !is 0) oldFont = OS.SelectObject (hDC, newFont);
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    while (tvItem.hItem !is 0) {
+        OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+        TreeItem item = tvItem.lParam !is -1 ? parent.items [tvItem.lParam] : null;
+        if (item !is null) {
+            int hFont = item.cellFont !is null ? item.cellFont [index] : -1;
+            if (hFont is -1) hFont = item.font;
+            if (hFont !is -1) hFont = OS.SelectObject (hDC, hFont);
+            RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC);
+            if (hFont !is -1) OS.SelectObject (hDC, hFont);
+            if (parent.hooks (DWT.MeasureItem)) {
+                int nSavedDC = OS.SaveDC (hDC);
+                GCData data = new GCData ();
+                data.device = display;
+                data.hFont = hFont;
+                GC gc = GC.win32_new (hDC, data);
+                Event event = new Event ();
+                event.item = item;
+                event.gc = gc;
+                event.index = index;
+                event.x = itemRect.left;
+                event.y = itemRect.top;
+                event.width = itemRect.right - itemRect.left;
+                event.height = itemRect.bottom - itemRect.top;
+                parent.sendEvent (DWT.MeasureItem, event);
+                event.gc = null;
+                gc.dispose ();
+                OS.RestoreDC (hDC, nSavedDC);
+                if (isDisposed () || parent.isDisposed ()) break;
+                if (event.height > parent.getItemHeight ()) parent.setItemHeight (event.height);
+                //itemRect.left = event.x;
+                itemRect.right = event.x + event.width;
+            }
+            columnWidth = Math.max (columnWidth, itemRect.right - headerRect.left);
+        }
+        tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, tvItem.hItem);
+    }
+    RECT rect = new RECT ();
+    int flags = OS.DT_CALCRECT | OS.DT_NOPREFIX;
+    TCHAR buffer = new TCHAR (parent.getCodePage (), text, false);
+    OS.DrawText (hDC, buffer, buffer.length (), rect, flags);
+    int headerWidth = rect.right - rect.left + Tree.HEADER_MARGIN;
+    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) headerWidth += Tree.HEADER_EXTRA;
+    if (image !is null || parent.sortColumn is this) {
+        Image headerImage = null;
+        if (parent.sortColumn is this && parent.sortDirection !is DWT.NONE) {
+            if (OS.COMCTL32_MAJOR < 6) {
+                headerImage = display.getSortImage (parent.sortDirection);
+            } else {
+                headerWidth += Tree.SORT_WIDTH;
+            }
+        } else {
+            headerImage = image;
+        }
+        if (headerImage !is null) {
+            Rectangle bounds = headerImage.getBounds ();
+            headerWidth += bounds.width;
+        }
+        int margin = 0;
+        if (hwndHeader !is 0 && OS.COMCTL32_VERSION >= OS.VERSION (5, 80)) {
+            margin = OS.SendMessage (hwndHeader, OS.HDM_GETBITMAPMARGIN, 0, 0);
+        } else {
+            margin = OS.GetSystemMetrics (OS.SM_CXEDGE) * 3;
+        }
+        headerWidth += margin * 2;
+    }
+    if (newFont !is 0) OS.SelectObject (hDC, oldFont);
+    OS.ReleaseDC (hwnd, hDC);
+    int gridWidth = parent.linesVisible ? Tree.GRID_WIDTH : 0;
+    setWidth (Math.max (headerWidth, columnWidth + gridWidth));
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    parent = null;
+}
+
+void releaseParent () {
+    super.releaseParent ();
+    if (parent.sortColumn is this) {
+        parent.sortColumn = null;
+    }
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is moved or resized.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #addControlListener
+ */
+public void removeControlListener (ControlListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Move, listener);
+    eventTable.unhook (DWT.Resize, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener(SelectionListener listener) {
+    checkWidget ();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Selection, listener);
+    eventTable.unhook (DWT.DefaultSelection,listener);
+}
+
+/**
+ * Controls how text and images will be displayed in the receiver.
+ * The argument should be one of <code>LEFT</code>, <code>RIGHT</code>
+ * or <code>CENTER</code>.
+ *
+ * @param alignment the new alignment
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setAlignment (int alignment) {
+    checkWidget ();
+    if ((alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER)) is 0) return;
+    int index = parent.indexOf (this);
+    if (index is -1 || index is 0) return;
+    style &= ~(DWT.LEFT | DWT.RIGHT | DWT.CENTER);
+    style |= alignment & (DWT.LEFT | DWT.RIGHT | DWT.CENTER);
+    int hwndHeader = parent.hwndHeader;
+    if (hwndHeader is 0) return;
+    HDITEM hdItem = new HDITEM ();
+    hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE;
+    OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+    hdItem.fmt &= ~OS.HDF_JUSTIFYMASK;
+    if ((style & DWT.LEFT) is DWT.LEFT) hdItem.fmt |= OS.HDF_LEFT;
+    if ((style & DWT.CENTER) is DWT.CENTER) hdItem.fmt |= OS.HDF_CENTER;
+    if ((style & DWT.RIGHT) is DWT.RIGHT) hdItem.fmt |= OS.HDF_RIGHT;
+    OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
+    if (index !is 0) {
+        int hwnd = parent.handle;
+        parent.forceResize ();
+        RECT rect = new RECT (), headerRect = new RECT ();
+        OS.GetClientRect (hwnd, rect);
+        OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+        rect.left = headerRect.left;
+        rect.right = headerRect.right;
+        OS.InvalidateRect (hwnd, rect, true);
+    }
+}
+
+public void setImage (Image image) {
+    checkWidget();
+    if (image !is null && image.isDisposed ()) {
+        error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    super.setImage (image);
+    if (parent.sortColumn !is this || parent.sortDirection !is DWT.NONE) {
+        setImage (image, false, false);
+    }
+}
+
+void setImage (Image image, bool sort, bool right) {
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    int hwndHeader = parent.hwndHeader;
+    if (hwndHeader is 0) return;
+    HDITEM hdItem = new HDITEM ();
+    hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE | OS.HDI_BITMAP;
+    OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+    hdItem.fmt &= ~OS.HDF_BITMAP_ON_RIGHT;
+    if (image !is null) {
+        if (sort) {
+            hdItem.mask &= ~OS.HDI_IMAGE;
+            hdItem.fmt &= ~OS.HDF_IMAGE;
+            hdItem.fmt |= OS.HDF_BITMAP;
+            hdItem.hbm = image.handle;
+        } else {
+            hdItem.mask &= ~OS.HDI_BITMAP;
+            hdItem.fmt &= ~OS.HDF_BITMAP;
+            hdItem.fmt |= OS.HDF_IMAGE;
+            hdItem.iImage = parent.imageIndexHeader (image);
+        }
+        if (right) hdItem.fmt |= OS.HDF_BITMAP_ON_RIGHT;
+    } else {
+        hdItem.mask &= ~(OS.HDI_IMAGE | OS.HDI_BITMAP);
+        hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_BITMAP);
+    }
+    OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
+}
+
+/**
+ * Sets the moveable attribute.  A column that is
+ * moveable can be reordered by the user by dragging
+ * the header. A column that is not moveable cannot be
+ * dragged by the user but may be reordered
+ * by the programmer.
+ *
+ * @param moveable the moveable attribute
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#setColumnOrder(int[])
+ * @see Tree#getColumnOrder()
+ * @see TreeColumn#getMoveable()
+ * @see DWT#Move
+ *
+ * @since 3.2
+ */
+public void setMoveable (bool moveable) {
+    checkWidget ();
+    this.moveable = moveable;
+}
+
+/**
+ * Sets the resizable attribute.  A column that is
+ * not resizable cannot be dragged by the user but
+ * may be resized by the programmer.
+ *
+ * @param resizable the resize attribute
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setResizable (bool resizable) {
+    checkWidget ();
+    this.resizable = resizable;
+}
+
+void setSortDirection (int direction) {
+    if (OS.COMCTL32_MAJOR >= 6) {
+        int hwndHeader = parent.hwndHeader;
+        if (hwndHeader !is 0) {
+            int index = parent.indexOf (this);
+            if (index is -1) return;
+            HDITEM hdItem = new HDITEM ();
+            hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE;
+            OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
+            switch (direction) {
+                case DWT.UP:
+                    hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_SORTDOWN);
+                    hdItem.fmt |= OS.HDF_SORTUP;
+                    break;
+                case DWT.DOWN:
+                    hdItem.fmt &= ~(OS.HDF_IMAGE | OS.HDF_SORTUP);
+                    hdItem.fmt |= OS.HDF_SORTDOWN;
+                    break;
+                case DWT.NONE:
+                    hdItem.fmt &= ~(OS.HDF_SORTUP | OS.HDF_SORTDOWN);
+                    if (image !is null) {
+                        hdItem.fmt |= OS.HDF_IMAGE;
+                        hdItem.iImage = parent.imageIndexHeader (image);
+                    } else {
+                        hdItem.fmt &= ~OS.HDF_IMAGE;
+                    }
+                    break;
+            }
+            OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
+            if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+                int hwnd = parent.handle;
+                parent.forceResize ();
+                RECT rect = new RECT (), headerRect = new RECT ();
+                OS.GetClientRect (hwnd, rect);
+                OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+                rect.left = headerRect.left;
+                rect.right = headerRect.right;
+                OS.InvalidateRect (hwnd, rect, true);
+            }
+        }
+    } else {
+        switch (direction) {
+            case DWT.UP:
+            case DWT.DOWN:
+                setImage (display.getSortImage (direction), true, true);
+                break;
+            case DWT.NONE:
+                setImage (image, false, false);
+                break;
+        }
+    }
+}
+
+public void setText (String string) {
+    checkWidget ();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (string.equals (text)) return;
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    super.setText (string);
+    /*
+    * Bug in Windows.  When a column header contains a
+    * mnemonic character, Windows does not measure the
+    * text properly.  This causes '...' to always appear
+    * at the end of the text.  The fix is to remove
+    * mnemonic characters and replace doubled mnemonics
+    * with spaces.
+    */
+    int hHeap = OS.GetProcessHeap ();
+    TCHAR buffer = new TCHAR (parent.getCodePage (), fixMnemonic (string), true);
+    int byteCount = buffer.length () * TCHAR.sizeof;
+    int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
+    OS.MoveMemory (pszText, buffer, byteCount);
+    int hwndHeader = parent.hwndHeader;
+    if (hwndHeader is 0) return;
+    HDITEM hdItem = new HDITEM ();
+    hdItem.mask = OS.HDI_TEXT;
+    hdItem.pszText = pszText;
+    int result = OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
+    if (pszText !is 0) OS.HeapFree (hHeap, 0, pszText);
+    if (result is 0) error (DWT.ERROR_CANNOT_SET_TEXT);
+}
+
+/**
+ * Sets the receiver's tool tip text to the argument, which
+ * may be null indicating that no tool tip text should be shown.
+ *
+ * @param string the new tool tip text (or null)
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setToolTipText (String string) {
+    checkWidget();
+    toolTipText = string;
+    int hwndHeaderToolTip = parent.headerToolTipHandle;
+    if (hwndHeaderToolTip is 0) {
+        parent.createHeaderToolTips ();
+        parent.updateHeaderToolTips ();
+    }
+}
+
+/**
+ * Sets the width of the receiver.
+ *
+ * @param width the new width
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setWidth (int width) {
+    checkWidget ();
+    if (width < 0) return;
+    int index = parent.indexOf (this);
+    if (index is -1) return;
+    int hwndHeader = parent.hwndHeader;
+    if (hwndHeader is 0) return;
+    HDITEM hdItem = new HDITEM ();
+    hdItem.mask = OS.HDI_WIDTH;
+    hdItem.cxy = width;
+    OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
+    RECT headerRect = new RECT ();
+    OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
+    parent.forceResize ();
+    int hwnd = parent.handle;
+    RECT rect = new RECT ();
+    OS.GetClientRect (hwnd, rect);
+    rect.left = headerRect.left;
+    OS.InvalidateRect (hwnd, rect, true);
+    parent.setScrollWidth ();
+}
+
+void updateToolTip (int index) {
+    int hwndHeaderToolTip = parent.headerToolTipHandle;
+    if (hwndHeaderToolTip !is 0) {
+        int hwndHeader = parent.hwndHeader;
+        RECT rect = new RECT ();
+        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) !is 0) {
+            TOOLINFO lpti = new TOOLINFO ();
+            lpti.cbSize = TOOLINFO.sizeof;
+            lpti.hwnd = hwndHeader;
+            lpti.uId = id;
+            lpti.left = rect.left;
+            lpti.top = rect.top;
+            lpti.right = rect.right;
+            lpti.bottom = rect.bottom;
+            OS.SendMessage (hwndHeaderToolTip, OS.TTM_NEWTOOLRECT, 0, lpti);
+        }
+    }
+}
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/TreeItem.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,1839 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.TreeItem;
+
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+class TreeItem : Item {
+    public this (Widget parent, int style) {
+        super (parent, style);
+    }
+}
+/++
+
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SCROLLINFO;
+import dwt.internal.win32.TCHAR;
+import dwt.internal.win32.TVITEM;
+
+/**
+ * Instances of this class represent a selectable user interface object
+ * that represents a hierarchy of tree items in a tree widget.
+ *
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ */
+
+public class TreeItem extends Item {
+    /**
+     * the handle to the OS 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 int handle;
+    Tree parent;
+    String [] strings;
+    Image [] images;
+    bool cached;
+    int background = -1, foreground = -1, font = -1;
+    int [] cellBackground, cellForeground, cellFont;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code> or a <code>TreeItem</code>)
+ * and a style value describing its behavior and appearance.
+ * The item is added to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a tree control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeItem (Tree parent, int style) {
+    this (parent, style, OS.TVGN_ROOT, OS.TVI_LAST, 0);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code> or a <code>TreeItem</code>),
+ * a style value describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a tree control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeItem (Tree parent, int style, int index) {
+    this (parent, style, OS.TVGN_ROOT, findPrevious (parent, index), 0);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code> or a <code>TreeItem</code>)
+ * and a style value describing its behavior and appearance.
+ * The item is added to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parentItem a tree control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeItem (TreeItem parentItem, int style) {
+    this (checkNull (parentItem).parent, style, parentItem.handle, OS.TVI_LAST, 0);
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code> or a <code>TreeItem</code>),
+ * a style value describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parentItem a tree control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeItem (TreeItem parentItem, int style, int index) {
+    this (checkNull (parentItem).parent, style, parentItem.handle, findPrevious (parentItem, index), 0);
+}
+
+TreeItem (Tree parent, int style, int hParent, int hInsertAfter, int hItem) {
+    super (parent, style);
+    this.parent = parent;
+    parent.createItem (this, hParent, hInsertAfter, hItem);
+}
+
+static TreeItem checkNull (TreeItem item) {
+    if (item is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    return item;
+}
+
+static int findPrevious (Tree parent, int index) {
+    if (parent is null) return 0;
+    if (index < 0) DWT.error (DWT.ERROR_INVALID_RANGE);
+    if (index is 0) return OS.TVI_FIRST;
+    int hwnd = parent.handle;
+    int hFirstItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
+    int hItem = parent.findItem (hFirstItem, index - 1);
+    if (hItem is 0) DWT.error (DWT.ERROR_INVALID_RANGE);
+    return hItem;
+}
+
+static int findPrevious (TreeItem parentItem, int index) {
+    if (parentItem is null) return 0;
+    if (index < 0) DWT.error (DWT.ERROR_INVALID_RANGE);
+    if (index is 0) return OS.TVI_FIRST;
+    Tree parent = parentItem.parent;
+    int hwnd = parent.handle, hParent = parentItem.handle;
+    int hFirstItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent);
+    int hItem = parent.findItem (hFirstItem, index - 1);
+    if (hItem is 0) DWT.error (DWT.ERROR_INVALID_RANGE);
+    return hItem;
+}
+
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+void clear () {
+    text = "";
+    image = null;
+    strings = null;
+    images = null;
+    if ((parent.style & DWT.CHECK) !is 0) {
+        int hwnd = parent.handle;
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+        tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+        tvItem.state = 1 << 12;
+        tvItem.hItem = handle;
+        OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem);
+    }
+    background = foreground = font = -1;
+    cellBackground = cellForeground = cellFont = null;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = false;
+}
+
+/**
+ * Clears the item at the given zero-relative index in the receiver.
+ * The text, icon and other attributes of the item are set to the default
+ * value.  If the tree was created with the <code>DWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param index the index of the item to clear
+ * @param all <code>true</code> if all child items of the indexed item should be
+ * cleared recursively, and <code>false</code> otherwise
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT#VIRTUAL
+ * @see DWT#SetData
+ *
+ * @since 3.2
+ */
+public void clear (int index, bool all) {
+    checkWidget ();
+    int hwnd = parent.handle;
+    int hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle);
+    if (hItem is 0) error (DWT.ERROR_INVALID_RANGE);
+    hItem = parent.findItem (hItem, index);
+    if (hItem is 0) error (DWT.ERROR_INVALID_RANGE);
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    parent.clear (hItem, tvItem);
+    if (all) {
+        hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
+        parent.clearAll (hItem, tvItem, all);
+    }
+}
+
+/**
+ * Clears all the items in the receiver. The text, icon and other
+ * attributes of the items are set to their default values. If the
+ * tree was created with the <code>DWT.VIRTUAL</code> style, these
+ * attributes are requested again as needed.
+ *
+ * @param all <code>true</code> if all child items should be cleared
+ * recursively, and <code>false</code> otherwise
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT#VIRTUAL
+ * @see DWT#SetData
+ *
+ * @since 3.2
+ */
+public void clearAll (bool all) {
+    checkWidget ();
+    int hwnd = parent.handle;
+    int hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle);
+    if (hItem is 0) return;
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    parent.clearAll (hItem, tvItem, all);
+}
+
+void destroyWidget () {
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    parent.releaseItem (handle, tvItem, false);
+    parent.destroyItem (this, handle);
+    releaseHandle ();
+}
+
+/**
+ * Returns the receiver's background color.
+ *
+ * @return the background color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public Color getBackground () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (background is -1) return parent.getBackground ();
+    return Color.win32_new (display, background);
+}
+
+/**
+ * Returns the background color at the given column index in the receiver.
+ *
+ * @param index the column index
+ * @return the background color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Color getBackground (int index) {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return getBackground ();
+    int pixel = cellBackground !is null ? cellBackground [index] : -1;
+    return pixel is -1 ? getBackground () : Color.win32_new (display, pixel);
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent.
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    RECT rect = getBounds (0, true, false, false);
+    int width = rect.right - rect.left, height = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent at a column in the tree.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding column rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Rectangle getBounds (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    RECT rect = getBounds (index, true, true, true);
+    int width = rect.right - rect.left, height = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+RECT getBounds (int index, bool getText, bool getImage, bool fullText) {
+    return getBounds (index, getText, getImage, fullText, false, true, 0);
+}
+
+//TODO - take into account grid (add bool arg) to damage less during redraw
+RECT getBounds (int index, bool getText, bool getImage, bool fullText, bool fullImage, bool clip, int hDC) {
+    if (!getText && !getImage) return new RECT ();
+    int hwnd = parent.handle;
+    if ((parent.style & DWT.VIRTUAL) is 0 && !cached && !parent.painted) {
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT;
+        tvItem.hItem = handle;
+        tvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+        parent.ignoreCustomDraw = true;
+        OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem);
+        parent.ignoreCustomDraw = false;
+    }
+    bool firstColumn = index is 0;
+    int columnCount = 0, hwndHeader = parent.hwndHeader;
+    if (hwndHeader !is 0) {
+        columnCount = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
+        firstColumn = index is OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
+    }
+    RECT rect = new RECT ();
+    if (firstColumn) {
+        rect.left = handle;
+        bool full = columnCount is 0 && getText && getImage && fullText && fullImage;
+        if (OS.SendMessage (hwnd, OS.TVM_GETITEMRECT, full ? 0 : 1, rect) is 0) {
+            return new RECT ();
+        }
+        if (getImage && !fullImage) {
+            if (OS.SendMessage (hwnd, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0) !is 0) {
+                Point size = parent.getImageSize ();
+                rect.left -= size.x + Tree.INSET;
+                if (!getText) rect.right = rect.left + size.x;
+            } else {
+                if (!getText) rect.right = rect.left;
+            }
+        }
+        if (fullText || fullImage || clip) {
+            if (hwndHeader !is 0) {
+                RECT headerRect = new RECT ();
+                if (columnCount !is 0) {
+                    if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) is 0) {
+                        return new RECT ();
+                    }
+                } else {
+                    headerRect.right = parent.scrollWidth;
+                    if (headerRect.right is 0) headerRect = rect;
+                }
+                if (fullText) rect.right = headerRect.right;
+                if (fullImage) rect.left = headerRect.left;
+                if (clip && headerRect.right < rect.right) {
+                    rect.right = headerRect.right;
+                }
+            }
+        }
+    } else {
+        if (!(0 <= index && index < columnCount)) return new RECT ();
+        RECT headerRect = new RECT ();
+        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) is 0) {
+            return new RECT ();
+        }
+        rect.left = handle;
+        if (OS.SendMessage (hwnd, OS.TVM_GETITEMRECT, 0, rect) is 0) {
+            return new RECT ();
+        }
+        rect.left = headerRect.left;
+        if (fullText && getImage) {
+            rect.right = headerRect.right;
+        } else {
+            rect.right = headerRect.left;
+            Image image = null;
+            if (index is 0) {
+                image = this.image;
+            } else {
+                if (images !is null) image = images [index];
+            }
+            if (image !is null) {
+                Point size = parent.getImageSize ();
+                rect.right += size.x;
+            }
+            if (getText) {
+                if (fullText) {
+                    rect.left = rect.right + Tree.INSET;
+                    rect.right = headerRect.right;
+                } else {
+                    String string = index is 0 ? text : strings !is null ? strings [index] : null;
+                    if (string !is null) {
+                        RECT textRect = new RECT ();
+                        TCHAR buffer = new TCHAR (parent.getCodePage (), string, false);
+                        int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_CALCRECT;
+                        int hNewDC = hDC, hFont = 0;
+                        if (hDC is 0) {
+                            hNewDC = OS.GetDC (hwnd);
+                            hFont = cellFont !is null ? cellFont [index] : -1;
+                            if (hFont is -1) hFont = font;
+                            if (hFont is -1) hFont = OS.SendMessage (hwnd, OS.WM_GETFONT, 0, 0);
+                            hFont = OS.SelectObject (hNewDC, hFont);
+                        }
+                        OS.DrawText (hNewDC, buffer, buffer.length (), textRect, flags);
+                        if (hDC is 0) {
+                            OS.SelectObject (hNewDC, hFont);
+                            OS.ReleaseDC (hwnd, hNewDC);
+                        }
+                        if (getImage) {
+                            rect.right += textRect.right - textRect.left + Tree.INSET * 3;
+                        } else {
+                            rect.left = rect.right + Tree.INSET;
+                            rect.right = rect.left + (textRect.right - textRect.left) + Tree.INSET;
+                        }
+                    }
+                }
+            }
+            if (clip && headerRect.right < rect.right) {
+                rect.right = headerRect.right;
+            }
+        }
+    }
+    int gridWidth = parent.linesVisible && columnCount !is 0 ? Tree.GRID_WIDTH : 0;
+    if (getText || !getImage) {
+        rect.right = Math.max (rect.left, rect.right - gridWidth);
+    }
+    rect.bottom = Math.max (rect.top, rect.bottom - gridWidth);
+    return rect;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is checked,
+ * and false otherwise.  When the parent does not have
+ * the <code>CHECK style, return false.
+ * <p>
+ *
+ * @return the checked state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getChecked () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if ((parent.style & DWT.CHECK) is 0) return false;
+    int hwnd = parent.handle;
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+    tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+    tvItem.hItem = handle;
+    int result = OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+    return (result !is 0) && (((tvItem.state >> 12) & 1) is 0);
+}
+
+/**
+ * Returns <code>true</code> if the receiver is expanded,
+ * and false otherwise.
+ * <p>
+ *
+ * @return the expanded state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getExpanded () {
+    checkWidget ();
+    int hwnd = parent.handle, state = 0;
+    if (OS.IsWinCE) {
+        TVITEM tvItem = new TVITEM ();
+        tvItem.hItem = handle;
+        tvItem.mask = OS.TVIF_STATE;
+        OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+        state = tvItem.state;
+    } else {
+        /*
+        * Bug in Windows.  Despite the fact that TVM_GETITEMSTATE claims
+        * to return only the bits specified by the stateMask, when called
+        * with TVIS_EXPANDED, the entire state is returned.  The fix is
+        * to explicitly check for the TVIS_EXPANDED bit.
+        */
+        state = OS.SendMessage (hwnd, OS.TVM_GETITEMSTATE, handle, OS.TVIS_EXPANDED);
+    }
+    return (state & OS.TVIS_EXPANDED) !is 0;
+}
+
+/**
+ * Returns the font that the receiver will use to paint textual information for this item.
+ *
+ * @return the receiver's font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Font getFont () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    return font is -1 ? parent.getFont () : Font.win32_new (display, font);
+}
+
+/**
+ * Returns the font that the receiver will use to paint textual information
+ * for the specified cell in this item.
+ *
+ * @param index the column index
+ * @return the receiver's font
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Font getFont (int index) {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count -1) return getFont ();
+    int hFont = (cellFont !is null) ? cellFont [index] : font;
+    return hFont is -1 ? getFont () : Font.win32_new (display, hFont);
+}
+
+/**
+ * Returns the foreground color that the receiver will use to draw.
+ *
+ * @return the receiver's foreground color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public Color getForeground () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (foreground is -1) return parent.getForeground ();
+    return Color.win32_new (display, foreground);
+}
+
+/**
+ *
+ * Returns the foreground color at the given column index in the receiver.
+ *
+ * @param index the column index
+ * @return the foreground color
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Color getForeground (int index) {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count -1) return getForeground ();
+    int pixel = cellForeground !is null ? cellForeground [index] : -1;
+    return pixel is -1 ? getForeground () : Color.win32_new (display, pixel);
+}
+
+/**
+ * Returns <code>true</code> if the receiver is grayed,
+ * and false otherwise. When the parent does not have
+ * the <code>CHECK style, return false.
+ * <p>
+ *
+ * @return the grayed state of the checkbox
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public bool getGrayed () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if ((parent.style & DWT.CHECK) is 0) return false;
+    int hwnd = parent.handle;
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+    tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+    tvItem.hItem = handle;
+    int result = OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+    return (result !is 0) && ((tvItem.state >> 12) > 2);
+}
+
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public TreeItem getItem (int index) {
+    checkWidget ();
+    if (index < 0) error (DWT.ERROR_INVALID_RANGE);
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int hwnd = parent.handle;
+    int hFirstItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle);
+    if (hFirstItem is 0) error (DWT.ERROR_INVALID_RANGE);
+    int hItem = parent.findItem (hFirstItem, index);
+    if (hItem is 0) error (DWT.ERROR_INVALID_RANGE);
+    return parent._getItem (hItem);
+}
+
+/**
+ * Returns the number of items contained in the receiver
+ * that are direct item children of the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int hwnd = parent.handle;
+    int hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle);
+    if (hItem is 0) return 0;
+    return parent.getItemCount (hItem);
+}
+
+/**
+ * Returns a (possibly empty) array of <code>TreeItem</code>s which
+ * are the direct item children of the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the receiver's items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem [] getItems () {
+    checkWidget ();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    int hwnd = parent.handle;
+    int hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle);
+    if (hItem is 0) return new TreeItem [0];
+    return parent.getItems (hItem);
+}
+
+public Image getImage () {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    return super.getImage ();
+}
+
+/**
+ * Returns the image stored at the given column index in the receiver,
+ * or null if the image has not been set or if the column does not exist.
+ *
+ * @param index the column index
+ * @return the image stored at the given column index in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Image getImage (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (index is 0) return getImage ();
+    if (images !is null) {
+        if (0 <= index && index < images.length) return images [index];
+    }
+    return null;
+}
+
+/**
+ * Returns a rectangle describing the size and location
+ * relative to its parent of an image at a column in the
+ * tree.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding image rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Rectangle getImageBounds (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    RECT rect = getBounds (index, false, true, false);
+    int width = rect.right - rect.left, height = rect.bottom - rect.top;
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+/**
+ * Returns the receiver's parent, which must be a <code>Tree</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Tree getParent () {
+    checkWidget ();
+    return parent;
+}
+
+/**
+ * Returns the receiver's parent item, which must be a
+ * <code>TreeItem</code> or null when the receiver is a
+ * root.
+ *
+ * @return the receiver's parent item
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem getParentItem () {
+    checkWidget ();
+    int hwnd = parent.handle;
+    int hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, handle);
+    return hItem !is 0 ? parent._getItem (hItem) : null;
+}
+
+public String getText () {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    return super.getText ();
+}
+
+/**
+ * Returns the text stored at the given column index in the receiver,
+ * or empty string if the text has not been set.
+ *
+ * @param index the column index
+ * @return the text stored at the given column index in the receiver
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public String getText (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (index is 0) return getText ();
+    if (strings !is null) {
+        if (0 <= index && index < strings.length) {
+            String string = strings [index];
+            return string !is null ? string : "";
+        }
+    }
+    return "";
+}
+
+/**
+ * Returns a rectangle describing the size and location
+ * relative to its parent of the text at a column in the
+ * tree.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding text rectangle
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public Rectangle getTextBounds (int index) {
+    checkWidget();
+    if (!parent.checkData (this, true)) error (DWT.ERROR_WIDGET_DISPOSED);
+    RECT rect = getBounds (index, true, false, true);
+    if (index is 0) rect.left += Tree.INSET - 1;
+    rect.left = Math.min (rect.left, rect.right);
+    rect.right = rect.right - Tree.INSET;
+    int width = Math.max (0, rect.right - rect.left);
+    int height = Math.max (0, rect.bottom - rect.top);
+    return new Rectangle (rect.left, rect.top, width, height);
+}
+
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int indexOf (TreeItem item) {
+    checkWidget ();
+    if (item is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (item.isDisposed()) error(DWT.ERROR_INVALID_ARGUMENT);
+    int hwnd = parent.handle;
+    int hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle);
+    return hItem is 0 ? -1 : parent.findIndex (hItem, item.handle);
+}
+
+void redraw () {
+    if (parent.currentItem is this || parent.drawCount !is 0) return;
+    int hwnd = parent.handle;
+    if (!OS.IsWindowVisible (hwnd)) return;
+    RECT rect = new RECT ();
+    rect.left = handle;
+    /*
+    * When there are no columns and the tree is not
+    * full selection, redraw only the text.  This is
+    * an optimization to reduce flashing.
+    */
+    bool full = (parent.style & (DWT.FULL_SELECTION | DWT.VIRTUAL)) !is 0;
+    if (!full) {
+        int hwndHeader = parent.hwndHeader;
+        if (hwndHeader !is 0) {
+            full = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0) !is 0;
+        }
+        if (!full) {
+            if (parent.hooks (DWT.EraseItem) || parent.hooks (DWT.PaintItem)) {
+                full = true;
+            }
+        }
+    }
+    if (OS.SendMessage (hwnd, OS.TVM_GETITEMRECT, full ? 0 : 1, rect) !is 0) {
+        OS.InvalidateRect (hwnd, rect, true);
+    }
+}
+
+void redraw (int column, bool drawText, bool drawImage) {
+    if (parent.currentItem is this || parent.drawCount !is 0) return;
+    int hwnd = parent.handle;
+    if (!OS.IsWindowVisible (hwnd)) return;
+    bool fullImage = column is 0 && drawText && drawImage;
+    RECT rect = getBounds (column, drawText, drawImage, true, fullImage, true, 0);
+    OS.InvalidateRect (hwnd, rect, true);
+}
+
+void releaseChildren (bool destroy) {
+    if (destroy) {
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+        parent.releaseItems (handle, tvItem);
+    }
+    super.releaseChildren (destroy);
+}
+
+void releaseHandle () {
+    super.releaseHandle ();
+    handle = 0;
+    parent = null;
+}
+
+void releaseWidget () {
+    super.releaseWidget ();
+    strings = null;
+    images = null;
+    cellBackground = cellForeground = cellFont = null;
+}
+
+/**
+ * Removes all of the items from the receiver.
+ * <p>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void removeAll () {
+    checkWidget ();
+    int hwnd = parent.handle;
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
+    tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle);
+    while (tvItem.hItem !is 0) {
+        OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+        TreeItem item = tvItem.lParam !is -1 ? parent.items [tvItem.lParam] : null;
+        if (item !is null && !item.isDisposed ()) {
+            item.dispose ();
+        } else {
+            parent.releaseItem (tvItem.hItem, tvItem, false);
+            parent.destroyItem (null, tvItem.hItem);
+        }
+        tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle);
+    }
+}
+
+/**
+ * Sets the receiver's background color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public void setBackground (Color color) {
+    checkWidget ();
+    if (color !is null && color.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int pixel = -1;
+    if (color !is null) {
+        parent.customDraw = true;
+        pixel = color.handle;
+    }
+    if (background is pixel) return;
+    background = pixel;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    redraw ();
+}
+
+/**
+ * Sets the background color at the given column index in the receiver
+ * to the color specified by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param index the column index
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ *
+ */
+public void setBackground (int index, Color color) {
+    checkWidget ();
+    if (color !is null && color.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    int pixel = -1;
+    if (color !is null) {
+        parent.customDraw = true;
+        pixel = color.handle;
+    }
+    if (cellBackground is null) {
+        cellBackground = new int [count];
+        for (int i = 0; i < count; i++) {
+            cellBackground [i] = -1;
+        }
+    }
+    if (cellBackground [index] is pixel) return;
+    cellBackground [index] = pixel;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    redraw (index, true, true);
+}
+
+/**
+ * Sets the checked state of the receiver.
+ * <p>
+ *
+ * @param checked the new checked state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setChecked (bool checked) {
+    checkWidget ();
+    if ((parent.style & DWT.CHECK) is 0) return;
+    int hwnd = parent.handle;
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+    tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+    tvItem.hItem = handle;
+    OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+    int state = tvItem.state >> 12;
+    if (checked) {
+        if ((state & 0x1) !is 0) state++;
+    } else {
+        if ((state & 0x1) is 0) --state;
+    }
+    state <<= 12;
+    if (tvItem.state is state) return;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    tvItem.state = state;
+    OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem);
+    /*
+    * Bug in Windows.  When TVM_SETITEM is used to set
+    * the state image of an item inside TVN_GETDISPINFO,
+    * the new state is not redrawn.  The fix is to force
+    * a redraw.
+    */
+    if ((parent.style & DWT.VIRTUAL) !is 0) {
+        if (parent.currentItem is this && OS.IsWindowVisible (hwnd)) {
+            RECT rect = new RECT ();
+            rect.left = handle;
+            if (OS.SendMessage (hwnd, OS.TVM_GETITEMRECT, 0, rect) !is 0) {
+                OS.InvalidateRect (hwnd, rect, true);
+            }
+        }
+    }
+}
+
+/**
+ * Sets the expanded state of the receiver.
+ * <p>
+ *
+ * @param expanded the new expanded state
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setExpanded (bool expanded) {
+    checkWidget ();
+
+    /* Do nothing when the item is a leaf or already expanded */
+    int hwnd = parent.handle;
+    if (OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle) is 0) {
+        return;
+    }
+    int state = 0;
+    if (OS.IsWinCE) {
+        TVITEM tvItem = new TVITEM ();
+        tvItem.hItem = handle;
+        tvItem.mask = OS.TVIF_STATE;
+        OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+        state = tvItem.state;
+    } else {
+        /*
+        * Bug in Windows.  Despite the fact that TVM_GETITEMSTATE claims
+        * to return only the bits specified by the stateMask, when called
+        * with TVIS_EXPANDED, the entire state is returned.  The fix is
+        * to explicitly check for the TVIS_EXPANDED bit.
+        */
+        state = OS.SendMessage (hwnd, OS.TVM_GETITEMSTATE, handle, OS.TVIS_EXPANDED);
+    }
+    if (((state & OS.TVIS_EXPANDED) !is 0) is expanded) return;
+
+    /*
+    * Feature in Windows.  When TVM_EXPAND is used to expand
+    * an item, the widget scrolls to show the item and the
+    * newly expanded items.  While not strictly incorrect,
+    * this means that application code that expands tree items
+    * in a background thread can scroll the widget while the
+    * user is interacting with it.  The fix is to remember
+    * the top item and the bounds of every tree item, turn
+    * redraw off, expand the item, scroll back to the top
+    * item.  If none of the rectangles have moved, then
+    * it is safe to turn redraw back on without redrawing
+    * the control.
+    */
+    RECT oldRect = null;
+    RECT [] rects = null;
+    SCROLLINFO oldInfo = null;
+    int count = 0, hBottomItem = 0;
+    bool redraw = false, noScroll = true;
+    int hTopItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+    if (noScroll && hTopItem !is 0) {
+        oldInfo = new SCROLLINFO ();
+        oldInfo.cbSize = SCROLLINFO.sizeof;
+        oldInfo.fMask = OS.SIF_ALL;
+        if (!OS.GetScrollInfo (hwnd, OS.SB_HORZ, oldInfo)) {
+            oldInfo = null;
+        }
+        if (parent.drawCount is 0 && OS.IsWindowVisible (hwnd)) {
+            bool noAnimate = true;
+            count = OS.SendMessage (hwnd, OS.TVM_GETVISIBLECOUNT, 0, 0);
+            rects = new RECT [count + 1];
+            int hItem = hTopItem, index = 0;
+            while (hItem !is 0 && (noAnimate || hItem !is handle) && index < count) {
+                RECT rect = new RECT ();
+                rect.left = hItem;
+                if (OS.SendMessage (hwnd, OS.TVM_GETITEMRECT, 1, rect) !is 0) {
+                    rects [index++] = rect;
+                }
+                hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
+            }
+            if (noAnimate || hItem !is handle) {
+                redraw = true;
+                count = index;
+                hBottomItem = hItem;
+                oldRect = new RECT ();
+                OS.GetClientRect (hwnd, oldRect);
+                int topHandle = parent.topHandle ();
+                OS.UpdateWindow (topHandle);
+                OS.DefWindowProc (topHandle, OS.WM_SETREDRAW, 0, 0);
+                if (hwnd !is topHandle) {
+                    OS.UpdateWindow (hwnd);
+                    OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 0, 0);
+                }
+                /*
+                * This code is intentionally commented.
+                */
+//              OS.SendMessage (hwnd, OS.WM_SETREDRAW, 0, 0);
+            }
+        }
+    }
+
+    /*
+    * Feature in Windows.  When the user collapses the root
+    * of a subtree that has the focus item, Windows moves
+    * the selection to the root of the subtree and issues
+    * a TVN_SELCHANGED to inform the programmer that the
+    * selection has changed.  When the programmer collapses
+    * the same subtree using TVM_EXPAND, Windows does not
+    * send the selection changed notification.  This is not
+    * strictly wrong but is inconsistent.  The fix is to
+    * check whether the selection has changed and issue
+    * the event.
+    */
+    int hOldItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+
+    /* Expand or collapse the item */
+    parent.ignoreExpand = true;
+    OS.SendMessage (hwnd, OS.TVM_EXPAND, expanded ? OS.TVE_EXPAND : OS.TVE_COLLAPSE, handle);
+    parent.ignoreExpand = false;
+
+    /* Scroll back to the top item */
+    if (noScroll && hTopItem !is 0) {
+        bool collapsed = false;
+        if (!expanded) {
+            RECT rect = new RECT ();
+            rect.left = hTopItem;
+            while (hTopItem !is 0 && OS.SendMessage (hwnd, OS.TVM_GETITEMRECT, 0, rect) is 0) {
+                hTopItem = rect.left = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hTopItem);
+                collapsed = true;
+            }
+        }
+        bool scrolled = true;
+        if (hTopItem !is 0) {
+            OS.SendMessage (hwnd, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hTopItem);
+            scrolled = hTopItem !is OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
+        }
+        if (!collapsed && !scrolled && oldInfo !is null) {
+            SCROLLINFO newInfo = new SCROLLINFO ();
+            newInfo.cbSize = SCROLLINFO.sizeof;
+            newInfo.fMask = OS.SIF_ALL;
+            if (OS.GetScrollInfo (hwnd, OS.SB_HORZ, newInfo)) {
+                if (oldInfo.nPos !is newInfo.nPos) {
+                    int lParam = OS.SB_THUMBPOSITION | ((oldInfo.nPos << 16) & 0xFFFF0000);
+                    OS.SendMessage (hwnd, OS.WM_HSCROLL, lParam, 0);
+                }
+            }
+        }
+        if (redraw) {
+            bool fixScroll = false;
+            if (!collapsed && !scrolled) {
+                RECT newRect = new RECT ();
+                OS.GetClientRect (hwnd, newRect);
+                if (OS.EqualRect (oldRect, newRect)) {
+                    int hItem = hTopItem, index = 0;
+                    while (hItem !is 0 && index < count) {
+                        RECT rect = new RECT ();
+                        rect.left = hItem;
+                        if (OS.SendMessage (hwnd, OS.TVM_GETITEMRECT, 1, rect) !is 0) {
+                            if (!OS.EqualRect (rect, rects [index])) {
+                                break;
+                            }
+                        }
+                        hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
+                        index++;
+                    }
+                    fixScroll = index is count && hItem is hBottomItem;
+                }
+            }
+            int topHandle = parent.topHandle ();
+            OS.DefWindowProc (topHandle, OS.WM_SETREDRAW, 1, 0);
+            if (hwnd !is topHandle) {
+                OS.DefWindowProc (hwnd, OS.WM_SETREDRAW, 1, 0);
+            }
+            /*
+            * This code is intentionally commented.
+            */
+//          OS.SendMessage (hwnd, OS.WM_SETREDRAW, 1, 0);
+            if (fixScroll) {
+                parent.updateScrollBar ();
+                SCROLLINFO info = new SCROLLINFO ();
+                info.cbSize = SCROLLINFO.sizeof;
+                info.fMask = OS.SIF_ALL;
+                if (OS.GetScrollInfo (hwnd, OS.SB_VERT, info)) {
+                    OS.SetScrollInfo (hwnd, OS.SB_VERT, info, true);
+                }
+                if (handle is hBottomItem) {
+                    RECT rect = new RECT ();
+                    rect.left = hBottomItem;
+                    if (OS.SendMessage (hwnd, OS.TVM_GETITEMRECT, 0, rect) !is 0) {
+                        OS.InvalidateRect (hwnd, rect, true);
+                    }
+                }
+            } else {
+                if (OS.IsWinCE) {
+                    OS.InvalidateRect (topHandle, null, true);
+                    if (hwnd !is topHandle) OS.InvalidateRect (hwnd, null, true);
+                } else {
+                    int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
+                    OS.RedrawWindow (topHandle, null, 0, flags);
+                }
+            }
+        }
+    }
+
+    /* Check for a selection event */
+    int hNewItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
+    if (hNewItem !is hOldItem) {
+        Event event = new Event ();
+        if (hNewItem !is 0) {
+            event.item = parent._getItem (hNewItem);
+            parent.hAnchor = hNewItem;
+        }
+        parent.sendEvent (DWT.Selection, event);
+    }
+}
+
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * for this item to the font specified by the argument, or to the default font
+ * for that kind of control if the argument is null.
+ *
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setFont (Font font){
+    checkWidget ();
+    if (font !is null && font.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int hFont = -1;
+    if (font !is null) {
+        parent.customDraw = true;
+        hFont = font.handle;
+    }
+    if (this.font is hFont) return;
+    this.font = hFont;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    /*
+    * Bug in Windows.  When the font is changed for an item,
+    * the bounds for the item are not updated, causing the text
+    * to be clipped.  The fix is to reset the text, causing
+    * Windows to compute the new bounds using the new font.
+    */
+    if ((parent.style & DWT.VIRTUAL) is 0 && !cached && !parent.painted) {
+        return;
+    }
+    int hwnd = parent.handle;
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT;
+    tvItem.hItem = handle;
+    tvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+    OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem);
+}
+
+
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * for the specified cell in this item to the font specified by the
+ * argument, or to the default font for that kind of control if the
+ * argument is null.
+ *
+ * @param index the column index
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setFont (int index, Font font) {
+    checkWidget ();
+    if (font !is null && font.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    int hFont = -1;
+    if (font !is null) {
+        parent.customDraw = true;
+        hFont = font.handle;
+    }
+    if (cellFont is null) {
+        cellFont = new int [count];
+        for (int i = 0; i < count; i++) {
+            cellFont [i] = -1;
+        }
+    }
+    if (cellFont [index] is hFont) return;
+    cellFont [index] = hFont;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    /*
+    * Bug in Windows.  When the font is changed for an item,
+    * the bounds for the item are not updated, causing the text
+    * to be clipped.  The fix is to reset the text, causing
+    * Windows to compute the new bounds using the new font.
+    */
+    if (index is 0) {
+        if ((parent.style & DWT.VIRTUAL) is 0 && !cached && !parent.painted) {
+            return;
+        }
+        int hwnd = parent.handle;
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT;
+        tvItem.hItem = handle;
+        tvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+        OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem);
+    } else {
+        redraw (index, true, false);
+    }
+}
+
+/**
+ * Sets the receiver's foreground color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @since 2.0
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public void setForeground (Color color) {
+    checkWidget ();
+    if (color !is null && color.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int pixel = -1;
+    if (color !is null) {
+        parent.customDraw = true;
+        pixel = color.handle;
+    }
+    if (foreground is pixel) return;
+    foreground = pixel;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    redraw ();
+}
+
+/**
+ * Sets the foreground color at the given column index in the receiver
+ * to the color specified by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param index the column index
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ *
+ */
+public void setForeground (int index, Color color){
+    checkWidget ();
+    if (color !is null && color.isDisposed ()) {
+        DWT.error (DWT.ERROR_INVALID_ARGUMENT);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    int pixel = -1;
+    if (color !is null) {
+        parent.customDraw = true;
+        pixel = color.handle;
+    }
+    if (cellForeground is null) {
+        cellForeground = new int [count];
+        for (int i = 0; i < count; i++) {
+            cellForeground [i] = -1;
+        }
+    }
+    if (cellForeground [index] is pixel) return;
+    cellForeground [index] = pixel;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    redraw (index, true, false);
+}
+
+/**
+ * Sets the grayed state of the checkbox for this item.  This state change
+ * only applies if the Tree was created with the DWT.CHECK style.
+ *
+ * @param grayed the new grayed state of the checkbox
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setGrayed (bool grayed) {
+    checkWidget ();
+    if ((parent.style & DWT.CHECK) is 0) return;
+    int hwnd = parent.handle;
+    TVITEM tvItem = new TVITEM ();
+    tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+    tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+    tvItem.hItem = handle;
+    OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+    int state = tvItem.state >> 12;
+    if (grayed) {
+        if (state <= 2) state +=2;
+    } else {
+        if (state > 2) state -=2;
+    }
+    state <<= 12;
+    if (tvItem.state is state) return;
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    tvItem.state = state;
+    OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem);
+    /*
+    * Bug in Windows.  When TVM_SETITEM is used to set
+    * the state image of an item inside TVN_GETDISPINFO,
+    * the new state is not redrawn.  The fix is to force
+    * a redraw.
+    */
+    if ((parent.style & DWT.VIRTUAL) !is 0) {
+        if (parent.currentItem is this && OS.IsWindowVisible (hwnd)) {
+            RECT rect = new RECT ();
+            rect.left = handle;
+            if (OS.SendMessage (hwnd, OS.TVM_GETITEMRECT, 0, rect) !is 0) {
+                OS.InvalidateRect (hwnd, rect, true);
+            }
+        }
+    }
+}
+
+/**
+ * Sets the image for multiple columns in the tree.
+ *
+ * @param images the array of new images
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the array of images is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if one of the images has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setImage (Image [] images) {
+    checkWidget();
+    if (images is null) error (DWT.ERROR_NULL_ARGUMENT);
+    for (int i=0; i<images.length; i++) {
+        setImage (i, images [i]);
+    }
+}
+
+/**
+ * Sets the receiver's image at a column.
+ *
+ * @param index the column index
+ * @param image the new image
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setImage (int index, Image image) {
+    checkWidget();
+    if (image !is null && image.isDisposed ()) {
+        error(DWT.ERROR_INVALID_ARGUMENT);
+    }
+    Image oldImage = null;
+    if (index is 0) {
+        if (image !is null && image.type is DWT.ICON) {
+            if (image.equals (this.image)) return;
+        }
+        oldImage = this.image;
+        super.setImage (image);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    if (images is null && index !is 0) {
+        images = new Image [count];
+        images [0] = image;
+    }
+    if (images !is null) {
+        if (image !is null && image.type is DWT.ICON) {
+            if (image.equals (images [index])) return;
+        }
+        oldImage = images [index];
+        images [index] = image;
+    }
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+
+    /* Ensure that the image list is created */
+    //TODO - items that are not in column zero don't need to be in the image list
+    parent.imageIndex (image, index);
+
+    if (index is 0) {
+        if ((parent.style & DWT.VIRTUAL) is 0 &&!cached && !parent.painted) {
+            return;
+        }
+        int hwnd = parent.handle;
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE;
+        tvItem.hItem = handle;
+        tvItem.iImage = tvItem.iSelectedImage = OS.I_IMAGECALLBACK;
+        /*
+        * Bug in Windows.  When I_IMAGECALLBACK is used with TVM_SETITEM
+        * to indicate that an image has changed, Windows does not draw
+        * the new image.  The fix is to use LPSTR_TEXTCALLBACK to force
+        * Windows to ask for the text, causing Windows to ask for both.
+        */
+        tvItem.mask |= OS.TVIF_TEXT;
+        tvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+        OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem);
+    } else {
+        bool drawText = (image is null && oldImage !is null) || (image !is null && oldImage is null);
+        redraw (index, drawText, true);
+    }
+}
+
+public void setImage (Image image) {
+    checkWidget ();
+    setImage (0, image);
+}
+
+/**
+ * Sets the number of child items contained in the receiver.
+ *
+ * @param count the number of items
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setItemCount (int count) {
+    checkWidget ();
+    count = Math.max (0, count);
+    int hwnd = parent.handle;
+    int hItem = OS.SendMessage (hwnd, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, handle);
+    parent.setItemCount (count, handle, hItem);
+}
+
+/**
+ * Sets the text for multiple columns in the tree.
+ *
+ * @param strings the array of new strings
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setText (String [] strings) {
+    checkWidget();
+    if (strings is null) error (DWT.ERROR_NULL_ARGUMENT);
+    for (int i=0; i<strings.length; i++) {
+        String string = strings [i];
+        if (string !is null) setText (i, string);
+    }
+}
+
+/**
+ * Sets the receiver's text at a column
+ *
+ * @param index the column index
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setText (int index, String string) {
+    checkWidget();
+    if (string is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (index is 0) {
+        if (string.equals (text)) return;
+        super.setText (string);
+    }
+    int count = Math.max (1, parent.getColumnCount ());
+    if (0 > index || index > count - 1) return;
+    if (strings is null && index !is 0) {
+        strings = new String [count];
+        strings [0] = text;
+    }
+    if (strings !is null) {
+        if (string.equals (strings [index])) return;
+        strings [index] = string;
+    }
+    if ((parent.style & DWT.VIRTUAL) !is 0) cached = true;
+    if (index is 0) {
+        if ((parent.style & DWT.VIRTUAL) is 0 && !cached && !parent.painted) {
+            return;
+        }
+        int hwnd = parent.handle;
+        TVITEM tvItem = new TVITEM ();
+        tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT;
+        tvItem.hItem = handle;
+        tvItem.pszText = OS.LPSTR_TEXTCALLBACK;
+        OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem);
+    } else {
+        redraw (index, true, false);
+    }
+}
+
+public void setText (String string) {
+    checkWidget();
+    setText (0, string);
+}
+
+/*public*/ void sort () {
+    checkWidget ();
+    if ((parent.style & DWT.VIRTUAL) !is 0) return;
+    parent.sort (handle, false);
+}
+
+}
+++/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/TypedListener.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,301 @@
+/*******************************************************************************
+ * 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 <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.widgets.TypedListener;
+
+
+import dwt.internal.DWTEventListener;
+import dwt.DWT;
+import dwt.widgets.Listener;
+import dwt.widgets.Event;
+import dwt.events.ArmEvent;
+import dwt.events.ArmListener;
+import dwt.events.ControlEvent;
+import dwt.events.ControlListener;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.DragDetectEvent;
+import dwt.events.DragDetectListener;
+import dwt.events.ExpandEvent;
+import dwt.events.ExpandListener;
+import dwt.events.FocusEvent;
+import dwt.events.FocusListener;
+import dwt.events.HelpEvent;
+import dwt.events.HelpListener;
+import dwt.events.KeyEvent;
+import dwt.events.KeyListener;
+import dwt.events.MenuEvent;
+import dwt.events.MenuListener;
+import dwt.events.MenuDetectEvent;
+import dwt.events.MenuDetectListener;
+import dwt.events.ModifyEvent;
+import dwt.events.ModifyListener;
+import dwt.events.MouseEvent;
+import dwt.events.MouseListener;
+import dwt.events.MouseMoveListener;
+import dwt.events.MouseTrackListener;
+import dwt.events.MouseWheelListener;
+import dwt.events.PaintEvent;
+import dwt.events.PaintListener;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.events.ShellEvent;
+import dwt.events.ShellListener;
+import dwt.events.TraverseEvent;
+import dwt.events.TraverseListener;
+import dwt.events.TreeEvent;
+import dwt.events.TreeListener;
+import dwt.events.VerifyEvent;
+import dwt.events.VerifyListener;
+import dwt.graphics.GC;
+
+
+/**
+ * Instances of this class are <em>internal DWT implementation</em>
+ * objects which provide a mapping between the typed and untyped
+ * listener mechanisms that DWT supports.
+ * <p>
+ * <b>IMPORTANT:</b> This class 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 should never be
+ * referenced from application code.
+ * </p>
+ *
+ * @see Listener
+ */
+public class TypedListener : Listener {
+
+    /**
+     * The receiver's event listener
+     */
+    protected DWTEventListener eventListener;
+
+/**
+ * Constructs a new instance of this class for the given event listener.
+ * <p>
+ * <b>IMPORTANT:</b> This method 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 should never be
+ * referenced from application code.
+ * </p>
+ *
+ * @param listener the event listener to store in the receiver
+ */
+public this (DWTEventListener listener) {
+    eventListener = listener;
+}
+
+/**
+ * Returns the receiver's event listener.
+ * <p>
+ * <b>IMPORTANT:</b> This method 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 should never be
+ * referenced from application code.
+ * </p>
+ *
+ * @return the receiver's event listener
+ */
+public DWTEventListener getEventListener () {
+    return eventListener;
+}
+
+/**
+ * Handles the given event.
+ * <p>
+ * <b>IMPORTANT:</b> This method 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 should never be
+ * referenced from application code.
+ * </p>
+ * @param e the event to handle
+ */
+public void handleEvent (Event e) {
+    switch (e.type) {
+        case DWT.Activate: {
+            (cast(ShellListener) eventListener).shellActivated(new ShellEvent(e));
+            break;
+        }
+        case DWT.Arm: {
+            (cast(ArmListener) eventListener).widgetArmed (new ArmEvent (e));
+            break;
+        }
+        case DWT.Close: {
+            /* Fields set by Decorations */
+            ShellEvent event = new ShellEvent (e);
+            (cast(ShellListener) eventListener).shellClosed(event);
+            e.doit = event.doit;
+            break;
+        }
+        case DWT.Collapse: {
+            if ( auto l = cast(TreeListener)eventListener ) {
+                l.treeCollapsed(new TreeEvent(e));
+            } else {
+                (cast(ExpandListener) eventListener).itemCollapsed(new ExpandEvent(e));
+            }
+            break;
+        }
+        case DWT.Deactivate: {
+            (cast(ShellListener) eventListener).shellDeactivated(new ShellEvent(e));
+            break;
+        }
+        case DWT.Deiconify: {
+            (cast(ShellListener) eventListener).shellDeiconified(new ShellEvent(e));
+            break;
+        }
+        case DWT.DefaultSelection: {
+            (cast(SelectionListener)eventListener).widgetDefaultSelected(new SelectionEvent(e));
+            break;
+        }
+        case DWT.Dispose: {
+            (cast(DisposeListener) eventListener).widgetDisposed(new DisposeEvent(e));
+            break;
+        }
+        case DWT.DragDetect: {
+            (cast(DragDetectListener) eventListener).dragDetected(new DragDetectEvent(e));
+            break;
+        }
+        case DWT.Expand: {
+            if (auto l = cast(TreeListener)eventListener ) {
+                l.treeExpanded(new TreeEvent(e));
+            } else {
+                (cast(ExpandListener) eventListener).itemExpanded(new ExpandEvent(e));
+            }
+            break;
+        }
+        case DWT.FocusIn: {
+            (cast(FocusListener) eventListener).focusGained(new FocusEvent(e));
+            break;
+        }
+        case DWT.FocusOut: {
+            (cast(FocusListener) eventListener).focusLost(new FocusEvent(e));
+            break;
+        }
+        case DWT.Help: {
+            (cast(HelpListener) eventListener).helpRequested (new HelpEvent (e));
+            break;
+        }
+        case DWT.Hide: {
+            (cast(MenuListener) eventListener).menuHidden(new MenuEvent(e));
+            break;
+        }
+        case DWT.Iconify: {
+            (cast(ShellListener) eventListener).shellIconified(new ShellEvent(e));
+            break;
+        }
+        case DWT.KeyDown: {
+            /* Fields set by Control */
+            KeyEvent event = new KeyEvent(e);
+            (cast(KeyListener) eventListener).keyPressed(event);
+            e.doit = event.doit;
+            break;
+        }
+        case DWT.KeyUp: {
+            /* Fields set by Control */
+            KeyEvent event = new KeyEvent(e);
+            (cast(KeyListener) eventListener).keyReleased(event);
+            e.doit = event.doit;
+            break;
+        }
+        case DWT.Modify: {
+            (cast(ModifyListener) eventListener).modifyText(new ModifyEvent(e));
+            break;
+        }
+        case DWT.MenuDetect: {
+            MenuDetectEvent event = new MenuDetectEvent(e);
+            (cast(MenuDetectListener) eventListener).menuDetected(event);
+            e.x = event.x;
+            e.y = event.y;
+            e.doit = event.doit;
+            break;
+        }
+        case DWT.MouseDown: {
+            (cast(MouseListener) eventListener).mouseDown(new MouseEvent(e));
+            break;
+        }
+        case DWT.MouseDoubleClick: {
+            (cast(MouseListener) eventListener).mouseDoubleClick(new MouseEvent(e));
+            break;
+        }
+        case DWT.MouseEnter: {
+            (cast(MouseTrackListener) eventListener).mouseEnter (new MouseEvent (e));
+            break;
+        }
+        case DWT.MouseExit: {
+            (cast(MouseTrackListener) eventListener).mouseExit (new MouseEvent (e));
+            break;
+        }
+        case DWT.MouseHover: {
+            (cast(MouseTrackListener) eventListener).mouseHover (new MouseEvent (e));
+            break;
+        }
+        case DWT.MouseMove: {
+            (cast(MouseMoveListener) eventListener).mouseMove(new MouseEvent(e));
+            return;
+        }
+        case DWT.MouseWheel: {
+            (cast(MouseWheelListener) eventListener).mouseScrolled(new MouseEvent(e));
+            return;
+        }
+        case DWT.MouseUp: {
+            (cast(MouseListener) eventListener).mouseUp(new MouseEvent(e));
+            break;
+        }
+        case DWT.Move: {
+            (cast(ControlListener) eventListener).controlMoved(new ControlEvent(e));
+            break;
+        }
+        case DWT.Paint: {
+            /* Fields set by Control */
+            PaintEvent event = new PaintEvent (e);
+            (cast(PaintListener) eventListener).paintControl (event);
+            e.gc = event.gc;
+            break;
+        }
+        case DWT.Resize: {
+            (cast(ControlListener) eventListener).controlResized(new ControlEvent(e));
+            break;
+        }
+        case DWT.Selection: {
+            /* Fields set by Sash */
+            SelectionEvent event = new SelectionEvent (e);
+            (cast(SelectionListener) eventListener).widgetSelected (event);
+            e.x = event.x;
+            e.y = event.y;
+            e.doit = event.doit;
+            break;
+        }
+        case DWT.Show: {
+            (cast(MenuListener) eventListener).menuShown(new MenuEvent(e));
+            break;
+        }
+        case DWT.Traverse: {
+            /* Fields set by Control */
+            TraverseEvent event = new TraverseEvent (e);
+            (cast(TraverseListener) eventListener).keyTraversed (event);
+            e.detail = event.detail;
+            e.doit = event.doit;
+            break;
+        }
+        case DWT.Verify: {
+            /* Fields set by Text, RichText */
+            VerifyEvent event = new VerifyEvent (e);
+            (cast(VerifyListener) eventListener).verifyText (event);
+            e.text = event.text;
+            e.doit = event.doit;
+            break;
+        }
+        default:
+    }
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/widgets/Widget.d	Mon Jan 28 04:47:28 2008 +0100
@@ -0,0 +1,2436 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.widgets.Widget;
+
+import dwt.widgets.Display;
+
+class Widget {
+    Display display;
+    void checkWidget();
+    void releaseWidget();
+    void releaseChildren(bool);
+    void release(bool);
+    void releaseParent();
+    bool isDisposed();
+    void dispose();
+    void error(int);
+    this () ;
+    this (Widget parent, int style) ;
+}
+/++
+import dwt.DWT;
+import dwt.DWTException;
+import dwt.events.DisposeListener;
+import dwt.graphics.GC;
+import dwt.graphics.GCData;
+import dwt.internal.DWTEventListener;
+import dwt.internal.win32.LRESULT;
+import dwt.internal.win32.MSG;
+import dwt.internal.win32.OS;
+import dwt.internal.win32.PAINTSTRUCT;
+import dwt.internal.win32.POINT;
+import dwt.internal.win32.RECT;
+import dwt.internal.win32.SHRGINFO;
+import dwt.internal.win32.TRACKMOUSEEVENT;
+
+/**
+ * This class is the abstract superclass of all user interface objects.
+ * Widgets are created, disposed and issue notification to listeners
+ * when events occur which affect them.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Dispose</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is intended to be subclassed <em>only</em>
+ * within the DWT implementation. However, it has not been marked
+ * final to allow those outside of the DWT development team to implement
+ * patched versions of the class in order to get around specific
+ * limitations in advance of when those limitations can be addressed
+ * by the team.  Any class built using subclassing to access the internals
+ * of this class will likely fail to compile or run between releases and
+ * may be strongly platform specific. Subclassing should not be attempted
+ * without an intimate and detailed understanding of the workings of the
+ * hierarchy. No support is provided for user-written classes which are
+ * implemented as subclasses of this class.
+ * </p>
+ *
+ * @see #checkSubclass
+ */
+
+public abstract class Widget {
+    int style, state;
+    Display display;
+    EventTable eventTable;
+    Object data;
+
+    /* Global state flags */
+    static final int DISPOSED       = 1<<0;
+    static final int CANVAS         = 1<<1;
+    static final int KEYED_DATA     = 1<<2;
+    static final int DISABLED       = 1<<3;
+    static final int HIDDEN         = 1<<4;
+
+    /* A layout was requested on this widget */
+    static final int LAYOUT_NEEDED  = 1<<5;
+
+    /* The preferred size of a child has changed */
+    static final int LAYOUT_CHANGED = 1<<6;
+
+    /* A layout was requested in this widget hierarchy */
+    static final int LAYOUT_CHILD = 1<<7;
+
+    /* Background flags */
+    static final int THEME_BACKGROUND = 1<<8;
+    static final int DRAW_BACKGROUND = 1<<9;
+    static final int PARENT_BACKGROUND = 1<<10;
+
+    /* Dispose and release flags */
+    static final int RELEASED       = 1<<11;
+    static final int DISPOSE_SENT   = 1<<12;
+
+    /* More global widget state flags */
+    static final int TRACK_MOUSE    = 1<<13;
+    static final int FOREIGN_HANDLE = 1<<14;
+    static final int DRAG_DETECT    = 1<<15;
+
+    /* Default size for widgets */
+    static final int DEFAULT_WIDTH  = 64;
+    static final int DEFAULT_HEIGHT = 64;
+
+    /* Check and initialize the Common Controls DLL */
+    static final int MAJOR = 5, MINOR = 80;
+    static {
+        if (!OS.IsWinCE) {
+            if (OS.COMCTL32_VERSION < OS.VERSION (MAJOR, MINOR)) {
+                System.out.println ("***WARNING: DWT requires comctl32.dll version " + MAJOR + "." + MINOR + " or greater"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                System.out.println ("***WARNING: Detected: " + OS.COMCTL32_MAJOR + "." + OS.COMCTL32_MINOR); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+        }
+        OS.InitCommonControls ();
+    }
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+Widget () {
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>DWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>DWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a widget which will be the parent of the new instance (cannot be null)
+ * @param style the style of widget to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see #checkSubclass
+ * @see #getStyle
+ */
+public Widget (Widget parent, int style) {
+    checkSubclass ();
+    checkParent (parent);
+    this.style = style;
+    display = parent.display;
+}
+
+void _addListener (int eventType, Listener listener) {
+    if (eventTable is null) eventTable = new EventTable ();
+    eventTable.hook (eventType, listener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when an event of the given type occurs. When the
+ * event does occur in the widget, the listener is notified by
+ * sending it the <code>handleEvent()</code> message. The event
+ * type is one of the event constants defined in class <code>DWT</code>.
+ *
+ * @param eventType the type of event to listen for
+ * @param listener the listener which should be notified when the event occurs
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Listener
+ * @see DWT
+ * @see #removeListener(int, Listener)
+ * @see #notifyListeners
+ */
+public void addListener (int eventType, Listener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    _addListener (eventType, listener);
+}
+
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the widget is disposed. When the widget is
+ * disposed, the listener is notified by sending it the
+ * <code>widgetDisposed()</code> message.
+ *
+ * @param listener the listener which should be notified when the receiver is disposed
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DisposeListener
+ * @see #removeDisposeListener
+ */
+public void addDisposeListener (DisposeListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    TypedListener typedListener = new TypedListener (listener);
+    addListener (DWT.Dispose, typedListener);
+}
+
+int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
+    return 0;
+}
+
+/**
+ * Returns a style with exactly one style bit set out of
+ * the specified set of exclusive style bits. All other
+ * possible bits are cleared when the first matching bit
+ * is found. Bits that are not part of the possible set
+ * are untouched.
+ *
+ * @param style the original style bits
+ * @param int0 the 0th possible style bit
+ * @param int1 the 1st possible style bit
+ * @param int2 the 2nd possible style bit
+ * @param int3 the 3rd possible style bit
+ * @param int4 the 4th possible style bit
+ * @param int5 the 5th possible style bit
+ *
+ * @return the new style bits
+ */
+static int checkBits (int style, int int0, int int1, int int2, int int3, int int4, int int5) {
+    int mask = int0 | int1 | int2 | int3 | int4 | int5;
+    if ((style & mask) is 0) style |= int0;
+    if ((style & int0) !is 0) style = (style & ~mask) | int0;
+    if ((style & int1) !is 0) style = (style & ~mask) | int1;
+    if ((style & int2) !is 0) style = (style & ~mask) | int2;
+    if ((style & int3) !is 0) style = (style & ~mask) | int3;
+    if ((style & int4) !is 0) style = (style & ~mask) | int4;
+    if ((style & int5) !is 0) style = (style & ~mask) | int5;
+    return style;
+}
+
+void checkOrientation (Widget parent) {
+    style &= ~DWT.MIRRORED;
+    if ((style & (DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT)) is 0) {
+        if (parent !is null) {
+            if ((parent.style & DWT.LEFT_TO_RIGHT) !is 0) style |= DWT.LEFT_TO_RIGHT;
+            if ((parent.style & DWT.RIGHT_TO_LEFT) !is 0) style |= DWT.RIGHT_TO_LEFT;
+        }
+    }
+    style = checkBits (style, DWT.LEFT_TO_RIGHT, DWT.RIGHT_TO_LEFT, 0, 0, 0, 0);
+}
+
+void checkOpened () {
+    /* Do nothing */
+}
+
+/**
+ * Throws an exception if the specified widget can not be
+ * used as a parent for the receiver.
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the parent is disposed</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * </ul>
+ */
+void checkParent (Widget parent) {
+    if (parent is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (parent.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
+    parent.checkWidget ();
+    parent.checkOpened ();
+}
+
+/**
+ * Checks that this class can be subclassed.
+ * <p>
+ * The DWT class library is intended to be subclassed
+ * only at specific, controlled points (most notably,
+ * <code>Composite</code> and <code>Canvas</code> when
+ * implementing new widgets). This method enforces this
+ * rule unless it is overridden.
+ * </p><p>
+ * <em>IMPORTANT:</em> By providing an implementation of this
+ * method that allows a subclass of a class which does not
+ * normally allow subclassing to be created, the implementer
+ * agrees to be fully responsible for the fact that any such
+ * subclass will likely fail between DWT releases and will be
+ * strongly platform specific. No support is provided for
+ * user-written classes which are implemented in this fashion.
+ * </p><p>
+ * The ability to subclass outside of the allowed 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.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ */
+protected void checkSubclass () {
+    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
+}
+
+/**
+ * Throws an <code>DWTException</code> if the receiver can not
+ * be accessed by the caller. This may include both checks on
+ * the state of the receiver and more generally on the entire
+ * execution context. This method <em>should</em> be called by
+ * widget implementors to enforce the standard DWT invariants.
+ * <p>
+ * Currently, it is an error to invoke any method (other than
+ * <code>isDisposed()</code>) on a widget that has had its
+ * <code>dispose()</code> method called. It is also an error
+ * to call widget methods from any thread that is different
+ * from the thread that created the widget.
+ * </p><p>
+ * In future releases of DWT, there may be more or fewer error
+ * checks and exceptions may be thrown for different reasons.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+protected void checkWidget () {
+    Display display = this.display;
+    if (display is null) error (DWT.ERROR_WIDGET_DISPOSED);
+    if (display.thread !is Thread.currentThread ()) error (DWT.ERROR_THREAD_INVALID_ACCESS);
+    if ((state & DISPOSED) !is 0) error (DWT.ERROR_WIDGET_DISPOSED);
+}
+
+/**
+ * Destroys the widget in the operating system and releases
+ * the widget's handle.  If the widget does not have a handle,
+ * this method may hide the widget, mark the widget as destroyed
+ * or do nothing, depending on the widget.
+ * <p>
+ * When a widget is destroyed in the operating system, its
+ * descendants are also destroyed by the operating system.
+ * This means that it is only necessary to call <code>destroyWidget</code>
+ * on the root of the widget tree.
+ * </p><p>
+ * This method is called after <code>releaseWidget()</code>.
+ * </p><p>
+ * See also <code>releaseChild()</code>, <code>releaseWidget()</code>
+ * and <code>releaseHandle()</code>.
+ * </p>
+ *
+ * @see #dispose
+ */
+void destroyWidget () {
+    releaseHandle ();
+}
+
+int DeferWindowPos(int hWinPosInfo, int hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags){
+    if (OS.IsWinCE) {
+        /*
+        * Feature in Windows.  On Windows CE, DeferWindowPos always causes
+        * a WM_SIZE message, even when the new size is the same as the old
+        * size.  The fix is to detect that the size has not changed and set
+        * SWP_NOSIZE.
+        */
+        if ((uFlags & OS.SWP_NOSIZE) is 0) {
+            RECT lpRect = new RECT ();
+            OS.GetWindowRect (hWnd, lpRect);
+            if (cy is lpRect.bottom - lpRect.top && cx is lpRect.right - lpRect.left) {
+                /*
+                * Feature in Windows.  On Windows CE, DeferWindowPos when called
+                * with SWP_DRAWFRAME always causes a WM_SIZE message, even
+                * when SWP_NOSIZE is set and when the new size is the same as the
+                * old size.  The fix is to clear SWP_DRAWFRAME when the size is
+                * the same.
+                */
+                uFlags &= ~OS.SWP_DRAWFRAME;
+                uFlags |= OS.SWP_NOSIZE;
+            }
+        }
+    }
+    return OS.DeferWindowPos (hWinPosInfo, hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
+}
+
+/**
+ * Disposes of the operating system resources associated with
+ * the receiver and all its descendants. After this method has
+ * been invoked, the receiver and all descendants will answer
+ * <code>true</code> when sent the message <code>isDisposed()</code>.
+ * Any internal connections between the widgets in the tree will
+ * have been removed to facilitate garbage collection.
+ * <p>
+ * NOTE: This method is not called recursively on the descendants
+ * of the receiver. This means that, widget implementers can not
+ * detect when a widget is being disposed of by re-implementing
+ * this method, but should instead listen for the <code>Dispose</code>
+ * event.
+ * </p>
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #addDisposeListener
+ * @see #removeDisposeListener
+ * @see #checkWidget
+ */
+public void dispose () {
+    /*
+    * Note:  It is valid to attempt to dispose a widget
+    * more than once.  If this happens, fail silently.
+    */
+    if (isDisposed ()) return;
+    if (!isValidThread ()) error (DWT.ERROR_THREAD_INVALID_ACCESS);
+    release (true);
+}
+
+bool dragDetect (int hwnd, int x, int y, bool filter, bool [] detect, bool [] consume) {
+    if (consume !is null) consume [0] = false;
+    if (detect !is null) detect [0] = true;
+    POINT pt = new POINT ();
+    pt.x = x;
+    pt.y = y;
+    OS.ClientToScreen (hwnd, pt);
+    return OS.DragDetect (hwnd, pt);
+}
+
+/**
+ * Does whatever widget specific cleanup is required, and then
+ * uses the code in <code>DWTError.error</code> to handle the error.
+ *
+ * @param code the descriptive error code
+ *
+ * @see DWT#error(int)
+ */
+void error (int code) {
+    DWT.error(code);
+}
+
+bool filters (int eventType) {
+    return display.filters (eventType);
+}
+
+Widget findItem (int id) {
+    return null;
+}
+
+char [] fixMnemonic (String string) {
+    char [] buffer = new char [string.length ()];
+    string.getChars (0, string.length (), buffer, 0);
+    int i = 0, j = 0;
+    while (i < buffer.length) {
+        if (buffer [i] is '&') {
+            if (i + 1 < buffer.length && buffer [i + 1] is '&') {
+                buffer [j++] = ' ';
+                i++;
+            }
+            i++;
+        } else {
+            buffer [j++] = buffer [i++];
+        }
+    }
+    while (j < buffer.length) buffer [j++] = 0;
+    return buffer;
+}
+
+/**
+ * Returns the application defined widget data associated
+ * with the receiver, or null if it has not been set. The
+ * <em>widget data</em> is a single, unnamed field that is
+ * stored with every widget.
+ * <p>
+ * Applications may put arbitrary objects in this field. If
+ * the object stored in the widget data needs to be notified
+ * when the widget is disposed of, it is the application's
+ * responsibility to hook the Dispose event on the widget and
+ * do so.
+ * </p>
+ *
+ * @return the widget data
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - when the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - when called from the wrong thread</li>
+ * </ul>
+ *
+ * @see #setData(Object)
+ */
+public Object getData () {
+    checkWidget();
+    return (state & KEYED_DATA) !is 0 ? ((Object []) data) [0] : data;
+}
+
+/**
+ * Returns the application defined property of the receiver
+ * with the specified name, or null if it has not been set.
+ * <p>
+ * Applications may have associated arbitrary objects with the
+ * receiver in this fashion. If the objects stored in the
+ * properties need to be notified when the widget is disposed
+ * of, it is the application's responsibility to hook the
+ * Dispose event on the widget and do so.
+ * </p>
+ *
+ * @param   key the name of the property
+ * @return the value of the property or null if it has not been set
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the key is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setData(String, Object)
+ */
+public Object getData (String key) {
+    checkWidget();
+    if (key is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if ((state & KEYED_DATA) !is 0) {
+        Object [] table = (Object []) data;
+        for (int i=1; i<table.length; i+=2) {
+            if (key.equals (table [i])) return table [i+1];
+        }
+    }
+    return null;
+}
+
+/**
+ * Returns the <code>Display</code> that is associated with
+ * the receiver.
+ * <p>
+ * A widget's display is either provided when it is created
+ * (for example, top level <code>Shell</code>s) or is the
+ * same as its parent's display.
+ * </p>
+ *
+ * @return the receiver's display
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * </ul>
+ */
+public Display getDisplay () {
+    Display display = this.display;
+    if (display is null) error (DWT.ERROR_WIDGET_DISPOSED);
+    return display;
+}
+
+Menu getMenu () {
+    return null;
+}
+
+/**
+ * Returns the name of the widget. This is the name of
+ * the class without the package name.
+ *
+ * @return the name of the widget
+ */
+String getName () {
+    String string = getClass ().getName ();
+    int index = string.lastIndexOf ('.');
+    if (index is -1) return string;
+    return string.substring (index + 1, string.length ());
+}
+
+/*
+ * Returns a short printable representation for the contents
+ * of a widget. For example, a button may answer the label
+ * text. This is used by <code>toString</code> to provide a
+ * more meaningful description of the widget.
+ *
+ * @return the contents string for the widget
+ *
+ * @see #toString
+ */
+String getNameText () {
+    return ""; //$NON-NLS-1$
+}
+
+/**
+ * Returns the receiver's style information.
+ * <p>
+ * Note that the value which is returned by this method <em>may
+ * not match</em> the value which was provided to the constructor
+ * when the receiver was created. This can occur when the underlying
+ * operating system does not support a particular combination of
+ * requested styles. For example, if the platform widget used to
+ * implement a particular DWT widget always has scroll bars, the
+ * result of calling this method would always have the
+ * <code>DWT.H_SCROLL</code> and <code>DWT.V_SCROLL</code> bits set.
+ * </p>
+ *
+ * @return the style bits
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getStyle () {
+    checkWidget();
+    return style;
+}
+
+/*
+ * Returns <code>true</code> if the specified eventType is
+ * hooked, and <code>false</code> otherwise. Implementations
+ * of DWT can avoid creating objects and sending events
+ * when an event happens in the operating system but
+ * there are no listeners hooked for the event.
+ *
+ * @param eventType the event to be checked
+ *
+ * @return <code>true</code> when the eventType is hooked and <code>false</code> otherwise
+ *
+ * @see #isListening
+ */
+bool hooks (int eventType) {
+    if (eventTable is null) return false;
+    return eventTable.hooks (eventType);
+}
+
+/**
+ * Returns <code>true</code> if the widget has been disposed,
+ * and <code>false</code> otherwise.
+ * <p>
+ * This method gets the dispose state for the widget.
+ * When a widget has been disposed, it is an error to
+ * invoke any other method using the widget.
+ * </p>
+ *
+ * @return <code>true</code> when the widget is disposed and <code>false</code> otherwise
+ */
+public bool isDisposed () {
+    return (state & DISPOSED) !is 0;
+}
+
+/**
+ * Returns <code>true</code> if there are any listeners
+ * for the specified event type associated with the receiver,
+ * and <code>false</code> otherwise. The event type is one of
+ * the event constants defined in class <code>DWT</code>.
+ *
+ * @param eventType the type of event
+ * @return true if the event is hooked
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT
+ */
+public bool isListening (int eventType) {
+    checkWidget();
+    return hooks (eventType);
+}
+
+/*
+ * Returns <code>true</code> when subclassing is
+ * allowed and <code>false</code> otherwise
+ *
+ * @return <code>true</code> when subclassing is allowed and <code>false</code> otherwise
+ */
+bool isValidSubclass () {
+    return Display.isValidClass (getClass ());
+}
+
+/*
+ * Returns <code>true</code> when the current thread is
+ * the thread that created the widget and <code>false</code>
+ * otherwise.
+ *
+ * @return <code>true</code> when the current thread is the thread that created the widget and <code>false</code> otherwise
+ */
+bool isValidThread () {
+    return getDisplay ().isValidThread ();
+}
+
+void mapEvent (int hwnd, Event event) {
+}
+
+GC new_GC (GCData data) {
+    return null;
+}
+
+/**
+ * Notifies all of the receiver's listeners for events
+ * of the given type that one such event has occurred by
+ * invoking their <code>handleEvent()</code> method.  The
+ * event type is one of the event constants defined in class
+ * <code>DWT</code>.
+ *
+ * @param eventType the type of event which has occurred
+ * @param event the event data
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DWT
+ * @see #addListener
+ * @see #removeListener(int, Listener)
+ */
+public void notifyListeners (int eventType, Event event) {
+    checkWidget();
+    if (event is null) event = new Event ();
+    sendEvent (eventType, event);
+}
+
+void postEvent (int eventType) {
+    sendEvent (eventType, null, false);
+}
+
+void postEvent (int eventType, Event event) {
+    sendEvent (eventType, event, false);
+}
+
+/*
+ * Releases the widget hierarchy and optionally destroys
+ * the receiver.
+ * <p>
+ * Typically, a widget with children will broadcast this
+ * message to all children so that they too can release their
+ * resources.  The <code>releaseHandle</code> method is used
+ * as part of this broadcast to zero the handle fields of the
+ * children without calling <code>destroyWidget</code>.  In
+ * this scenario, the children are actually destroyed later,
+ * when the operating system destroys the widget tree.
+ * </p>
+ *
+ * @param destroy indicates that the receiver should be destroyed
+ *
+ * @see #dispose
+ * @see #releaseHandle
+ * @see #releaseParent
+ * @see #releaseWidget
+*/
+void release (bool destroy) {
+    if ((state & DISPOSE_SENT) is 0) {
+        state |= DISPOSE_SENT;
+        sendEvent (DWT.Dispose);
+    }
+    if ((state & DISPOSED) is 0) {
+        releaseChildren (destroy);
+    }
+    if ((state & RELEASED) is 0) {
+        state |= RELEASED;
+        if (destroy) {
+            releaseParent ();
+            releaseWidget ();
+            destroyWidget ();
+        } else {
+            releaseWidget ();
+            releaseHandle ();
+        }
+    }
+}
+
+void releaseChildren (bool destroy) {
+}
+
+/*
+ * Releases the widget's handle by zero'ing it out.
+ * Does not destroy or release any operating system
+ * resources.
+ * <p>
+ * This method is called after <code>releaseWidget</code>
+ * or from <code>destroyWidget</code> when a widget is being
+ * destroyed to ensure that the widget is marked as destroyed
+ * in case the act of destroying the widget in the operating
+ * system causes application code to run in callback that
+ * could access the widget.
+ * </p>
+ *
+ * @see #dispose
+ * @see #releaseChildren
+ * @see #releaseParent
+ * @see #releaseWidget
+ */
+void releaseHandle () {
+    state |= DISPOSED;
+    display = null;
+}
+
+/*
+ * Releases the receiver, a child in a widget hierarchy,
+ * from its parent.
+ * <p>
+ * When a widget is destroyed, it may be necessary to remove
+ * it from an internal data structure of the parent. When
+ * a widget has no handle, it may also be necessary for the
+ * parent to hide the widget or otherwise indicate that the
+ * widget has been disposed. For example, disposing a menu
+ * bar requires that the menu bar first be released from the
+ * shell when the menu bar is active.
+ * </p>
+ *
+ * @see #dispose
+ * @see #releaseChildren
+ * @see #releaseWidget
+ * @see #releaseHandle
+ */
+void releaseParent () {
+}
+
+/*
+ * Releases any internal resources back to the operating
+ * system and clears all fields except the widget handle.
+ * <p>
+ * When a widget is destroyed, resources that were acquired
+ * on behalf of the programmer need to be returned to the
+ * operating system.  For example, if the widget made a
+ * copy of an icon, supplied by the programmer, this copy
+ * would be freed in <code>releaseWidget</code>.  Also,
+ * to assist the garbage collector and minimize the amount
+ * of memory that is not reclaimed when the programmer keeps
+ * a reference to a disposed widget, all fields except the
+ * handle are zero'd.  The handle is needed by <code>destroyWidget</code>.
+ * </p>
+ *
+ * @see #dispose
+ * @see #releaseChildren
+ * @see #releaseHandle
+ * @see #releaseParent
+ */
+void releaseWidget () {
+    eventTable = null;
+    data = null;
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when an event of the given type occurs. The event
+ * type is one of the event constants defined in class <code>DWT</code>.
+ *
+ * @param eventType the type of event to listen for
+ * @param listener the listener which should no longer be notified when the event occurs
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Listener
+ * @see DWT
+ * @see #addListener
+ * @see #notifyListeners
+ */
+public void removeListener (int eventType, Listener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (eventType, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when an event of the given type occurs.
+ * <p>
+ * <b>IMPORTANT:</b> This method 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 should never be
+ * referenced from application code.
+ * </p>
+ *
+ * @param eventType the type of event to listen for
+ * @param listener the listener which should no longer be notified when the event occurs
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Listener
+ * @see #addListener
+ */
+protected void removeListener (int eventType, DWTEventListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (eventType, listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the widget is disposed.
+ *
+ * @param listener the listener which should no longer be notified when the receiver is disposed
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see DisposeListener
+ * @see #addDisposeListener
+ */
+public void removeDisposeListener (DisposeListener listener) {
+    checkWidget();
+    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
+    if (eventTable is null) return;
+    eventTable.unhook (DWT.Dispose, listener);
+}
+
+bool sendDragEvent (int button, int x, int y) {
+    Event event = new Event ();
+    event.button = button;
+    event.x = x;
+    event.y = y;
+    setInputState (event, DWT.DragDetect);
+    postEvent (DWT.DragDetect, event);
+    if (isDisposed ()) return false;
+    return event.doit;
+}
+
+bool sendDragEvent (int button, int stateMask, int x, int y) {
+    Event event = new Event ();
+    event.button = button;
+    event.x = x;
+    event.y = y;
+    event.stateMask = stateMask;
+    postEvent (DWT.DragDetect, event);
+    if (isDisposed ()) return false;
+    return event.doit;
+}
+
+void sendEvent (Event event) {
+    Display display = event.display;
+    if (!display.filterEvent (event)) {
+        if (eventTable !is null) eventTable.sendEvent (event);
+    }
+}
+
+void sendEvent (int eventType) {
+    sendEvent (eventType, null, true);
+}
+
+void sendEvent (int eventType, Event event) {
+    sendEvent (eventType, event, true);
+}
+
+void sendEvent (int eventType, Event event, bool send) {
+    if (eventTable is null && !display.filters (eventType)) {
+        return;
+    }
+    if (event is null) event = new Event ();
+    event.type = eventType;
+    event.display = display;
+    event.widget = this;
+    if (event.time is 0) {
+        event.time = display.getLastEventTime ();
+    }
+    if (send) {
+        sendEvent (event);
+    } else {
+        display.postEvent (event);
+    }
+}
+
+bool sendKeyEvent (int type, int msg, int wParam, int lParam) {
+    Event event = new Event ();
+    if (!setKeyState (event, type, wParam, lParam)) return true;
+    return sendKeyEvent (type, msg, wParam, lParam, event);
+}
+
+bool sendKeyEvent (int type, int msg, int wParam, int lParam, Event event) {
+    sendEvent (type, event);
+    if (isDisposed ()) return false;
+    return event.doit;
+}
+
+bool sendMouseEvent (int type, int button, int hwnd, int msg, int wParam, int lParam) {
+    return sendMouseEvent (type, button, display.getClickCount (type, button, hwnd, lParam), 0, false, hwnd, msg, wParam, lParam);
+}
+
+bool sendMouseEvent (int type, int button, int count, int detail, bool send, int hwnd, int msg, int wParam, int lParam) {
+    if (!hooks (type) && !filters (type)) return true;
+    Event event = new Event ();
+    event.button = button;
+    event.detail = detail;
+    event.count = count;
+    event.x = (short) (lParam & 0xFFFF);
+    event.y = (short) (lParam >> 16);
+    setInputState (event, type);
+    mapEvent (hwnd, event);
+    if (send) {
+        sendEvent (type, event);
+        if (isDisposed ()) return false;
+    } else {
+        postEvent (type, event);
+    }
+    return event.doit;
+}
+
+/**
+ * Sets the application defined widget data associated
+ * with the receiver to be the argument. The <em>widget
+ * data</em> is a single, unnamed field that is stored
+ * with every widget.
+ * <p>
+ * Applications may put arbitrary objects in this field. If
+ * the object stored in the widget data needs to be notified
+ * when the widget is disposed of, it is the application's
+ * responsibility to hook the Dispose event on the widget and
+ * do so.
+ * </p>
+ *
+ * @param data the widget data
+ *
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - when the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - when called from the wrong thread</li>
+ * </ul>
+ *
+ * @see #getData()
+ */
+public void setData (Object data) {
+    checkWidget();
+    if ((state & KEYED_DATA) !is 0) {
+        ((Object []) this.data) [0] = data;
+    } else {
+        this.data = data;
+    }
+}
+
+/**
+ * Sets the application defined property of the receiver
+ * with the specified name to the given value.
+ * <p>
+ * Applications may associate arbitrary objects with the
+ * receiver in this fashion. If the objects stored in the
+ * properties need to be notified when the widget is disposed
+ * of, it is the application's responsibility to hook the
+ * Dispose event on the widget and do so.
+ * </p>
+ *
+ * @param key the name of the property
+ * @param value the new value for the property
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the key is null</li>
+ * </ul>
+ * @exception DWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #getData(String)
+ */
+public void setData (String key, Object value) {
+    checkWidget();
+    if (key is null) error (DWT.ERROR_NULL_ARGUMENT);
+    int index = 1;
+    Object [] table = null;
+    if ((state & KEYED_DATA) !is 0) {
+        table = (Object []) data;
+        while (index < table.length) {
+            if (key.equals (table [index])) break;
+            index += 2;
+        }
+    }
+    if (value !is null) {
+        if ((state & KEYED_DATA) !is 0) {
+            if (index is table.length) {
+                Object [] newTable = new Object [table.length + 2];
+                System.arraycopy (table, 0, newTable, 0, table.length);
+                data = table = newTable;
+            }
+        } else {
+            table = new Object [3];
+            table [0] = data;
+            data = table;
+            state |= KEYED_DATA;
+        }
+        table [index] = key;
+        table [index + 1] = value;
+    } else {
+        if ((state & KEYED_DATA) !is 0) {
+            if (index !is table.length) {
+                int length = table.length - 2;
+                if (length is 1) {
+                    data = table [0];
+                    state &= ~KEYED_DATA;
+                } else {
+                    Object [] newTable = new Object [length];
+                    System.arraycopy (table, 0, newTable, 0, index);
+                    System.arraycopy (table, index + 2, newTable, index, length - index);
+                    data = newTable;
+                }
+            }
+        }
+    }
+}
+
+bool sendFocusEvent (int type) {
+    sendEvent (type);
+    // widget could be disposed at this point
+    return true;
+}
+
+bool setInputState (Event event, int type) {
+    if (OS.GetKeyState (OS.VK_MENU) < 0) event.stateMask |= DWT.ALT;
+    if (OS.GetKeyState (OS.VK_SHIFT) < 0) event.stateMask |= DWT.SHIFT;
+    if (OS.GetKeyState (OS.VK_CONTROL) < 0) event.stateMask |= DWT.CONTROL;
+    if (OS.GetKeyState (OS.VK_LBUTTON) < 0) event.stateMask |= DWT.BUTTON1;
+    if (OS.GetKeyState (OS.VK_MBUTTON) < 0) event.stateMask |= DWT.BUTTON2;
+    if (OS.GetKeyState (OS.VK_RBUTTON) < 0) event.stateMask |= DWT.BUTTON3;
+    if (OS.GetKeyState (OS.VK_XBUTTON1) < 0) event.stateMask |= DWT.BUTTON4;
+    if (OS.GetKeyState (OS.VK_XBUTTON2) < 0) event.stateMask |= DWT.BUTTON5;
+    switch (type) {
+        case DWT.MouseDown:
+        case DWT.MouseDoubleClick:
+            if (event.button is 1) event.stateMask &= ~DWT.BUTTON1;
+            if (event.button is 2) event.stateMask &= ~DWT.BUTTON2;
+            if (event.button is 3) event.stateMask &= ~DWT.BUTTON3;
+            if (event.button is 4) event.stateMask &= ~DWT.BUTTON4;
+            if (event.button is 5) event.stateMask &= ~DWT.BUTTON5;
+            break;
+        case DWT.MouseUp:
+            if (event.button is 1) event.stateMask |= DWT.BUTTON1;
+            if (event.button is 2) event.stateMask |= DWT.BUTTON2;
+            if (event.button is 3) event.stateMask |= DWT.BUTTON3;
+            if (event.button is 4) event.stateMask |= DWT.BUTTON4;
+            if (event.button is 5) event.stateMask |= DWT.BUTTON5;
+            break;
+        case DWT.KeyDown:
+        case DWT.Traverse:
+            if (event.keyCode is DWT.ALT) event.stateMask &= ~DWT.ALT;
+            if (event.keyCode is DWT.SHIFT) event.stateMask &= ~DWT.SHIFT;
+            if (event.keyCode is DWT.CONTROL) event.stateMask &= ~DWT.CONTROL;
+            break;
+        case DWT.KeyUp:
+            if (event.keyCode is DWT.ALT) event.stateMask |= DWT.ALT;
+            if (event.keyCode is DWT.SHIFT) event.stateMask |= DWT.SHIFT;
+            if (event.keyCode is DWT.CONTROL) event.stateMask |= DWT.CONTROL;
+            break;
+    }
+    return true;
+}
+
+bool setKeyState (Event event, int type, int wParam, int lParam) {
+
+    /*
+    * Feature in Windows.  When the user presses Ctrl+Backspace
+    * or Ctrl+Enter, Windows sends a WM_CHAR with Delete (0x7F)
+    * and '\n' instead of '\b' and '\r'.  This is the correct
+    * platform behavior but is not portable.  The fix is to detect
+    * these cases and convert the character.
+    */
+    switch (display.lastAscii) {
+        case DWT.DEL:
+            if (display.lastKey is DWT.BS) display.lastAscii = DWT.BS;
+            break;
+        case DWT.LF:
+            if (display.lastKey is DWT.CR) display.lastAscii = DWT.CR;
+            break;
+    }
+
+    /*
+    * Feature in Windows.  When the user presses either the Enter
+    * key or the numeric keypad Enter key, Windows sends a WM_KEYDOWN
+    * with wParam=VK_RETURN in both cases.  In order to distinguish
+    * between the keys, the extended key bit is tested. If the bit
+    * is set, assume that the numeric keypad Enter was pressed.
+    */
+    if (display.lastKey is DWT.CR && display.lastAscii is DWT.CR) {
+        if ((lParam & 0x1000000) !is 0) display.lastKey = DWT.KEYPAD_CR;
+    }
+
+    if (display.lastVirtual) {
+        /*
+        * Feature in Windows.  The virtual key VK_DELETE is not
+        * treated as both a virtual key and an ASCII key by Windows.
+        * Therefore, we will not receive a WM_CHAR for this key.
+        * The fix is to treat VK_DELETE as a special case and map
+        * the ASCII value explicitly (Delete is 0x7F).
+        */
+        if (display.lastKey is OS.VK_DELETE) display.lastAscii = 0x7F;
+
+        /*
+        * Feature in Windows.  When the user presses Ctrl+Pause, the
+        * VK_CANCEL key is generated and a WM_CHAR is sent with 0x03,
+        * possibly to allow an application to look for Ctrl+C and the
+        * the Break key at the same time.  This is unexpected and
+        * unwanted.  The fix is to detect the case and set the character
+        * to zero.
+        */
+        if (display.lastKey is OS.VK_CANCEL) display.lastAscii = 0x0;
+
+        event.keyCode = Display.translateKey (display.lastKey);
+    } else {
+        event.keyCode = display.lastKey;
+    }
+    if (display.lastAscii !is 0 || display.lastNull) {
+        event.character = Display.mbcsToWcs ((char) display.lastAscii);
+    }
+    if (event.keyCode is 0 && event.character is 0) {
+        if (!display.lastNull) return false;
+    }
+    return setInputState (event, type);
+}
+
+bool SetWindowPos (int hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags) {
+    if (OS.IsWinCE) {
+        /*
+        * Feature in Windows.  On Windows CE, SetWindowPos() always causes
+        * a WM_SIZE message, even when the new size is the same as the old
+        * size.  The fix is to detect that the size has not changed and set
+        * SWP_NOSIZE.
+        */
+        if ((uFlags & OS.SWP_NOSIZE) is 0) {
+            RECT lpRect = new RECT ();
+            OS.GetWindowRect (hWnd, lpRect);
+            if (cy is lpRect.bottom - lpRect.top && cx is lpRect.right - lpRect.left) {
+                /*
+                * Feature in Windows.  On Windows CE, SetWindowPos() when called
+                * with SWP_DRAWFRAME always causes a WM_SIZE message, even
+                * when SWP_NOSIZE is set and when the new size is the same as the
+                * old size.  The fix is to clear SWP_DRAWFRAME when the size is
+                * the same.
+                */
+                uFlags &= ~OS.SWP_DRAWFRAME;
+                uFlags |= OS.SWP_NOSIZE;
+            }
+        }
+    }
+    return OS.SetWindowPos (hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
+}
+
+bool showMenu (int x, int y) {
+    Event event = new Event ();
+    event.x = x;
+    event.y = y;
+    sendEvent (DWT.MenuDetect, event);
+    if (!event.doit) return true;
+    Menu menu = getMenu ();
+    if (menu !is null && !menu.isDisposed ()) {
+        if (x !is event.x || y !is event.y) {
+            menu.setLocation (event.x, event.y);
+        }
+        menu.setVisible (true);
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the receiver
+ */
+public String toString () {
+    String string = "*Disposed*"; //$NON-NLS-1$
+    if (!isDisposed ()) {
+        string = "*Wrong Thread*"; //$NON-NLS-1$
+        if (isValidThread ()) string = getNameText ();
+    }
+    return getName () + " {" + string + "}"; //$NON-NLS-1$ //$NON-NLS-2$
+}
+
+LRESULT wmCaptureChanged (int hwnd, int wParam, int lParam) {
+    display.captureChanged = true;
+    return null;
+}
+
+LRESULT wmChar (int hwnd, int wParam, int lParam) {
+    /*
+    * Do not report a lead byte as a key pressed.
+    */
+    if (!OS.IsUnicode && OS.IsDBLocale) {
+        byte lead = (byte) (wParam & 0xFF);
+        if (OS.IsDBCSLeadByte (lead)) return null;
+    }
+    display.lastAscii = wParam;
+    display.lastNull = wParam is 0;
+    if (!sendKeyEvent (DWT.KeyDown, OS.WM_CHAR, wParam, lParam)) {
+        return LRESULT.ONE;
+    }
+    // widget could be disposed at this point
+    return null;
+}
+
+LRESULT wmContextMenu (int hwnd, int wParam, int lParam) {
+    if (wParam !is hwnd) return null;
+
+    /*
+    * Feature in Windows.  SHRecognizeGesture() sends an undocumented
+    * WM_CONTEXTMENU notification when the flag SHRG_NOTIFY_PARENT is
+    * not set.  This causes the context menu to be displayed twice,
+    * once by the caller of SHRecognizeGesture() and once from this
+    * method.  The fix is to ignore WM_CONTEXTMENU notifications on
+    * all WinCE platforms.
+    *
+    * NOTE: This only happens on WM2003.  Previous WinCE versions did
+    * not support WM_CONTEXTMENU.
+    */
+    if (OS.IsWinCE) return null;
+
+    /*
+    * Feature in Windows.  When the user presses  WM_NCRBUTTONUP,
+    * a WM_CONTEXTMENU message is generated.  This happens when
+    * the user releases the mouse over a scroll bar.  Normally,
+    * window displays the default scrolling menu but applications
+    * can process WM_CONTEXTMENU to display a different menu.
+    * Typically, an application does not want to supply a special
+    * scroll menu.  The fix is to look for a WM_CONTEXTMENU that
+    * originated from a mouse event and display the menu when the
+    * mouse was released in the client area.
+    */
+    int x = 0, y = 0;
+    if (lParam !is -1) {
+        POINT pt = new POINT ();
+        x = pt.x = (short) (lParam & 0xFFFF);
+        y = pt.y = (short) (lParam >> 16);
+        OS.ScreenToClient (hwnd, pt);
+        RECT rect = new RECT ();
+        OS.GetClientRect (hwnd, rect);
+        if (!OS.PtInRect (rect, pt)) return null;
+    } else {
+        int pos = OS.GetMessagePos ();
+        x = (short) (pos & 0xFFFF);
+        y = (short) (pos >> 16);
+    }
+
+    /* Show the menu */
+    return showMenu (x, y) ? LRESULT.ZERO : null;
+}
+
+LRESULT wmIMEChar (int hwnd, int wParam, int lParam) {
+    Display display = this.display;
+    display.lastKey = 0;
+    display.lastAscii = wParam;
+    display.lastVirtual = display.lastNull = display.lastDead = false;
+    if (!sendKeyEvent (DWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) {
+        return LRESULT.ONE;
+    }
+    sendKeyEvent (DWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam);
+    // widget could be disposed at this point
+    display.lastKey = display.lastAscii = 0;
+    return LRESULT.ONE;
+}
+
+LRESULT wmKeyDown (int hwnd, int wParam, int lParam) {
+
+    /* Ignore repeating modifier keys by testing key down state */
+    switch (wParam) {
+        case OS.VK_SHIFT:
+        case OS.VK_MENU:
+        case OS.VK_CONTROL:
+        case OS.VK_CAPITAL:
+        case OS.VK_NUMLOCK:
+        case OS.VK_SCROLL:
+            if ((lParam & 0x40000000) !is 0) return null;
+    }
+
+    /* Clear last key and last ascii because a new key has been typed */
+    display.lastAscii = display.lastKey = 0;
+    display.lastVirtual = display.lastNull = display.lastDead = false;
+
+    /*
+    * Do not report a lead byte as a key pressed.
+    */
+    if (!OS.IsUnicode && OS.IsDBLocale) {
+        byte lead = (byte) (wParam & 0xFF);
+        if (OS.IsDBCSLeadByte (lead)) return null;
+    }
+
+    /* Map the virtual key */
+    /*
+    * Bug in WinCE.  MapVirtualKey() returns incorrect values.
+    * The fix is to rely on a key mappings table to determine
+    * whether the key event must be sent now or if a WM_CHAR
+    * event will follow.  The key mappings table maps virtual
+    * keys to DWT key codes and does not contain mappings for
+    * Windows virtual keys like VK_A.  Virtual keys that are
+    * both virtual and ASCII are a special case.
+    */
+    int mapKey = 0;
+    if (OS.IsWinCE) {
+        switch (wParam) {
+            case OS.VK_BACK: mapKey = DWT.BS; break;
+            case OS.VK_RETURN: mapKey = DWT.CR; break;
+            case OS.VK_DELETE: mapKey = DWT.DEL; break;
+            case OS.VK_ESCAPE: mapKey = DWT.ESC; break;
+            case OS.VK_TAB: mapKey = DWT.TAB; break;
+        }
+    } else {
+        mapKey = OS.MapVirtualKey (wParam, 2);
+    }
+
+    /*
+    * Bug in Windows 95 and NT.  When the user types an accent key such
+    * as ^ to get an accented character on a German keyboard, the accent
+    * key should be ignored and the next key that the user types is the
+    * accented key.  The fix is to detect the accent key stroke (called
+    * a dead key) by testing the high bit of the value returned by
+    * MapVirtualKey().  A further problem is that the high bit on
+    * Windows NT is bit 32 while the high bit on Windows 95 is bit 16.
+    * They should both be bit 32.
+    *
+    * When the user types an accent key that does not correspond to a
+    * virtual key, MapVirtualKey() won't set the high bit to indicate
+    * a dead key.  This happens when an accent key, such as '^' is the
+    * result of a modifier such as Shift key and MapVirtualKey() always
+    * returns the unshifted key.  The fix is to peek for a WM_DEADCHAR
+    * and avoid issuing the event.
+    */
+    if (OS.IsWinNT) {
+        if ((mapKey & 0x80000000) !is 0) return null;
+    } else {
+        if ((mapKey & 0x8000) !is 0) return null;
+    }
+    MSG msg = new MSG ();
+    int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE;
+    if (OS.PeekMessage (msg, hwnd, OS.WM_DEADCHAR, OS.WM_DEADCHAR, flags)) {
+        display.lastDead = true;
+        display.lastVirtual = mapKey is 0;
+        display.lastKey = display.lastVirtual ? wParam : mapKey;
+        return null;
+    }
+
+    /*
+    *  Bug in Windows.  Somehow, the widget is becoming disposed after
+    *  calling PeekMessage().  In rare circumstances, it seems that
+    *  PeekMessage() can allow DWT listeners to run that might contain
+    *  application code that disposes the widget.  It is not exactly
+    *  clear how this can happen.  PeekMessage() is only looking for
+    *  WM_DEADCHAR.  It is not dispatching any message that it finds
+    *  or removing any message from the queue.  Cross-thread messages
+    *  are disabled.  The fix is to check for a disposed widget and
+    *  return without calling the window proc.
+    */
+    if (isDisposed ()) return LRESULT.ONE;
+
+    /*
+    * If we are going to get a WM_CHAR, ensure that last key has
+    * the correct character value for the key down and key up
+    * events.  It is not sufficient to ignore the WM_KEYDOWN
+    * (when we know we are going to get a WM_CHAR) and compute
+    * the key in WM_CHAR because there is not enough information
+    * by the time we get the WM_CHAR.  For example, when the user
+    * types Ctrl+Shift+6 on a US keyboard, we get a WM_CHAR with
+    * wParam=30.  When the user types Ctrl+Shift+6 on a German
+    * keyboard, we also get a WM_CHAR with wParam=30.  On the US
+    * keyboard Shift+6 is ^, on the German keyboard Shift+6 is &.
+    * There is no way to map wParam=30 in WM_CHAR to the correct
+    * value.  Also, on international keyboards, the control key
+    * may be down when the user has not entered a control character.
+    *
+    * NOTE: On Windows 98, keypad keys are virtual despite the
+    * fact that a WM_CHAR is issued.  On Windows 2000 and XP,
+    * they are not virtual.  Therefore it is necessary to force
+    * numeric keypad keys to be virtual.
+    */
+    display.lastVirtual = mapKey is 0 || display.numpadKey (wParam) !is 0;
+    if (display.lastVirtual) {
+        display.lastKey = wParam;
+        /*
+        * Feature in Windows.  The virtual key VK_DELETE is not
+        * treated as both a virtual key and an ASCII key by Windows.
+        * Therefore, we will not receive a WM_CHAR for this key.
+        * The fix is to treat VK_DELETE as a special case and map
+        * the ASCII value explicitly (Delete is 0x7F).
+        */
+        if (display.lastKey is OS.VK_DELETE) display.lastAscii = 0x7F;
+
+        /*
+        * It is possible to get a WM_CHAR for a virtual key when
+        * Num Lock is on.  If the user types Home while Num Lock
+        * is down, a WM_CHAR is issued with WPARM=55 (for the
+        * character 7).  If we are going to get a WM_CHAR we need
+        * to ensure that the last key has the correct value.  Note
+        * that Ctrl+Home does not issue a WM_CHAR when Num Lock is
+        * down.
+        */
+        if (OS.VK_NUMPAD0 <= display.lastKey && display.lastKey <= OS.VK_DIVIDE) {
+            /*
+            * Feature in Windows.  Calling to ToAscii() or ToUnicode(), clears
+            * the accented state such that the next WM_CHAR loses the accent.
+            * This makes is critical that the accent key is detected.  Also,
+            * these functions clear the character that is entered using the
+            * special Windows keypad sequence when NumLock is down (ie. typing
+            * ALT+0231 should gives 'c' with a cedilla when NumLock is down).
+            */
+            if (display.asciiKey (display.lastKey) !is 0) return null;
+            display.lastAscii = display.numpadKey (display.lastKey);
+        }
+    } else {
+        /*
+        * Convert LastKey to lower case because Windows non-virtual
+        * keys that are also ASCII keys, such as like VK_A, are have
+        * upper case values in WM_KEYDOWN despite the fact that the
+        * Shift was not pressed.
+        */
+        display.lastKey = OS.CharLower ((short) mapKey);
+
+        /*
+        * Feature in Windows. The virtual key VK_CANCEL is treated
+        * as both a virtual key and ASCII key by Windows.  This
+        * means that a WM_CHAR with WPARAM=3 will be issued for
+        * this key.  In order to distinguish between this key and
+        * Ctrl+C, mark the key as virtual.
+        */
+        if (wParam is OS.VK_CANCEL) display.lastVirtual = true;
+
+        /*
+        * Some key combinations map to Windows ASCII keys depending
+        * on the keyboard.  For example, Ctrl+Alt+Q maps to @ on a
+        * German keyboard.  If the current key combination is special,
+        * the correct character is placed in wParam for processing in
+        * WM_CHAR.  If this is the case, issue the key down event from
+        * inside WM_CHAR.
+        */
+        int asciiKey = display.asciiKey (wParam);
+        if (asciiKey !is 0) {
+            /*
+            * When the user types Ctrl+Space, ToAscii () maps this to
+            * Space.  Normally, ToAscii () maps a key to a different
+            * key if both a WM_KEYDOWN and a WM_CHAR will be issued.
+            * To avoid the extra DWT.KeyDown, look for a space and
+            * issue the event from WM_CHAR.
+            */
+            if (asciiKey is ' ') return null;
+            if (asciiKey !is wParam) return null;
+            /*
+            * Feature in Windows. The virtual key VK_CANCEL is treated
+            * as both a virtual key and ASCII key by Windows.  This
+            * means that a WM_CHAR with WPARAM=3 will be issued for
+            * this key. To avoid the extra DWT.KeyDown, look for
+            * VK_CANCEL and issue the event from WM_CHAR.
+            */
+            if (wParam is OS.VK_CANCEL) return null;
+        }
+
+        /*
+        * If the control key is not down at this point, then
+        * the key that was pressed was an accent key or a regular
+        * key such as 'A' or Shift+A.  In that case, issue the
+        * key event from WM_CHAR.
+        */
+        if (OS.GetKeyState (OS.VK_CONTROL) >= 0) return null;
+
+        /*
+        * Get the shifted state or convert to lower case if necessary.
+        * If the user types Ctrl+A, LastAscii should be 'a', not 'A'.
+        * If the user types Ctrl+Shift+A, LastAscii should be 'A'.
+        * If the user types Ctrl+Shift+6, the value of LastAscii will
+        * depend on the international keyboard.
+        */
+        if (OS.GetKeyState (OS.VK_SHIFT) < 0) {
+            display.lastAscii = display.shiftedKey (wParam);
+            if (display.lastAscii is 0) display.lastAscii = mapKey;
+        } else {
+            display.lastAscii = OS.CharLower ((short) mapKey);
+        }
+
+        /* Note that Ctrl+'@' is ASCII NUL and is delivered in WM_CHAR */
+        if (display.lastAscii is '@') return null;
+        display.lastAscii = display.controlKey (display.lastAscii);
+    }
+    if (!sendKeyEvent (DWT.KeyDown, OS.WM_KEYDOWN, wParam, lParam)) {
+        return LRESULT.ONE;
+    }
+    // widget could be disposed at this point
+    return null;
+}
+
+LRESULT wmKeyUp (int hwnd, int wParam, int lParam) {
+    Display display = this.display;
+
+    /* Check for hardware keys */
+    if (OS.IsWinCE) {
+        if (OS.VK_APP1 <= wParam && wParam <= OS.VK_APP6) {
+            display.lastKey = display.lastAscii = 0;
+            display.lastVirtual = display.lastNull = display.lastDead = false;
+            Event event = new Event ();
+            event.detail = wParam - OS.VK_APP1 + 1;
+            /* Check the bit 30 to get the key state */
+            int type = (lParam & 0x40000000) !is 0 ? DWT.HardKeyUp : DWT.HardKeyDown;
+            if (setInputState (event, type)) sendEvent (type, event);
+            // widget could be disposed at this point
+            return null;
+        }
+    }
+
+    /*
+    * If the key up is not hooked, reset last key
+    * and last ascii in case the key down is hooked.
+    */
+    if (!hooks (DWT.KeyUp) && !display.filters (DWT.KeyUp)) {
+        display.lastKey = display.lastAscii = 0;
+        display.lastVirtual = display.lastNull = display.lastDead = false;
+        return null;
+    }
+
+    /* Map the virtual key. */
+    /*
+    * Bug in WinCE.  MapVirtualKey() returns incorrect values.
+    * The fix is to rely on a key mappings table to determine
+    * whether the key event must be sent now or if a WM_CHAR
+    * event will follow.  The key mappings table maps virtual
+    * keys to DWT key codes and does not contain mappings for
+    * Windows virtual keys like VK_A.  Virtual keys that are
+    * both virtual and ASCII are a special case.
+    */
+    int mapKey = 0;
+    if (OS.IsWinCE) {
+        switch (wParam) {
+            case OS.VK_BACK: mapKey = DWT.BS; break;
+            case OS.VK_RETURN: mapKey = DWT.CR; break;
+            case OS.VK_DELETE: mapKey = DWT.DEL; break;
+            case OS.VK_ESCAPE: mapKey = DWT.ESC; break;
+            case OS.VK_TAB: mapKey = DWT.TAB; break;
+        }
+    } else {
+        mapKey = OS.MapVirtualKey (wParam, 2);
+    }
+
+    /*
+    * Bug in Windows 95 and NT.  When the user types an accent key such
+    * as ^ to get an accented character on a German keyboard, the accent
+    * key should be ignored and the next key that the user types is the
+    * accented key. The fix is to detect the accent key stroke (called
+    * a dead key) by testing the high bit of the value returned by
+    * MapVirtualKey ().  A further problem is that the high bit on
+    * Windows NT is bit 32 while the high bit on Windows 95 is bit 16.
+    * They should both be bit 32.
+    */
+    if (OS.IsWinNT) {
+        if ((mapKey & 0x80000000) !is 0) return null;
+    } else {
+        if ((mapKey & 0x8000) !is 0) return null;
+    }
+    if (display.lastDead) return null;
+
+    /*
+    * NOTE: On Windows 98, keypad keys are virtual despite the
+    * fact that a WM_CHAR is issued.  On Windows 2000 and XP,
+    * they are not virtual.  Therefore it is necessary to force
+    * numeric keypad keys to be virtual.
+    */
+    display.lastVirtual = mapKey is 0 || display.numpadKey (wParam) !is 0;
+    if (display.lastVirtual) {
+        display.lastKey = wParam;
+    } else {
+        /*
+        * Feature in Windows. The virtual key VK_CANCEL is treated
+        * as both a virtual key and ASCII key by Windows.  This
+        * means that a WM_CHAR with WPARAM=3 will be issued for
+        * this key.  In order to distinguish between this key and
+        * Ctrl+C, mark the key as virtual.
+        */
+        if (wParam is OS.VK_CANCEL) display.lastVirtual = true;
+        if (display.lastKey is 0) {
+            display.lastAscii = 0;
+            display.lastNull = display.lastDead = false;
+            return null;
+        }
+    }
+    LRESULT result = null;
+    if (!sendKeyEvent (DWT.KeyUp, OS.WM_KEYUP, wParam, lParam)) {
+        result = LRESULT.ONE;
+    }
+    // widget could be disposed at this point
+    display.lastKey = display.lastAscii = 0;
+    display.lastVirtual = display.lastNull = display.lastDead = false;
+    return result;
+}
+
+LRESULT wmKillFocus (int hwnd, int wParam, int lParam) {
+    int code = callWindowProc (hwnd, OS.WM_KILLFOCUS, wParam, lParam);
+    sendFocusEvent (DWT.FocusOut);
+    // widget could be disposed at this point
+
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in the focus
+    * or deactivate events.  If this happens, end the
+    * processing of the Windows message by returning
+    * zero as the result of the window proc.
+    */
+    if (isDisposed ()) return LRESULT.ZERO;
+    if (code is 0) return LRESULT.ZERO;
+    return new LRESULT (code);
+}
+
+LRESULT wmLButtonDblClk (int hwnd, int wParam, int lParam) {
+    /*
+    * Feature in Windows. Windows sends the following
+    * messages when the user double clicks the mouse:
+    *
+    *   WM_LBUTTONDOWN      - mouse down
+    *   WM_LBUTTONUP        - mouse up
+    *   WM_LBUTTONDBLCLK    - double click
+    *   WM_LBUTTONUP        - mouse up
+    *
+    * Applications that expect matching mouse down/up
+    * pairs will not see the second mouse down.  The
+    * fix is to send a mouse down event.
+    */
+    LRESULT result = null;
+    Display display = this.display;
+    display.captureChanged = false;
+    sendMouseEvent (DWT.MouseDown, 1, hwnd, OS.WM_LBUTTONDOWN, wParam, lParam);
+    if (sendMouseEvent (DWT.MouseDoubleClick, 1, hwnd, OS.WM_LBUTTONDBLCLK, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_LBUTTONDBLCLK, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is hwnd) OS.SetCapture (hwnd);
+    }
+    return result;
+}
+
+LRESULT wmLButtonDown (int hwnd, int wParam, int lParam) {
+    Display display = this.display;
+    LRESULT result = null;
+    int x = (short) (lParam & 0xFFFF);
+    int y = (short) (lParam >> 16);
+    bool [] consume = null, detect = null;
+    bool dragging = false, mouseDown = true;
+    int count = display.getClickCount (DWT.MouseDown, 1, hwnd, lParam);
+    if (count is 1 && (state & DRAG_DETECT) !is 0 && hooks (DWT.DragDetect)) {
+        if (!OS.IsWinCE) {
+            /*
+            * Feature in Windows.  It's possible that the drag
+            * operation will not be started while the mouse is
+            * down, meaning that the mouse should be captured.
+            * This can happen when the user types the ESC key
+            * to cancel the drag.  The fix is to query the state
+            * of the mouse and capture the mouse accordingly.
+            */
+            detect = new bool [1];
+            consume = new bool [1];
+            dragging = dragDetect (hwnd, x, y, true, detect, consume);
+            if (isDisposed ()) return LRESULT.ZERO;
+            mouseDown = OS.GetKeyState (OS.VK_LBUTTON) < 0;
+        }
+    }
+    display.captureChanged = false;
+    bool dispatch = sendMouseEvent (DWT.MouseDown, 1, count, 0, false, hwnd, OS.WM_LBUTTONDOWN, wParam, lParam);
+    if (dispatch && (consume is null || !consume [0])) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_LBUTTONDOWN, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    if (OS.IsPPC) {
+        /*
+        * Note: On WinCE PPC, only attempt to recognize the gesture for
+        * a context menu when the control contains a valid menu or there
+        * are listeners for the MenuDetect event.
+        */
+        Menu menu = getMenu ();
+        bool hasMenu = menu !is null && !menu.isDisposed ();
+        if (hasMenu || hooks (DWT.MenuDetect)) {
+            SHRGINFO shrg = new SHRGINFO ();
+            shrg.cbSize = SHRGINFO.sizeof;
+            shrg.hwndClient = hwnd;
+            shrg.ptDown_x = x;
+            shrg.ptDown_y = y;
+            shrg.dwFlags = OS.SHRG_RETURNCMD;
+            int type = OS.SHRecognizeGesture (shrg);
+            if (type is OS.GN_CONTEXTMENU) showMenu (x, y);
+        }
+    }
+    if (mouseDown) {
+        if (!display.captureChanged && !isDisposed ()) {
+            if (OS.GetCapture () !is hwnd) OS.SetCapture (hwnd);
+        }
+    }
+    if (dragging) {
+        sendDragEvent (1, x, y);
+    } else {
+        if (detect !is null && detect [0]) {
+            /*
+            * Feature in Windows.  DragDetect() captures the mouse
+            * and tracks its movement until the user releases the
+            * left mouse button, presses the ESC key, or moves the
+            * mouse outside the drag rectangle.  If the user moves
+            * the mouse outside of the drag rectangle, DragDetect()
+            * returns true and a drag and drop operation can be
+            * started.  When the left mouse button is released or
+            * the ESC key is pressed, these events are consumed by
+            * DragDetect() so that application code that matches
+            * mouse down/up pairs or looks for the ESC key will not
+            * function properly.  The fix is to send the missing
+            * events when the drag has not started.
+            *
+            * NOTE: For now, don't send a fake WM_KEYDOWN/WM_KEYUP
+            * events for the ESC key.  This would require computing
+            * wParam (the key) and lParam (the repeat count, scan code,
+            * extended-key flag, context code, previous key-state flag,
+            * and transition-state flag) which is non-trivial.
+            */
+            if (OS.GetKeyState (OS.VK_ESCAPE) >= 0) {
+                OS.SendMessage (hwnd, OS.WM_LBUTTONUP, wParam, lParam);
+            }
+        }
+    }
+    return result;
+}
+
+LRESULT wmLButtonUp (int hwnd, int wParam, int lParam) {
+    Display display = this.display;
+    LRESULT result = null;
+    if (sendMouseEvent (DWT.MouseUp, 1, hwnd, OS.WM_LBUTTONUP, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_LBUTTONUP, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    /*
+    * 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 (((wParam & 0xFFFF) & mask) is 0) {
+        if (OS.GetCapture () is hwnd) OS.ReleaseCapture ();
+    }
+    return result;
+}
+
+LRESULT wmMButtonDblClk (int hwnd, int wParam, int lParam) {
+    /*
+    * Feature in Windows. Windows sends the following
+    * messages when the user double clicks the mouse:
+    *
+    *   WM_MBUTTONDOWN      - mouse down
+    *   WM_MBUTTONUP        - mouse up
+    *   WM_MLBUTTONDBLCLK   - double click
+    *   WM_MBUTTONUP        - mouse up
+    *
+    * Applications that expect matching mouse down/up
+    * pairs will not see the second mouse down.  The
+    * fix is to send a mouse down event.
+    */
+    LRESULT result = null;
+    Display display = this.display;
+    display.captureChanged = false;
+    sendMouseEvent (DWT.MouseDown, 2, hwnd, OS.WM_MBUTTONDOWN, wParam, lParam);
+    if (sendMouseEvent (DWT.MouseDoubleClick, 2, hwnd, OS.WM_MBUTTONDBLCLK, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_MBUTTONDBLCLK, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is hwnd) OS.SetCapture (hwnd);
+    }
+    return result;
+}
+
+LRESULT wmMButtonDown (int hwnd, int wParam, int lParam) {
+    LRESULT result = null;
+    Display display = this.display;
+    display.captureChanged = false;
+    if (sendMouseEvent (DWT.MouseDown, 2, hwnd, OS.WM_MBUTTONDOWN, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_MBUTTONDOWN, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is hwnd) OS.SetCapture (hwnd);
+    }
+    return result;
+}
+
+LRESULT wmMButtonUp (int hwnd, int wParam, int lParam) {
+    Display display = this.display;
+    LRESULT result = null;
+    if (sendMouseEvent (DWT.MouseUp, 2, hwnd, OS.WM_MBUTTONUP, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_MBUTTONUP, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    /*
+    * 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 (((wParam & 0xFFFF) & mask) is 0) {
+        if (OS.GetCapture () is hwnd) OS.ReleaseCapture ();
+    }
+    return result;
+}
+
+LRESULT wmMouseHover (int hwnd, int wParam, int lParam) {
+    if (!sendMouseEvent (DWT.MouseHover, 0, hwnd, OS.WM_MOUSEHOVER, wParam, lParam)) {
+        return LRESULT.ZERO;
+    }
+    return null;
+}
+
+LRESULT wmMouseLeave (int hwnd, int wParam, int lParam) {
+    if (!hooks (DWT.MouseExit) && !filters (DWT.MouseExit)) return null;
+    int pos = OS.GetMessagePos ();
+    POINT pt = new POINT ();
+    pt.x = (short) (pos & 0xFFFF);
+    pt.y = (short) (pos >> 16);
+    OS.ScreenToClient (hwnd, pt);
+    lParam = (pt.x & 0xFFFF) | ((pt.y << 16) & 0xFFFF0000);
+    if (!sendMouseEvent (DWT.MouseExit, 0, hwnd, OS.WM_MOUSELEAVE, wParam, lParam)) {
+        return LRESULT.ZERO;
+    }
+    return null;
+}
+
+LRESULT wmMouseMove (int hwnd, int wParam, int lParam) {
+    LRESULT result = null;
+    Display display = this.display;
+    int pos = OS.GetMessagePos ();
+    if (pos !is display.lastMouse || display.captureChanged) {
+        if (!OS.IsWinCE) {
+            bool trackMouse = (state & TRACK_MOUSE) !is 0;
+            bool mouseEnter = hooks (DWT.MouseEnter) || display.filters (DWT.MouseEnter);
+            bool mouseExit = hooks (DWT.MouseExit) || display.filters (DWT.MouseExit);
+            bool mouseHover = hooks (DWT.MouseHover) || display.filters (DWT.MouseHover);
+            if (trackMouse || mouseEnter || mouseExit || mouseHover) {
+                TRACKMOUSEEVENT lpEventTrack = new TRACKMOUSEEVENT ();
+                lpEventTrack.cbSize = TRACKMOUSEEVENT.sizeof;
+                lpEventTrack.dwFlags = OS.TME_QUERY;
+                lpEventTrack.hwndTrack = hwnd;
+                OS.TrackMouseEvent (lpEventTrack);
+                if (lpEventTrack.dwFlags is 0) {
+                    lpEventTrack.dwFlags = OS.TME_LEAVE | OS.TME_HOVER;
+                    lpEventTrack.hwndTrack = hwnd;
+                    OS.TrackMouseEvent (lpEventTrack);
+                    if (mouseEnter) {
+                        /*
+                        * Force all outstanding WM_MOUSELEAVE messages to be dispatched before
+                        * issuing a mouse enter.  This causes mouse exit events to be processed
+                        * before mouse enter events.  Note that WM_MOUSELEAVE is posted to the
+                        * event queue by TrackMouseEvent().
+                        */
+                        MSG msg = new MSG ();
+                        int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE;
+                        while (OS.PeekMessage (msg, 0, OS.WM_MOUSELEAVE, OS.WM_MOUSELEAVE, flags)) {
+                            OS.TranslateMessage (msg);
+                            OS.DispatchMessage (msg);
+                        }
+                        sendMouseEvent (DWT.MouseEnter, 0, hwnd, OS.WM_MOUSEMOVE, wParam, lParam);
+                    }
+                } else {
+                    lpEventTrack.dwFlags = OS.TME_HOVER;
+                    OS.TrackMouseEvent (lpEventTrack);
+                }
+            }
+        }
+        if (pos !is display.lastMouse) {
+            display.lastMouse = pos;
+            if (!sendMouseEvent (DWT.MouseMove, 0, hwnd, OS.WM_MOUSEMOVE, wParam, lParam)) {
+                result = LRESULT.ZERO;
+            }
+        }
+    }
+    display.captureChanged = false;
+    return result;
+}
+
+LRESULT wmMouseWheel (int hwnd, int wParam, int lParam) {
+    if (!hooks (DWT.MouseWheel) && !filters (DWT.MouseWheel)) return null;
+    int delta = wParam >> 16;
+    int [] value = new int [1];
+    int count, detail;
+    OS.SystemParametersInfo (OS.SPI_GETWHEELSCROLLLINES, 0, value, 0);
+    if (value [0] is OS.WHEEL_PAGESCROLL) {
+        detail = DWT.SCROLL_PAGE;
+        count = delta / OS.WHEEL_DELTA;
+    } else {
+        detail = DWT.SCROLL_LINE;
+        count = value [0] * delta / OS.WHEEL_DELTA;
+    }
+    POINT pt = new POINT ();
+    pt.x = (short) (lParam & 0xFFFF);
+    pt.y = (short) (lParam >> 16);
+    OS.ScreenToClient (hwnd, pt);
+    lParam = (pt.x & 0xFFFF) | ((pt.y << 16) & 0xFFFF0000);
+    if (!sendMouseEvent (DWT.MouseWheel, 0, count, detail, true, hwnd, OS.WM_MOUSEWHEEL, wParam, lParam)) {
+        return LRESULT.ZERO;
+    }
+    return null;
+}
+
+LRESULT wmPaint (int hwnd, int wParam, int lParam) {
+
+    /* Exit early - don't draw the background */
+    if (!hooks (DWT.Paint) && !filters (DWT.Paint)) {
+        return null;
+    }
+
+    /* Issue a paint event */
+    int result = 0;
+    if (OS.IsWinCE) {
+        RECT rect = new RECT ();
+        OS.GetUpdateRect (hwnd, rect, false);
+        result = callWindowProc (hwnd, OS.WM_PAINT, wParam, lParam);
+        /*
+        * Bug in Windows.  When InvalidateRgn(), InvalidateRect()
+        * or RedrawWindow() with RDW_INVALIDATE is called from
+        * within WM_PAINT to invalidate a region for a further
+        * BeginPaint(), the caret is not properly erased causing
+        * pixel corruption.  The fix is to hide and show the
+        * caret.
+        */
+        OS.HideCaret (hwnd);
+        OS.InvalidateRect (hwnd, rect, false);
+        OS.ShowCaret (hwnd);
+        PAINTSTRUCT ps = new PAINTSTRUCT ();
+        GCData data = new GCData ();
+        data.ps = ps;
+        data.hwnd = hwnd;
+        GC gc = new_GC (data);
+        if (gc !is null) {
+            int width = ps.right - ps.left;
+            int height = ps.bottom - ps.top;
+            if (width !is 0 && height !is 0) {
+                Event event = new Event ();
+                event.gc = gc;
+                event.x = ps.left;
+                event.y = ps.top;
+                event.width = width;
+                event.height = height;
+                sendEvent (DWT.Paint, event);
+                // widget could be disposed at this point
+                event.gc = null;
+            }
+            gc.dispose ();
+        }
+    } else {
+        int rgn = OS.CreateRectRgn (0, 0, 0, 0);
+        OS.GetUpdateRgn (hwnd, rgn, false);
+        result = callWindowProc (hwnd, OS.WM_PAINT, wParam, lParam);
+        GCData data = new GCData ();
+        data.hwnd = hwnd;
+        GC gc = new_GC (data);
+        if (gc !is null) {
+            OS.HideCaret (hwnd);
+            RECT rect = new RECT();
+            OS.GetRgnBox (rgn, rect);
+            int width = rect.right - rect.left;
+            int height = rect.bottom - rect.top;
+            if (width !is 0 && height !is 0) {
+                int hDC = gc.handle;
+                OS.SelectClipRgn (hDC, rgn);
+                OS.SetMetaRgn (hDC);
+                Event event = new Event ();
+                event.gc = gc;
+                event.x = rect.left;
+                event.y = rect.top;
+                event.width = width;
+                event.height = height;
+                sendEvent (DWT.Paint, event);
+                // widget could be disposed at this point
+                event.gc = null;
+            }
+            gc.dispose ();
+            OS.ShowCaret (hwnd);
+        }
+        OS.DeleteObject (rgn);
+    }
+    if (result is 0) return LRESULT.ZERO;
+    return new LRESULT (result);
+}
+
+LRESULT wmPrint (int hwnd, int wParam, int lParam) {
+    /*
+    * Bug in Windows.  When WM_PRINT is used to print the contents
+    * of a control that has WS_EX_CLIENTEDGE, the old 3D border is
+    * drawn instead of the theme border.  The fix is to call the
+    * default window proc and then draw the theme border on top.
+    */
+    if ((lParam & OS.PRF_NONCLIENT) !is 0) {
+        if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
+            int bits = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE);
+            if ((bits & OS.WS_EX_CLIENTEDGE) !is 0) {
+                int code = callWindowProc (hwnd, OS.WM_PRINT, wParam, lParam);
+                RECT rect = new RECT ();
+                OS.GetWindowRect (hwnd, rect);
+                rect.right -= rect.left;
+                rect.bottom -= rect.top;
+                rect.left = rect.top = 0;
+                int border = OS.GetSystemMetrics (OS.SM_CXEDGE);
+                OS.ExcludeClipRect (wParam, border, border, rect.right - border, rect.bottom - border);
+                OS.DrawThemeBackground (display.hEditTheme (), wParam, OS.EP_EDITTEXT, OS.ETS_NORMAL, rect, null);
+                return new LRESULT (code);
+            }
+        }
+    }
+    return null;
+}
+
+LRESULT wmRButtonDblClk (int hwnd, int wParam, int lParam) {
+    /*
+    * Feature in Windows. Windows sends the following
+    * messages when the user double clicks the mouse:
+    *
+    *   WM_RBUTTONDOWN      - mouse down
+    *   WM_RBUTTONUP        - mouse up
+    *   WM_RBUTTONDBLCLK    - double click
+    *   WM_LBUTTONUP        - mouse up
+    *
+    * Applications that expect matching mouse down/up
+    * pairs will not see the second mouse down.  The
+    * fix is to send a mouse down event.
+    */
+    LRESULT result = null;
+    Display display = this.display;
+    display.captureChanged = false;
+    sendMouseEvent (DWT.MouseDown, 3, hwnd, OS.WM_RBUTTONDOWN, wParam, lParam);
+    if (sendMouseEvent (DWT.MouseDoubleClick, 3, hwnd, OS.WM_RBUTTONDBLCLK, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_RBUTTONDBLCLK, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is hwnd) OS.SetCapture (hwnd);
+    }
+    return result;
+}
+
+LRESULT wmRButtonDown (int hwnd, int wParam, int lParam) {
+    LRESULT result = null;
+    Display display = this.display;
+    display.captureChanged = false;
+    if (sendMouseEvent (DWT.MouseDown, 3, hwnd, OS.WM_RBUTTONDOWN, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_RBUTTONDOWN, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is hwnd) OS.SetCapture (hwnd);
+    }
+    return result;
+}
+
+LRESULT wmRButtonUp (int hwnd, int wParam, int lParam) {
+    Display display = this.display;
+    LRESULT result = null;
+    if (sendMouseEvent (DWT.MouseUp, 3, hwnd, OS.WM_RBUTTONUP, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_RBUTTONUP, wParam, lParam));
+    } else {
+        /* Call the DefWindowProc() to support WM_CONTEXTMENU */
+        OS.DefWindowProc (hwnd, OS.WM_RBUTTONUP, wParam, lParam);
+        result = LRESULT.ZERO;
+    }
+    /*
+    * 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 (((wParam & 0xFFFF) & mask) is 0) {
+        if (OS.GetCapture () is hwnd) OS.ReleaseCapture ();
+    }
+    return result;
+}
+
+LRESULT wmSetFocus (int hwnd, int wParam, int lParam) {
+    int code = callWindowProc (hwnd, OS.WM_SETFOCUS, wParam, lParam);
+    sendFocusEvent (DWT.FocusIn);
+    // widget could be disposed at this point
+
+    /*
+    * It is possible (but unlikely), that application
+    * code could have disposed the widget in the focus
+    * or activate events.  If this happens, end the
+    * processing of the Windows message by returning
+    * zero as the result of the window proc.
+    */
+    if (isDisposed ()) return LRESULT.ZERO;
+    if (code is 0) return LRESULT.ZERO;
+    return new LRESULT (code);
+}
+
+LRESULT wmSysChar (int hwnd, int wParam, int lParam) {
+    Display display = this.display;
+    display.lastAscii = wParam;
+    display.lastNull = wParam is 0;
+
+    /* Do not issue a key down if a menu bar mnemonic was invoked */
+    if (!hooks (DWT.KeyDown) && !display.filters (DWT.KeyDown)) {
+        return null;
+    }
+
+    /* Call the window proc to determine whether it is a system key or mnemonic */
+    bool oldKeyHit = display.mnemonicKeyHit;
+    display.mnemonicKeyHit = true;
+    int result = callWindowProc (hwnd, OS.WM_SYSCHAR, wParam, lParam);
+    bool consumed = false;
+    if (!display.mnemonicKeyHit) {
+        consumed = !sendKeyEvent (DWT.KeyDown, OS.WM_SYSCHAR, wParam, lParam);
+        // widget could be disposed at this point
+    }
+    consumed |= display.mnemonicKeyHit;
+    display.mnemonicKeyHit = oldKeyHit;
+    return consumed ? LRESULT.ONE : new LRESULT (result);
+}
+
+LRESULT wmSysKeyDown (int hwnd, int wParam, int lParam) {
+    /*
+    * Feature in Windows.  When WM_SYSKEYDOWN is sent,
+    * the user pressed ALT+<key> or F10 to get to the
+    * menu bar.  In order to issue events for F10 but
+    * ignore other key presses when the ALT is not down,
+    * make sure that either F10 was pressed or that ALT
+    * is pressed.
+    */
+    if (wParam !is OS.VK_F10) {
+        /* Make sure WM_SYSKEYDOWN was sent by ALT-<aKey>. */
+        if ((lParam & 0x20000000) is 0) return null;
+    }
+
+    /* Ignore well known system keys */
+    switch (wParam) {
+        case OS.VK_F4: {
+            int hwndShell = hwnd;
+            while (OS.GetParent (hwndShell) !is 0) {
+                if (OS.GetWindow (hwndShell, OS.GW_OWNER) !is 0) break;
+                hwndShell = OS.GetParent (hwndShell);
+            }
+            int bits = OS.GetWindowLong (hwndShell, OS.GWL_STYLE);
+            if ((bits & OS.WS_SYSMENU) !is 0) return null;
+        }
+    }
+
+    /* Ignore repeating modifier keys by testing key down state */
+    switch (wParam) {
+        case OS.VK_SHIFT:
+        case OS.VK_MENU:
+        case OS.VK_CONTROL:
+        case OS.VK_CAPITAL:
+        case OS.VK_NUMLOCK:
+        case OS.VK_SCROLL:
+            if ((lParam & 0x40000000) !is 0) return null;
+    }
+
+    /* Clear last key and last ascii because a new key has been typed */
+    display.lastAscii = display.lastKey = 0;
+    display.lastVirtual = display.lastNull = display.lastDead = false;
+
+    /* If are going to get a WM_SYSCHAR, ignore this message. */
+    /*
+    * Bug in WinCE.  MapVirtualKey() returns incorrect values.
+    * The fix is to rely on a key mappings table to determine
+    * whether the key event must be sent now or if a WM_CHAR
+    * event will follow.  The key mappings table maps virtual
+    * keys to DWT key codes and does not contain mappings for
+    * Windows virtual keys like VK_A.  Virtual keys that are
+    * both virtual and ASCII are a special case.
+    */
+    int mapKey = 0;
+    if (OS.IsWinCE) {
+        switch (wParam) {
+            case OS.VK_BACK: mapKey = DWT.BS; break;
+            case OS.VK_RETURN: mapKey = DWT.CR; break;
+            case OS.VK_DELETE: mapKey = DWT.DEL; break;
+            case OS.VK_ESCAPE: mapKey = DWT.ESC; break;
+            case OS.VK_TAB: mapKey = DWT.TAB; break;
+        }
+    } else {
+        mapKey = OS.MapVirtualKey (wParam, 2);
+    }
+    display.lastVirtual = mapKey is 0 || display.numpadKey (wParam) !is 0;
+    if (display.lastVirtual) {
+        display.lastKey = wParam;
+        /*
+        * Feature in Windows.  The virtual key VK_DELETE is not
+        * treated as both a virtual key and an ASCII key by Windows.
+        * Therefore, we will not receive a WM_SYSCHAR for this key.
+        * The fix is to treat VK_DELETE as a special case and map
+        * the ASCII value explicitly (Delete is 0x7F).
+        */
+        if (display.lastKey is OS.VK_DELETE) display.lastAscii = 0x7F;
+
+        /* When a keypad key is typed, a WM_SYSCHAR is not issued */
+        if (OS.VK_NUMPAD0 <= display.lastKey && display.lastKey <= OS.VK_DIVIDE) {
+            /*
+            * A WM_SYSCHAR will be issued for '*', '+', '-', '.' and '/'
+            * on the numeric keypad.  Avoid issuing the key event twice
+            * by checking for these keys.  Note that calling to ToAscii()
+            * or ToUnicode(), clear the character that is entered using
+            * the special Windows keypad sequence when NumLock is down
+            * (ie. typing ALT+0231 should gives 'c' with a cedilla when
+            * NumLock is down).  Do not call either of these from here.
+            */
+            switch (display.lastKey) {
+                case OS.VK_MULTIPLY:
+                case OS.VK_ADD:
+                case OS.VK_SUBTRACT:
+                case OS.VK_DECIMAL:
+                case OS.VK_DIVIDE: return null;
+            }
+            display.lastAscii = display.numpadKey (display.lastKey);
+        }
+    } else {
+        /*
+        * Convert LastKey to lower case because Windows non-virtual
+        * keys that are also ASCII keys, such as like VK_A, are have
+        * upper case values in WM_SYSKEYDOWN despite the fact that the
+        * Shift was not pressed.
+        */
+        display.lastKey = OS.CharLower ((short) mapKey);
+
+        /*
+        * Feature in Windows 98.  MapVirtualKey() indicates that
+        * a WM_SYSCHAR message will occur for Alt+Enter but
+        * this message never happens.  The fix is to issue the
+        * event from WM_SYSKEYDOWN and map VK_RETURN to '\r'.
+        */
+        if (OS.IsWinNT) return null;
+        if (wParam !is OS.VK_RETURN) return null;
+        display.lastAscii = '\r';
+    }
+
+    if (!sendKeyEvent (DWT.KeyDown, OS.WM_SYSKEYDOWN, wParam, lParam)) {
+        return LRESULT.ONE;
+    }
+    // widget could be disposed at this point
+    return null;
+}
+
+LRESULT wmSysKeyUp (int hwnd, int wParam, int lParam) {
+    return wmKeyUp (hwnd, wParam, lParam);
+}
+
+LRESULT wmXButtonDblClk (int hwnd, int wParam, int lParam) {
+    /*
+    * Feature in Windows. Windows sends the following
+    * messages when the user double clicks the mouse:
+    *
+    *   WM_XBUTTONDOWN      - mouse down
+    *   WM_XBUTTONUP        - mouse up
+    *   WM_XLBUTTONDBLCLK   - double click
+    *   WM_XBUTTONUP        - mouse up
+    *
+    * Applications that expect matching mouse down/up
+    * pairs will not see the second mouse down.  The
+    * fix is to send a mouse down event.
+    */
+    LRESULT result = null;
+    Display display = this.display;
+    display.captureChanged = false;
+    int button = (wParam >> 16 is OS.XBUTTON1) ? 4 : 5;
+    sendMouseEvent (DWT.MouseDown, button, hwnd, OS.WM_XBUTTONDOWN, wParam, lParam);
+    if (sendMouseEvent (DWT.MouseDoubleClick, button, hwnd, OS.WM_XBUTTONDBLCLK, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_XBUTTONDBLCLK, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is hwnd) OS.SetCapture (hwnd);
+    }
+    return result;
+}
+
+LRESULT wmXButtonDown (int hwnd, int wParam, int lParam) {
+    LRESULT result = null;
+    Display display = this.display;
+    display.captureChanged = false;
+    display.xMouse = true;
+    int button = (wParam >> 16 is OS.XBUTTON1) ? 4 : 5;
+    if (sendMouseEvent (DWT.MouseDown, button, hwnd, OS.WM_XBUTTONDOWN, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_XBUTTONDOWN, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    if (!display.captureChanged && !isDisposed ()) {
+        if (OS.GetCapture () !is hwnd) OS.SetCapture (hwnd);
+    }
+    return result;
+}
+
+LRESULT wmXButtonUp (int hwnd, int wParam, int lParam) {
+    Display display = this.display;
+    LRESULT result = null;
+    int button = (wParam >> 16 is OS.XBUTTON1) ? 4 : 5;
+    if (sendMouseEvent (DWT.MouseUp, button, hwnd, OS.WM_XBUTTONUP, wParam, lParam)) {
+        result = new LRESULT (callWindowProc (hwnd, OS.WM_XBUTTONUP, wParam, lParam));
+    } else {
+        result = LRESULT.ZERO;
+    }
+    /*
+    * 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 (((wParam & 0xFFFF) & mask) is 0) {
+        if (OS.GetCapture () is hwnd) OS.ReleaseCapture ();
+    }
+    return result;
+}
+}
+++/
\ No newline at end of file