view dwt/widgets/Composite.d @ 165:07ed83d51a19

Fix tooltip bugs. by Zhiguang Liang 1).tailed by 0XFF string do a conversion of utf8-> utf16 string would throw exception by tango. We need to nullify the last byte of the chars string. 2).the function finger print doesn't keep consistency. setToolTipText in Shell.d Note: in the NMTTDISPINFO lpnmtdi struct from message, the field hinst should be forced to null. but from the test experience, it now keeps always 0, let it be the same as java code.
author davelzg@gmail.com
date Fri, 22 Feb 2008 00:40:30 +0800
parents 25f88bf5a6df
children ab60f3309436
line wrap: on
line source

/*******************************************************************************
 * 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.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.win32.OS;
import dwt.widgets.Scrollable;
import dwt.widgets.Control;
import dwt.widgets.Layout;
import dwt.widgets.Menu;
import dwt.widgets.Shell;
import dwt.widgets.Decorations;
import dwt.widgets.Event;
import dwt.widgets.ToolTip;
import dwt.widgets.Display;

import dwt.dwthelper.System;
import dwt.dwthelper.utils;

/**
 * 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 : Scrollable {

    alias Scrollable.computeSize computeSize;
    alias Scrollable.translateMnemonic translateMnemonic;

    Layout layout_;
    HFONT font;
    WINDOWPOS* [] lpwp;
    Control [] tabList;
    int layoutCount, backgroundMode;

/**
 * Prevents uninitialized instances from being created outside the package.
 */
this () {
}

/**
 * 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 this (Composite parent, int style) {
    super (parent, style);
}

Control [] _getChildren () {
    int count = 0;
    auto hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
    if (hwndChild is null) return new Control [0];
    while (hwndChild !is null) {
        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 null) {
        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;
        }
    }
}

override void checkBuffered () {
    if (OS.IsWinCE || (state & CANVAS) is 0) {
        super.checkBuffered ();
    }
}

override protected void checkSubclass () {
    /* Do nothing - Subclassing is allowed */
}

override 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;
}

override 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);
}

override 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 ();
}

override 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;
}

override 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;
    auto hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
    while (hwndChild !is null) {
        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);
    }
}

override 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);
}

override bool redrawChildren () {
    if (!super.redrawChildren ()) return false;
    Control [] children = _getChildren ();
    for (int i=0; i<children.length; i++) {
        children [i].redrawChildren ();
    }
    return true;
}

override 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);
}

override void releaseWidget () {
    super.releaseWidget ();
    if ((state & CANVAS) !is 0 && (style & DWT.EMBEDDED) !is 0) {
        auto hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
        if (hwndChild !is null) {
            int threadId = OS.GetWindowThreadProcessId (hwndChild, null);
            if (threadId !is OS.GetCurrentThreadId ()) {
                OS.ShowWindow (hwndChild, OS.SW_HIDE);
                OS.SetParent (hwndChild, null);
            }
        }
    }
    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;
    HDWP hdwp;
    if (defer) {
        hdwp = OS.BeginDeferWindowPos (pwp.length);
        if (hdwp is null) 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, null, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
                if (hdwp is null) return false;
            } else {
                SetWindowPos (wp._hwnd, null, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
            }
        }
    }
    if (defer) return cast(bool)OS.EndDeferWindowPos (hdwp);
    return true;
}

void resizeEmbeddedHandle(HANDLE embeddedHandle, int width, int height) {
    if (embeddedHandle is null) return;
    uint processID;
    int threadId = OS.GetWindowThreadProcessId (embeddedHandle, &processID);
    if (threadId !is OS.GetCurrentThreadId ()) {
        if (processID is OS.GetCurrentProcessId ()) {
            if (display.msgHook is null) {
                static 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.getMsgFunc, 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, null, 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 ();
    }
}

override 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 ();
}

override 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];
        }
    }
}

override 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;
}

char[] toolTipText (NMTTDISPINFO* hdr) {
    Shell shell = getShell ();
    if ((hdr.uFlags & OS.TTF_IDISHWND) is 0) {
        char[] string = null;
        ToolTip toolTip = shell.findToolTip (hdr.hdr.idFrom);
        if (toolTip !is null) {
            string = toolTip.message;
            if (string is null || string.length is 0) string = " ";
        }
        return string;
    }
    shell.setToolTipTitle (hdr.hdr.hwndFrom, null, null);
    OS.SendMessage (hdr.hdr.hwndFrom, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
    Control control = display.getControl ( cast(HANDLE) hdr.hdr.idFrom);
    return control !is null ? control.toolTipText_ : null;
}

override 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;
}

