org.eclipse.swt.widgets.ToolBar.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.swt.widgets.ToolBar.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2014 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.widgets;

import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.win32.*;

/**
 * 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>
 * <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>
 *
 * @see <a href="http://www.eclipse.org/swt/snippets/#toolbar">ToolBar, ToolItem snippets</a>
 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
 * @noextend This class is not intended to be subclassed by clients.
 */
public class ToolBar extends Composite {
    int lastFocusId, lastArrowId, lastHotId;
    ToolItem[] items;
    ToolItem[] tabItemList;
    boolean ignoreResize, ignoreMouse;
    ImageList imageList, disabledImageList, hotImageList;
    static final long 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>SWT</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>SWT</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 SWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
     * </ul>
     *
     * @see SWT#FLAT
     * @see SWT#WRAP
     * @see SWT#RIGHT
     * @see SWT#HORIZONTAL
     * @see SWT#SHADOW_OUT
     * @see SWT#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 & SWT.VERTICAL) != 0) {
            this.style |= SWT.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.IsAppThemed()) {
                if ((style & SWT.RIGHT) != 0)
                    bits |= OS.TBSTYLE_LIST;
            }
            OS.SetWindowLong(handle, OS.GWL_STYLE, bits | OS.CCS_VERT);
        } else {
            this.style |= SWT.HORIZONTAL;
        }
    }

    @Override
    long callWindowProc(long hwnd, int msg, long wParam, long lParam) {
        if (handle == 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 == 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 & SWT.FLAT) == 0)
            style |= SWT.NO_FOCUS;

        /*
        * A vertical tool bar cannot wrap because TB_SETROWS
        * fails when the toolbar has TBSTYLE_WRAPABLE.
        */
        if ((style & SWT.VERTICAL) != 0)
            style &= ~SWT.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 SWT style.
        */
        return style & ~(SWT.H_SCROLL | SWT.V_SCROLL);
    }

    @Override
    void checkBuffered() {
        super.checkBuffered();
        style |= SWT.DOUBLE_BUFFERED;
    }

    @Override
    protected void checkSubclass() {
        if (!isValidSubclass())
            error(SWT.ERROR_INVALID_SUBCLASS);
    }

    @Override
    Point computeSizeInPixels(int wHint, int hHint, boolean changed) {
        int width = 0, height = 0;
        if ((style & SWT.VERTICAL) != 0) {
            RECT rect = new RECT();
            TBBUTTON lpButton = new TBBUTTON();
            int count = (int) 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) != 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 = getBorderWidthInPixels();
            int newWidth = wHint == SWT.DEFAULT ? 0x3FFF : wHint + border * 2;
            int newHeight = hHint == SWT.DEFAULT ? 0x3FFF : hHint + border * 2;
            boolean redraw = getDrawing() && OS.IsWindowVisible(handle);
            ignoreResize = true;
            if (redraw)
                OS.UpdateWindow(handle);
            int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER;
            OS.SetWindowPos(handle, 0, 0, 0, newWidth, newHeight, flags);
            int count = (int) OS.SendMessage(handle, OS.TB_BUTTONCOUNT, 0, 0);
            if (count != 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);
            }
            OS.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 == 0)
            width = DEFAULT_WIDTH;
        if (height == 0)
            height = DEFAULT_HEIGHT;
        if (wHint != SWT.DEFAULT)
            width = wHint;
        if (hHint != SWT.DEFAULT)
            height = hHint;
        Rectangle trim = computeTrimInPixels(0, 0, width, height);
        width = trim.width;
        height = trim.height;
        return new Point(width, height);
    }

    @Override
    Rectangle computeTrimInPixels(int x, int y, int width, int height) {
        Rectangle trim = super.computeTrimInPixels(x, y, width, height);
        int bits = OS.GetWindowLong(handle, OS.GWL_STYLE);
        if ((bits & OS.CCS_NODIVIDER) == 0)
            trim.height += 2;
        return trim;
    }

    @Override
    Widget computeTabGroup() {
        ToolItem[] items = _getItems();
        if (tabItemList == null) {
            int i = 0;
            while (i < items.length && items[i].control == null)
                i++;
            if (i == items.length)
                return super.computeTabGroup();
        }
        int index = (int) OS.SendMessage(handle, OS.TB_GETHOTITEM, 0, 0);
        if (index == -1)
            index = lastHotId;
        while (index >= 0) {
            ToolItem item = items[index];
            if (item.isTabGroup())
                return item;
            index--;
        }
        return super.computeTabGroup();
    }

    @Override
    Widget[] computeTabList() {
        ToolItem[] items = _getItems();
        if (tabItemList == null) {
            int i = 0;
            while (i < items.length && items[i].control == null)
                i++;
            if (i == items.length)
                return super.computeTabList();
        }
        Widget result[] = {};
        if (!isTabGroup() || !isEnabled() || !isVisible())
            return result;
        ToolItem[] list = tabList != null ? _getTabItemList() : items;
        for (int i = 0; i < list.length; i++) {
            ToolItem child = list[i];
            Widget[] childList = child.computeTabList();
            if (childList.length != 0) {
                Widget[] newResult = new Widget[result.length + childList.length];
                System.arraycopy(result, 0, newResult, 0, result.length);
                System.arraycopy(childList, 0, newResult, result.length, childList.length);
                result = newResult;
            }
        }
        if (result.length == 0)
            result = new Widget[] { this };
        return result;
    }

    @Override
    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 & SWT.FLAT) != 0) {
            if (!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.
        */
        //   long 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.
        */
        long 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
                | OS.TBSTYLE_EX_DOUBLEBUFFER;
        OS.SendMessage(handle, OS.TB_SETEXTENDEDSTYLE, 0, bits);
    }

    void createItem(ToolItem item, int index) {
        int count = (int) OS.SendMessage(handle, OS.TB_BUTTONCOUNT, 0, 0);
        if (!(0 <= index && index <= count))
            error(SWT.ERROR_INVALID_RANGE);
        int id = 0;
        while (id < items.length && items[id] != null)
            id++;
        if (id == 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) == 0)
            lpButton.iBitmap = OS.I_IMAGENONE;
        if (OS.SendMessage(handle, OS.TB_INSERTBUTTON, index, lpButton) == 0) {
            error(SWT.ERROR_ITEM_NOT_ADDED);
        }
        items[item.id = id] = item;
        if ((style & SWT.VERTICAL) != 0)
            setRowCount(count + 1);
        layoutItems();
    }

    @Override
    void createWidget() {
        super.createWidget();
        items = new ToolItem[4];
        lastFocusId = lastArrowId = lastHotId = -1;
    }

    @Override
    int applyThemeBackground() {
        return -1; /* No Change */
    }

    void destroyItem(ToolItem item) {
        TBBUTTONINFO info = new TBBUTTONINFO();
        info.cbSize = TBBUTTONINFO.sizeof;
        info.dwMask = OS.TBIF_IMAGE | OS.TBIF_STYLE;
        int index = (int) 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) == 0 && info.iImage != OS.I_IMAGENONE) {
            if (imageList != null)
                imageList.put(info.iImage, null);
            if (hotImageList != null)
                hotImageList.put(info.iImage, null);
            if (disabledImageList != null)
                disabledImageList.put(info.iImage, null);
        }
        OS.SendMessage(handle, OS.TB_DELETEBUTTON, index, 0);
        if (item.id == lastFocusId)
            lastFocusId = -1;
        if (item.id == lastArrowId)
            lastArrowId = -1;
        if (item.id == lastHotId)
            lastHotId = -1;
        items[item.id] = null;
        item.id = -1;
        int count = (int) OS.SendMessage(handle, OS.TB_BUTTONCOUNT, 0, 0);
        if (count == 0) {
            if (imageList != null) {
                OS.SendMessage(handle, OS.TB_SETIMAGELIST, 0, 0);
                display.releaseToolImageList(imageList);
            }
            if (hotImageList != null) {
                OS.SendMessage(handle, OS.TB_SETHOTIMAGELIST, 0, 0);
                display.releaseToolHotImageList(hotImageList);
            }
            if (disabledImageList != null) {
                OS.SendMessage(handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0);
                display.releaseToolDisabledImageList(disabledImageList);
            }
            imageList = hotImageList = disabledImageList = null;
            items = new ToolItem[4];
        }
        if ((style & SWT.VERTICAL) != 0)
            setRowCount(count - 1);
        layoutItems();
    }

    @Override
    void enableWidget(boolean 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 != null) {
                if ((item.style & SWT.SEPARATOR) == 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 SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public ToolItem getItem(int index) {
        checkWidget();
        int count = (int) OS.SendMessage(handle, OS.TB_BUTTONCOUNT, 0, 0);
        if (!(0 <= index && index < count))
            error(SWT.ERROR_INVALID_RANGE);
        TBBUTTON lpButton = new TBBUTTON();
        long result = OS.SendMessage(handle, OS.TB_GETBUTTON, index, lpButton);
        if (result == 0)
            error(SWT.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 SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public ToolItem getItem(Point point) {
        checkWidget();
        if (point == null)
            error(SWT.ERROR_NULL_ARGUMENT);
        return getItemInPixels(DPIUtil.autoScaleUp(point));
    }

    ToolItem getItemInPixels(Point point) {
        ToolItem[] items = getItems();
        for (int i = 0; i < items.length; i++) {
            Rectangle rect = items[i].getBoundsInPixels();
            if (rect.contains(point))
                return items[i];
        }
        return null;
    }

    /**
     * Returns the number of items contained in the receiver.
     *
     * @return the number of items
     *
     * @exception SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public int getItemCount() {
        checkWidget();
        return (int) 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 SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public ToolItem[] getItems() {
        checkWidget();
        return _getItems();
    }

    ToolItem[] _getItems() {
        int count = (int) 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 SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public int getRowCount() {
        checkWidget();
        if ((style & SWT.VERTICAL) != 0) {
            return (int) OS.SendMessage(handle, OS.TB_BUTTONCOUNT, 0, 0);
        }
        return (int) OS.SendMessage(handle, OS.TB_GETROWS, 0, 0);
    }

    ToolItem[] _getTabItemList() {
        if (tabItemList == null)
            return tabItemList;
        int count = 0;
        for (int i = 0; i < tabItemList.length; i++) {
            if (!tabItemList[i].isDisposed())
                count++;
        }
        if (count == tabItemList.length)
            return tabItemList;
        ToolItem[] newList = new ToolItem[count];
        int index = 0;
        for (int i = 0; i < tabItemList.length; i++) {
            if (!tabItemList[i].isDisposed()) {
                newList[index++] = tabItemList[i];
            }
        }
        tabItemList = newList;
        return tabItemList;
    }

    /**
     * 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 SWTException <ul>
     *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
     * </ul>
     */
    public int indexOf(ToolItem item) {
        checkWidget();
        if (item == null)
            error(SWT.ERROR_NULL_ARGUMENT);
        if (item.isDisposed())
            error(SWT.ERROR_INVALID_ARGUMENT);
        return (int) 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.IsAppThemed()) {
            if ((style & SWT.RIGHT) != 0 && (style & SWT.VERTICAL) == 0) {
                boolean hasText = false, hasImage = false;
                for (int i = 0; i < items.length; i++) {
                    ToolItem item = items[i];
                    if (item != null) {
                        if (!hasText)
                            hasText = item.text.length() != 0;
                        if (!hasImage)
                            hasImage = item.image != 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 != 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.
                    */
                    long hFont = OS.SendMessage(handle, OS.WM_GETFONT, 0, 0);
                    OS.SendMessage(handle, OS.WM_SETFONT, hFont, 0);
                    setDropDownItems(true);
                }
            }
        }

        if ((style & SWT.WRAP) != 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 & SWT.VERTICAL) != 0) {
            int itemCount = (int) OS.SendMessage(handle, OS.TB_BUTTONCOUNT, 0, 0);
            if (itemCount > 1) {
                TBBUTTONINFO info = new TBBUTTONINFO();
                info.cbSize = TBBUTTONINFO.sizeof;
                info.dwMask = OS.TBIF_SIZE;
                long size = OS.SendMessage(handle, OS.TB_GETBUTTONSIZE, 0, 0);
                info.cx = (short) OS.LOWORD(size);
                int index = 0, extraPadding = 0;
                while (index < items.length) {
                    ToolItem item = items[index];
                    if (item != null && (item.style & SWT.DROP_DOWN) != 0) {
                        /*
                         * Specifying 1 pixel extra padding to avoid truncation
                         * of widest item in the tool-bar when a tool-bar has
                         * SWT.VERTICAL style and any of the items in the
                         * tool-bar has SWT.DROP_DOWN style, Refer bug#437206
                         */
                        extraPadding = 1;
                        break;
                    }
                    index++;
                }
                if (index < items.length) {
                    long padding = OS.SendMessage(handle, OS.TB_GETPADDING, 0, 0);
                    info.cx += OS.LOWORD(padding + extraPadding) * 2;
                }
                for (int i = 0; i < items.length; i++) {
                    ToolItem item = items[i];
                    if (item != null && (item.style & SWT.SEPARATOR) == 0) {
                        OS.SendMessage(handle, OS.TB_SETBUTTONINFO, item.id, info);
                    }
                }
            }
        }

        /*
        * Feature on Windows. When SWT.WRAP or SWT.VERTICAL are set
        * the separator items with control are implemented using BTNS_BUTTON
        * instead of BTNS_SEP. When that is the case and TBSTYLE_LIST is
        * set, the layout of the ToolBar recalculates the width for all
        * BTNS_BUTTON based on the text and bitmap of the item.
        * This is not strictly wrong, but the user defined width for the
        * separators has to be respected if set.
        * The fix is to detect this case and reset the cx width for the item.
        */
        if ((style & (SWT.WRAP | SWT.VERTICAL)) != 0) {
            int bits = OS.GetWindowLong(handle, OS.GWL_STYLE);
            if ((bits & OS.TBSTYLE_LIST) != 0) {
                TBBUTTONINFO info = new TBBUTTONINFO();
                info.cbSize = TBBUTTONINFO.sizeof;
                info.dwMask = OS.TBIF_SIZE;
                for (int i = 0; i < items.length; i++) {
                    ToolItem item = items[i];
                    if (item != null && item.cx > 0) {
                        info.cx = item.cx;
                        OS.SendMessage(handle, OS.TB_SETBUTTONINFO, item.id, info);
                    }
                }
            }
        }

        for (int i = 0; i < items.length; i++) {
            ToolItem item = items[i];
            if (item != null)
                item.resizeControl();
        }
    }

    @Override
    boolean mnemonicHit(char ch) {
        int[] id = new int[1];
        if (OS.SendMessage(handle, OS.TB_MAPACCELERATOR, ch, id) == 0) {
            return false;
        }
        if ((style & SWT.FLAT) != 0 && !setTabGroupFocus())
            return false;
        int index = (int) OS.SendMessage(handle, OS.TB_COMMANDTOINDEX, id[0], 0);
        if (index == -1)
            return false;
        OS.SendMessage(handle, OS.TB_SETHOTITEM, index, 0);
        items[id[0]].click(false);
        return true;
    }

    @Override
    boolean mnemonicMatch(char ch) {
        int[] id = new int[1];
        if (OS.SendMessage(handle, OS.TB_MAPACCELERATOR, ch, id) == 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 = (int) OS.SendMessage(handle, OS.TB_COMMANDTOINDEX, id[0], 0);
        if (index == -1)
            return false;
        return findMnemonic(items[id[0]].text) != '\0';
    }

    @Override
    void releaseChildren(boolean destroy) {
        if (items != null) {
            for (int i = 0; i < items.length; i++) {
                ToolItem item = items[i];
                if (item != null && !item.isDisposed()) {
                    item.release(false);
                }
            }
            items = null;
        }
        super.releaseChildren(destroy);
    }

    @Override
    void releaseWidget() {
        super.releaseWidget();
        if (imageList != null) {
            OS.SendMessage(handle, OS.TB_SETIMAGELIST, 0, 0);
            display.releaseToolImageList(imageList);
        }
        if (hotImageList != null) {
            OS.SendMessage(handle, OS.TB_SETHOTIMAGELIST, 0, 0);
            display.releaseToolHotImageList(hotImageList);
        }
        if (disabledImageList != null) {
            OS.SendMessage(handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0);
            display.releaseToolDisabledImageList(disabledImageList);
        }
        imageList = hotImageList = disabledImageList = null;
    }

    @Override
    void removeControl(Control control) {
        super.removeControl(control);
        for (int i = 0; i < items.length; i++) {
            ToolItem item = items[i];
            if (item != null && item.control == control) {
                item.setControl(null);
            }
        }
    }

    @Override
    void reskinChildren(int flags) {
        if (items != null) {
            for (int i = 0; i < items.length; i++) {
                ToolItem item = items[i];
                if (item != null)
                    item.reskin(flags);
            }
        }
        super.reskinChildren(flags);
    }

    @Override
    void setBackgroundImage(long hBitmap) {
        super.setBackgroundImage(hBitmap);
        setBackgroundTransparent(hBitmap != 0);
    }

    @Override
    void setBackgroundPixel(int pixel) {
        super.setBackgroundPixel(pixel);
        setBackgroundTransparent(pixel != -1);
    }

    void setBackgroundTransparent(boolean 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 & SWT.FLAT) != 0) {
            if (!OS.IsAppThemed()) {
                int bits = OS.GetWindowLong(handle, OS.GWL_STYLE);
                if (!transparent && findBackgroundControl() == null) {
                    bits &= ~OS.TBSTYLE_TRANSPARENT;
                } else {
                    bits |= OS.TBSTYLE_TRANSPARENT;
                }
                OS.SetWindowLong(handle, OS.GWL_STYLE, bits);
            }
        }
    }

    @Override
    void setBoundsInPixels(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 != null) {
            if (getDrawing() && OS.IsWindowVisible(handle)) {
                parent.setResizeChildren(false);
                parent.setResizeChildren(true);
            }
        }
        super.setBoundsInPixels(x, y, width, height, flags);
    }

    @Override
    void setDefaultFont() {
        super.setDefaultFont();
        OS.SendMessage(handle, OS.TB_SETBITMAPSIZE, 0, 0);
        OS.SendMessage(handle, OS.TB_SETBUTTONSIZE, 0, 0);
    }

    void setDropDownItems(boolean 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
        * only images.
        */
        if (OS.IsAppThemed()) {
            boolean hasText = false, hasImage = false;
            for (int i = 0; i < items.length; i++) {
                ToolItem item = items[i];
                if (item != null) {
                    if (!hasText)
                        hasText = item.text.length() != 0;
                    if (!hasImage)
                        hasImage = item.image != null;
                    if (hasText && hasImage)
                        break;
                }
            }
            if (hasImage && !hasText) {
                for (int i = 0; i < items.length; i++) {
                    ToolItem item = items[i];
                    if (item != null && (item.style & SWT.DROP_DOWN) != 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 == imageList)
            return;
        long hImageList = 0;
        if ((disabledImageList = imageList) != null) {
            hImageList = disabledImageList.getHandle();
        }
        setDropDownItems(false);
        OS.SendMessage(handle, OS.TB_SETDISABLEDIMAGELIST, 0, hImageList);
        setDropDownItems(true);
    }

    @Override
    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 = SWT.PUSH | SWT.CHECK | SWT.RADIO | SWT.DROP_DOWN;
        while (index < items.length) {
            ToolItem item = items[index];
            if (item != null && (item.style & mask) != 0)
                break;
            index++;
        }
        if (index == 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 == imageList)
            return;
        long hImageList = 0;
        if ((hotImageList = imageList) != null) {
            hImageList = hotImageList.getHandle();
        }
        setDropDownItems(false);
        OS.SendMessage(handle, OS.TB_SETHOTIMAGELIST, 0, hImageList);
        setDropDownItems(true);
    }

    void setImageList(ImageList imageList) {
        if (this.imageList == imageList)
            return;
        long hImageList = 0;
        if ((this.imageList = imageList) != null) {
            hImageList = imageList.getHandle();
        }
        setDropDownItems(false);
        OS.SendMessage(handle, OS.TB_SETIMAGELIST, 0, hImageList);
        setDropDownItems(true);
    }

    @Override
    public boolean setParent(Composite parent) {
        checkWidget();
        if (!super.setParent(parent))
            return false;
        long hwndParent = parent.handle;
        OS.SendMessage(handle, OS.TB_SETPARENT, hwndParent, 0);
        /*
        * Bug in Windows.  When a tool bar is reparented, the tooltip
        * control that is automatically created for the item is not
        * reparented to the new shell.  The fix is to move the tooltip
        * over using SetWindowLongPtr().  Note that for some reason,
        * SetParent() does not work.
        */
        long hwndShell = parent.getShell().handle;
        long hwndToolTip = OS.SendMessage(handle, OS.TB_GETTOOLTIPS, 0, 0);
        OS.SetWindowLongPtr(hwndToolTip, OS.GWLP_HWNDPARENT, hwndShell);
        return true;
    }

    @Override
    public void setRedraw(boolean redraw) {
        checkWidget();
        setDropDownItems(false);
        super.setRedraw(redraw);
        setDropDownItems(true);
    }

    void setRowCount(int count) {
        if ((style & SWT.VERTICAL) != 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, OS.MAKEWPARAM(count, 1), 0);
            int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOZORDER;
            OS.SetWindowPos(handle, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, flags);
            ignoreResize = false;
        }
    }

    /*public*/ void setTabItemList(ToolItem[] tabList) {
        checkWidget();
        if (tabList != null) {
            for (int i = 0; i < tabList.length; i++) {
                ToolItem item = tabList[i];
                if (item == null)
                    error(SWT.ERROR_INVALID_ARGUMENT);
                if (item.isDisposed())
                    error(SWT.ERROR_INVALID_ARGUMENT);
                if (item.parent != this)
                    error(SWT.ERROR_INVALID_PARENT);
            }
            ToolItem[] newList = new ToolItem[tabList.length];
            System.arraycopy(tabList, 0, newList, 0, tabList.length);
            tabList = newList;
        }
        this.tabItemList = tabList;
    }

    @Override
    boolean setTabItemFocus() {
        int index = 0;
        while (index < items.length) {
            ToolItem item = items[index];
            if (item != null && (item.style & SWT.SEPARATOR) == 0) {
                if (item.getEnabled())
                    break;
            }
            index++;
        }
        if (index == items.length)
            return false;
        return super.setTabItemFocus();
    }

    @Override
    boolean updateTextDirection(int textDirection) {
        if (super.updateTextDirection(textDirection)) {
            ToolItem[] items = _getItems();
            int i = items.length;
            while (i-- > 0) {
                items[i].updateTextDirection(style & SWT.FLIP_TEXT_DIRECTION);
            }
            return true;
        }
        return false;
    }

    @Override
    String toolTipText(NMTTDISPINFO hdr) {
        if ((hdr.uFlags & OS.TTF_IDISHWND) != 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 = (int) hdr.idFrom;
        long hwndToolTip = OS.SendMessage(handle, OS.TB_GETTOOLTIPS, 0, 0);
        if (hwndToolTip == hdr.hwndFrom) {
            /*
            * Bug in Windows. For some reason the reading order
            * in NMTTDISPINFO is sometimes set incorrectly.  The
            * reading order seems to change every time the mouse
            * enters the control from the top edge.  The fix is
            * to explicitly set TTF_RTLREADING.
            */
            int flags = SWT.RIGHT_TO_LEFT | SWT.FLIP_TEXT_DIRECTION;
            if ((style & flags) != 0 && (style & flags) != flags) {
                hdr.uFlags |= OS.TTF_RTLREADING;
            } else {
                hdr.uFlags &= ~OS.TTF_RTLREADING;
            }
            if (toolTipText != null)
                return ""; //$NON-NLS-1$
            if (0 <= index && index < items.length) {
                ToolItem item = items[index];
                if (item != null) {
                    /*
                    * Bug in Windows.  When the  arrow keys are used to change
                    * the hot item, for some reason, Windows displays the tool
                    * tip for the hot item in at (0, 0) on the screen rather
                    * than next to the current hot item.  This fix is to disallow
                    * tool tips while the user is traversing with the arrow keys.
                    */
                    if (lastArrowId != -1)
                        return "";
                    return item.toolTipText;
                }
            }
        }
        return super.toolTipText(hdr);
    }

    @Override
    void updateOrientation() {
        super.updateOrientation();
        if (imageList != null) {
            Point size = imageList.getImageSize();
            ImageList newImageList = display.getImageListToolBar(style & SWT.RIGHT_TO_LEFT, size.x, size.y);
            ImageList newHotImageList = display.getImageListToolBarHot(style & SWT.RIGHT_TO_LEFT, size.x, size.y);
            ImageList newDisabledImageList = display.getImageListToolBarDisabled(style & SWT.RIGHT_TO_LEFT, size.x,
                    size.y);
            TBBUTTONINFO info = new TBBUTTONINFO();
            info.cbSize = TBBUTTONINFO.sizeof;
            info.dwMask = OS.TBIF_IMAGE;
            int count = (int) OS.SendMessage(handle, OS.TB_BUTTONCOUNT, 0, 0);
            for (int i = 0; i < count; i++) {
                ToolItem item = items[i];
                if ((item.style & SWT.SEPARATOR) != 0)
                    continue;
                if (item.image == null)
                    continue;
                OS.SendMessage(handle, OS.TB_GETBUTTONINFO, item.id, info);
                if (info.iImage != OS.I_IMAGENONE) {
                    Image image = imageList.get(info.iImage);
                    Image hot = hotImageList.get(info.iImage);
                    Image disabled = disabledImageList.get(info.iImage);
                    imageList.put(info.iImage, null);
                    hotImageList.put(info.iImage, null);
                    disabledImageList.put(info.iImage, null);
                    info.iImage = newImageList.add(image);
                    newHotImageList.add(hot);
                    newDisabledImageList.add(disabled);
                    OS.SendMessage(handle, OS.TB_SETBUTTONINFO, item.id, info);
                }
            }
            display.releaseToolImageList(imageList);
            display.releaseToolHotImageList(hotImageList);
            display.releaseToolDisabledImageList(disabledImageList);
            OS.SendMessage(handle, OS.TB_SETIMAGELIST, 0, newImageList.getHandle());
            OS.SendMessage(handle, OS.TB_SETHOTIMAGELIST, 0, newHotImageList.getHandle());
            OS.SendMessage(handle, OS.TB_SETDISABLEDIMAGELIST, 0, newDisabledImageList.getHandle());
            imageList = newImageList;
            hotImageList = newHotImageList;
            disabledImageList = newDisabledImageList;
            OS.InvalidateRect(handle, null, true);
        }
    }

    @Override
    int widgetStyle() {
        int bits = super.widgetStyle() | OS.CCS_NORESIZE | OS.TBSTYLE_TOOLTIPS | OS.TBSTYLE_CUSTOMERASE;
        if (OS.IsAppThemed())
            bits |= OS.TBSTYLE_TRANSPARENT;
        if ((style & SWT.SHADOW_OUT) == 0)
            bits |= OS.CCS_NODIVIDER;
        if ((style & SWT.WRAP) != 0)
            bits |= OS.TBSTYLE_WRAPABLE;
        if ((style & SWT.FLAT) != 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.IsAppThemed()) {
            if ((style & SWT.RIGHT) != 0)
                bits |= OS.TBSTYLE_LIST;
        }
        return bits;
    }

    @Override
    TCHAR windowClass() {
        return ToolBarClass;
    }

    @Override
    long windowProc() {
        return ToolBarProc;
    }

    @Override
    LRESULT WM_CAPTURECHANGED(long wParam, long lParam) {
        LRESULT result = super.WM_CAPTURECHANGED(wParam, lParam);
        if (result != 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 != null) {
                int fsState = (int) OS.SendMessage(handle, OS.TB_GETSTATE, item.id, 0);
                if ((fsState & OS.TBSTATE_PRESSED) != 0) {
                    fsState &= ~OS.TBSTATE_PRESSED;
                    OS.SendMessage(handle, OS.TB_SETSTATE, item.id, fsState);
                }
            }
        }
        return result;
    }

    @Override
    LRESULT WM_CHAR(long wParam, long lParam) {
        LRESULT result = super.WM_CHAR(wParam, lParam);
        if (result != null)
            return result;
        switch ((int) wParam) {
        case ' ':
            int index = (int) OS.SendMessage(handle, OS.TB_GETHOTITEM, 0, 0);
            if (index != -1) {
                TBBUTTON lpButton = new TBBUTTON();
                long code = OS.SendMessage(handle, OS.TB_GETBUTTON, index, lpButton);
                if (code != 0) {
                    items[lpButton.idCommand].click(false);
                    return LRESULT.ZERO;
                }
            }
        }
        return result;
    }

    @Override
    LRESULT WM_COMMAND(long wParam, long 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 SWT, the message
        * is delivered twice, once by SWT 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 != null)
            return result;
        return LRESULT.ZERO;
    }

    @Override
    LRESULT WM_GETDLGCODE(long wParam, long 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 != null)
            return result;
        return new LRESULT(OS.DLGC_BUTTON | OS.DLGC_WANTARROWS);
    }

    @Override
    LRESULT WM_KEYDOWN(long wParam, long lParam) {
        LRESULT result = super.WM_KEYDOWN(wParam, lParam);
        if (result != null)
            return result;
        switch ((int) 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;
    }

    @Override
    LRESULT WM_KILLFOCUS(long wParam, long lParam) {
        int index = (int) OS.SendMessage(handle, OS.TB_GETHOTITEM, 0, 0);
        TBBUTTON lpButton = new TBBUTTON();
        long code = OS.SendMessage(handle, OS.TB_GETBUTTON, index, lpButton);
        if (code != 0)
            lastFocusId = lpButton.idCommand;
        return super.WM_KILLFOCUS(wParam, lParam);
    }

    @Override
    LRESULT WM_LBUTTONDOWN(long wParam, long lParam) {
        if (ignoreMouse)
            return null;
        return super.WM_LBUTTONDOWN(wParam, lParam);
    }

    @Override
    LRESULT WM_LBUTTONUP(long wParam, long lParam) {
        if (ignoreMouse)
            return null;
        return super.WM_LBUTTONUP(wParam, lParam);
    }

    @Override
    LRESULT WM_MOUSELEAVE(long wParam, long lParam) {
        LRESULT result = super.WM_MOUSELEAVE(wParam, lParam);
        if (result != 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.
        */
        TOOLINFO lpti = new TOOLINFO();
        lpti.cbSize = TOOLINFO.sizeof;
        long hwndToolTip = OS.SendMessage(handle, OS.TB_GETTOOLTIPS, 0, 0);
        if (OS.SendMessage(hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) {
            if ((lpti.uFlags & OS.TTF_IDISHWND) == 0) {
                OS.SendMessage(hwndToolTip, OS.TTM_DELTOOL, 0, lpti);
                OS.SendMessage(hwndToolTip, OS.TTM_ADDTOOL, 0, lpti);
            }
        }
        return result;
    }

    @Override
    LRESULT WM_MOUSEMOVE(long wParam, long lParam) {
        if (OS.GetMessagePos() != display.lastMouse)
            lastArrowId = -1;
        return super.WM_MOUSEMOVE(wParam, lParam);
    }

    @Override
    LRESULT WM_NOTIFY(long wParam, long 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 SWT, the message
        * is delivered twice, once by SWT 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 != null)
            return result;
        return LRESULT.ZERO;
    }

    @Override
    LRESULT WM_SETFOCUS(long wParam, long lParam) {
        LRESULT result = super.WM_SETFOCUS(wParam, lParam);
        if (lastFocusId != -1 && handle == OS.GetFocus()) {
            int index = (int) OS.SendMessage(handle, OS.TB_COMMANDTOINDEX, lastFocusId, 0);
            OS.SendMessage(handle, OS.TB_SETHOTITEM, index, 0);
        }
        return result;
    }

    @Override
    LRESULT WM_SIZE(long wParam, long lParam) {
        if (ignoreResize) {
            long code = callWindowProc(handle, OS.WM_SIZE, wParam, lParam);
            if (code == 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 & SWT.BORDER) != 0 && (style & SWT.WRAP) != 0) {
            RECT windowRect = new RECT();
            OS.GetWindowRect(handle, windowRect);
            int index = 0, border = getBorderWidthInPixels() * 2;
            RECT rect = new RECT();
            int count = (int) 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 = (int) OS.SendMessage(handle, OS.TB_GETEXTENDEDSTYLE, 0, 0);
            if (index == count) {
                bits |= OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS;
            } else {
                bits &= ~OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS;
            }
            OS.SendMessage(handle, OS.TB_SETEXTENDEDSTYLE, 0, bits);
        }
        layoutItems();
        return result;
    }

    @Override
    LRESULT WM_WINDOWPOSCHANGING(long wParam, long lParam) {
        LRESULT result = super.WM_WINDOWPOSCHANGING(wParam, lParam);
        if (result != 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 (!getDrawing())
            return result;
        if ((style & SWT.WRAP) == 0)
            return result;
        if (!OS.IsWindowVisible(handle))
            return result;
        if (OS.SendMessage(handle, OS.TB_GETROWS, 0, 0) == 1) {
            return result;
        }
        WINDOWPOS lpwp = new WINDOWPOS();
        OS.MoveMemory(lpwp, lParam, WINDOWPOS.sizeof);
        if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) != 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;
    }

    @Override
    LRESULT wmCommandChild(long wParam, long lParam) {
        ToolItem child = items[OS.LOWORD(wParam)];
        if (child == null)
            return null;
        return child.wmCommandChild(wParam, lParam);
    }

    private boolean customDrawing() {
        return hasCustomBackground() || (hasCustomForeground() && OS.IsWindowEnabled(handle));
    }

    @Override
    LRESULT wmNotifyChild(NMHDR hdr, long wParam, long 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 != null) {
                Event event = new Event();
                event.detail = SWT.ARROW;
                int index = (int) OS.SendMessage(handle, OS.TB_COMMANDTOINDEX, lpnmtb.iItem, 0);
                RECT rect = new RECT();
                OS.SendMessage(handle, OS.TB_GETITEMRECT, index, rect);
                event.setLocationInPixels(rect.left, rect.bottom);
                child.sendSelectionEvent(SWT.Selection, event, false);
            }
            break;
        case OS.NM_CUSTOMDRAW:
            /*
            * 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.
            */
            NMTBCUSTOMDRAW nmcd = new NMTBCUSTOMDRAW();
            OS.MoveMemory(nmcd, lParam, NMTBCUSTOMDRAW.sizeof);
            //         if (drawCount != 0 || !OS.IsWindowVisible (handle)) {
            //            if (OS.WindowFromDC (nmcd.hdc) == 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) == 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);
            }
            case OS.CDDS_PREPAINT: {
                return new LRESULT(customDrawing() ? OS.CDRF_NOTIFYITEMDRAW : OS.CDRF_DODEFAULT);
            }
            case OS.CDDS_ITEMPREPAINT: {
                if (customDrawing()) {
                    nmcd.clrBtnFace = getBackgroundPixel();
                    nmcd.clrText = getForegroundPixel();
                    OS.MoveMemory(lParam, nmcd, NMTBCUSTOMDRAW.sizeof);
                    return new LRESULT(OS.TBCDRF_USECDCOLORS);
                }
                return new LRESULT(OS.CDRF_DODEFAULT);
            }
            }
            break;
        case OS.TBN_HOTITEMCHANGE:
            NMTBHOTITEM lpnmhi = new NMTBHOTITEM();
            OS.MoveMemory(lpnmhi, lParam, NMTBHOTITEM.sizeof);
            switch (lpnmhi.dwFlags) {
            case OS.HICF_MOUSE: {
                /*
                * Bug in Windows.  When the tool bar has focus, a mouse is
                * in an item and hover help for that item is displayed and
                * then the arrow keys are used to change the hot item,
                * for some reason, Windows snaps the hot item back to the
                * one that is under the mouse.  The fix is to disallow
                * hot item changes when the user is traversing using the
                * arrow keys.
                */
                if (lastArrowId != -1)
                    return LRESULT.ONE;
                break;
            }
            case OS.HICF_ARROWKEYS: {
                RECT client = new RECT();
                OS.GetClientRect(handle, client);
                int index = (int) 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;
                }
                lastArrowId = lpnmhi.idNew;
                break;
            }
            default:
                lastArrowId = -1;
            }
            if ((lpnmhi.dwFlags & OS.HICF_LEAVING) == 0) {
                lastHotId = lpnmhi.idNew;
            }
            break;
        }
        return super.wmNotifyChild(hdr, wParam, lParam);
    }

}