override 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;
            default:
        }
    }
    return super.translateTraversal (msg);
}

override 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 ();
        }
    }
}

override 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 ();
        }
    }
}

override void updateBackgroundMode () {
    super.updateBackgroundMode ();
    Control [] children = _getChildren ();
    for (int i = 0; i < children.length; i++) {
        children [i].updateBackgroundMode ();
    }
}

override 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);
        }
    }
}

override 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);
        }
    }
}

override int widgetStyle () {
    /* Force clipping of children by setting WS_CLIPCHILDREN */
    return super.widgetStyle () | OS.WS_CLIPCHILDREN;
}

override 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;
}

override 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 null) flags |= OS.DLGC_STATIC;
        if (flags !is 0) return new LRESULT (flags);
    }
    return result;
}

override 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 null) font = defaultFont ();
    return new LRESULT (cast(int) font);
}

override 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 null) setFocus ();
        }
    }
    return result;
}

override 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;
}

override 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;
            OS.GetClientRect (handle, &rect);
            resizeEmbeddedHandle ( cast(HANDLE)lParam, rect.right - rect.left, rect.bottom - rect.top);
        }
    }
    return super.WM_PARENTNOTIFY (wParam, lParam);
}

override 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;
    static 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;
    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) {
            auto hDC = OS.BeginPaint (handle, &ps);
            int width = ps.rcPaint.right - ps.rcPaint.left;
            int height = ps.rcPaint.bottom - ps.rcPaint.top;
            if (width !is 0 && height !is 0) {
                HDC phdc;
                int flags = OS.BPBF_COMPATIBLEBITMAP;
                RECT prcTarget;
                OS.SetRect (&prcTarget, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
                auto 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 = cast(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;
                    OS.SetRect (&rect, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
                    drawBackground (phdc, &rect);
                }
                GC gc = GC.win32_new (phdc, data);
                Event event = new Event ();
                event.gc = gc;
                event.x = ps.rcPaint.left;
                event.y = ps.rcPaint.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 */
            HRGN sysRgn;
            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, cast(RGNDATA*)lpRgnData.ptr);
                            HRGN newSysRgn = OS.ExtCreateRegion ( cast(XFORM*) [-1.0f, 0, 0, 1, 0, 0].ptr, nBytes, cast(RGNDATA*)lpRgnData.ptr);
                            OS.DeleteObject (sysRgn);
                            sysRgn = newSysRgn;
                        }
                    }
                    if (OS.IsWinNT) {
                        POINT pt;
                        OS.MapWindowPoints (null, handle, &pt, 1);
                        OS.OffsetRgn (sysRgn, pt.x, pt.y);
                    }
                }
            }

            /* Send the paint event */
            int width = ps.rcPaint.right - ps.rcPaint.left;
            int height = ps.rcPaint.bottom - ps.rcPaint.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.rcPaint.left, -ps.rcPaint.top);
                    OS.SelectClipRgn (gc.handle, sysRgn);
                    OS.OffsetRgn (sysRgn, ps.rcPaint.left, ps.rcPaint.top);
                    OS.SetMetaRgn (gc.handle);
                    OS.SetWindowOrgEx (gc.handle, ps.rcPaint.left, ps.rcPaint.top, null);
                    OS.SetBrushOrgEx (gc.handle, ps.rcPaint.left, ps.rcPaint.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;
                        OS.SetRect (&rect, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
                        drawBackground (gc.handle, &rect);
                    }
                }
                Event event = new Event ();
                event.gc = gc;
                RECT rect;
                if ((style & DWT.NO_MERGE_PAINTS) !is 0 && OS.GetRgnBox (sysRgn, &rect) is OS.COMPLEXREGION) {
                    int nBytes = OS.GetRegionData (sysRgn, 0, null);
                    int [] lpRgnData = new int [nBytes / 4];
                    OS.GetRegionData (sysRgn, nBytes, cast(RGNDATA*)lpRgnData.ptr);
                    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) {
                        OS.SetRect (&rect, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
                        drawBackground (gc.handle, &rect);
                    }
                    event.x = ps.rcPaint.left;
                    event.y = ps.rcPaint.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.rcPaint.left, ps.rcPaint.top);
                    image.dispose ();
                    gc = paintGC;
                }
            }
            if (sysRgn !is null) OS.DeleteObject (sysRgn);

            /* Dispose the paint GC */
            gc.dispose ();
        }
    } else {
        auto hDC = OS.BeginPaint (handle, &ps);
        if ((style & DWT.NO_BACKGROUND) is 0) {
            RECT rect;
            OS.SetRect (&rect, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.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;
}

override 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 ();
        auto nSavedDC = OS.SaveDC (cast(HDC)wParam);
        RECT rect;
        OS.GetClientRect (handle, &rect);
        if ((style & DWT.NO_BACKGROUND) is 0) {
            drawBackground ( cast(HDC)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 = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
            data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
            GC gc = GC.win32_new (cast(HDC)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 (cast(HDC)wParam, nSavedDC);
    }
    return result;
}

override LRESULT WM_SETFONT (int wParam, int lParam) {
    if (lParam !is 0) OS.InvalidateRect (handle, null, true);
    font = cast(HFONT)wParam;
    return super.WM_SETFONT (wParam, lParam);
}

override 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;
}

override LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) {
    LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
    if (result !is null) return result;
    auto hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
    while (hwndChild !is null) {
        OS.SendMessage (hwndChild, OS.WM_SYSCOLORCHANGE, 0, 0);
        hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
    }
    return result;
}

override 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.
    */
    static 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, null, flags);
                    }
                if (code is 0) return LRESULT.ZERO;
                return new LRESULT (code);
            default:
        }
    }
    /* Return the result */
    return result;
}

override 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 (HWND 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);
            }
            auto hDC = OS.GetWindowDC (hwnd);
            RECT 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;
}

override LRESULT wmNotify (NMHDR* hdr, int wParam, int lParam) {
    static 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.
                */
                auto hwndParent = hdr.hwndFrom;
                do {
                    hwndParent = OS.GetParent (hwndParent);
                    if (hwndParent is null) break;
                    int bits = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE);
                    if ((bits & OS.WS_EX_TOPMOST) !is 0) break;
                } while (true);
                if (hwndParent !is null) break;
                display.lockActiveWindow = true;
                int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOSIZE;
                HWND hwndInsertAfter = cast(HWND)( 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 = cast(NMTTDISPINFO*)lParam;
//                 if (hdr.code is OS.TTN_GETDISPINFOA) {
//                     lpnmtdi = new NMTTDISPINFOA ();
//                     OS.MoveMemory (cast(NMTTDISPINFOA)lpnmtdi, lParam, NMTTDISPINFOA.sizeof);
//                 } else {
//                     lpnmtdi = new NMTTDISPINFOW ();
//                     OS.MoveMemory (cast(NMTTDISPINFOW)lpnmtdi, lParam, NMTTDISPINFOW.sizeof);
//                 }
                char[] 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);
                    chars[$-1] = 0; // d initialize it to 0xFF, we need to make it null terminated

                    /*
                    * Ensure that the orientation of the tool tip matches
                    * the orientation of the control.
                    */
                    HWND hwnd = cast(HWND)hdr.idFrom;
                    if (hwnd !is null && ((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) {
                        auto bytes = MBCSsToStr( chars, getCodePage () );
                        //byte [] bytes = new byte [chars.length * 2];
                        //OS.WideCharToMultiByte (getCodePage (), 0, chars.ptr, chars.length, bytes, bytes.length, null, null);
                        shell.setToolTipText (lpnmtdi, bytes);
                        //OS.MoveMemory (lParam, cast(NMTTDISPINFOA)lpnmtdi, NMTTDISPINFOA.sizeof);
                    } else {
                        shell.setToolTipText (lpnmtdi, chars);
                        //OS.MoveMemory (lParam, cast(NMTTDISPINFOW)lpnmtdi, NMTTDISPINFOW.sizeof);
                    }
                    return LRESULT.ZERO;
                }
                break;
            }
            default:
        }
    }
    return super.wmNotify (hdr, wParam, lParam);
}

}