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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2000, 2015 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
 *    EclipseSource - ongoing development
 *******************************************************************************/
package org.eclipse.swt.widgets;

import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.widgets.ICoolBarAdapter;
import org.eclipse.swt.internal.widgets.IItemHolderAdapter;
import org.eclipse.swt.internal.widgets.coolbarkit.CoolBarLCA;

/**
 * Instances of this class provide an area for dynamically positioning the items
 * they contain.
 * <p>
 * The item children that may be added to instances of this class must be of
 * type <code>CoolItem</code>.
 * </p>
 * <p>
 * Note that although this class is a subclass of <code>Composite</code>, it
 * does not make sense to add <code>Control</code> children to it, or set a
 * layout on it.
 * </p>
 * <p>
 * <dl>
 * <dt><b>Styles:</b></dt>
 * <dd>FLAT, HORIZONTAL, VERTICAL</dd>
 * <dt><b>Events:</b></dt>
 * <dd>(none)</dd>
 * </dl>
 * </p>
 * <p>
 * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
 * </p>
 * <p>
 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
 * </p>
 * @since 1.0
 */
public class CoolBar extends Composite {

    private class CoolBarAdapter implements ICoolBarAdapter {
        @Override
        public void setItemOrder(int[] itemOrder) {
            CoolBar.this.setItemOrder(itemOrder);
        }
    }

    private class CoolBarItemHolder implements IItemHolderAdapter<Item> {

        @Override
        public void add(Item item) {
        }

        @Override
        public Item[] getItems() {
            return CoolBar.this.getItems();
        }

        @Override
        public void insert(Item item, int index) {
        }

        @Override
        public void remove(Item item) {
        }
    }

    CoolItem[][] items = new CoolItem[0][0];
    CoolItem[] originalItems = new CoolItem[0];
    // Cursor hoverCursor, dragCursor, cursor;
    CoolItem dragging = null;
    int mouseXOffset, itemXOffset;
    boolean isLocked = false;
    boolean inDispose = false;
    static final int ROW_SPACING = 2;
    static final int CLICK_DISTANCE = 3;
    static final int DEFAULT_COOLBAR_WIDTH = 0;
    static final int DEFAULT_COOLBAR_HEIGHT = 0;

    private transient IItemHolderAdapter<Item> itemHolder;
    private transient ICoolBarAdapter coolBarAdapter;

    /**
     * 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
     * @see Widget#checkSubclass
     * @see Widget#getStyle
     */
    public CoolBar(Composite parent, int style) {
        super(parent, checkStyle(style));
        if ((style & SWT.VERTICAL) != 0) {
            this.style |= SWT.VERTICAL;
            // hoverCursor = new Cursor(display, SWT.CURSOR_SIZENS);
        } else {
            this.style |= SWT.HORIZONTAL;
            // hoverCursor = new Cursor(display, SWT.CURSOR_SIZEWE);
        }
        // dragCursor = new Cursor(display, SWT.CURSOR_SIZEALL);
        Listener listener = new Listener() {
            @Override
            public void handleEvent(Event event) {
                switch (event.type) {
                case SWT.Dispose:
                    onDispose();
                    break;
                // case SWT.MouseDown: onMouseDown(event); break;
                // case SWT.MouseExit: onMouseExit(); break;
                // case SWT.MouseMove: onMouseMove(event); break;
                // case SWT.MouseUp: onMouseUp(event); break;
                // case SWT.MouseDoubleClick: onMouseDoubleClick(event); break;
                // case SWT.Paint: onPaint(event); break;
                case SWT.Resize:
                    onResize();
                    break;
                }
            }
        };
        int[] events = new int[] { SWT.Dispose,
                // SWT.MouseDown,
                // SWT.MouseExit,
                // SWT.MouseMove,
                // SWT.MouseUp,
                // SWT.MouseDoubleClick,
                // SWT.Paint,
                SWT.Resize };
        for (int i = 0; i < events.length; i++) {
            addListener(events[i], listener);
        }
    }

    @Override
    void initState() {
        removeState( /* CANVAS | */ THEME_BACKGROUND);
    }

    private static int checkStyle(int style) {
        int result = style;
        result |= SWT.NO_FOCUS;
        // return (style | SWT.NO_REDRAW_RESIZE) & ~(SWT.V_SCROLL | SWT.H_SCROLL);
        return (result) & ~(SWT.V_SCROLL | SWT.H_SCROLL);
    }

    // void _setCursor (Cursor cursor) {
    // if (this.cursor != null) return;
    // super.setCursor (cursor);
    // }
    @Override
    protected void checkSubclass() {
        if (!isValidSubclass())
            error(SWT.ERROR_INVALID_SUBCLASS);
    }

    @Override
    public Point computeSize(int wHint, int hHint, boolean changed) {
        checkWidget();
        int width = 0, height = 0;
        wrapItems((style & SWT.VERTICAL) != 0 ? hHint : wHint);
        boolean flat = (style & SWT.FLAT) != 0;
        for (int row = 0; row < items.length; row++) {
            int rowWidth = 0, rowHeight = 0;
            for (int i = 0; i < items[row].length; i++) {
                CoolItem item = items[row][i];
                rowWidth += item.preferredWidth;
                rowHeight = Math.max(rowHeight, item.preferredHeight);
            }
            height += rowHeight;
            if (!flat && row > 0)
                height += ROW_SPACING;
            width = Math.max(width, rowWidth);
        }
        wrapItems(getWidth());
        if (width == 0)
            width = DEFAULT_COOLBAR_WIDTH;
        if (height == 0)
            height = DEFAULT_COOLBAR_HEIGHT;
        if (wHint != SWT.DEFAULT)
            width = wHint;
        if (hHint != SWT.DEFAULT)
            height = hHint;
        Rectangle trim = computeTrim(0, 0, width, height);
        return fixPoint(trim.width, trim.height);
    }

    CoolItem getGrabbedItem(int x, int y) {
        for (int row = 0; row < items.length; row++) {
            for (int i = 0; i < items[row].length; i++) {
                CoolItem item = items[row][i];
                Rectangle bounds = item.internalGetBounds();
                bounds.width = CoolItem.MINIMUM_WIDTH;
                if (bounds.x > x)
                    break;
                if (bounds.y > y)
                    return null;
                if (bounds.contains(x, y)) {
                    return item;
                }
            }
        }
        return null;
    }

    /**
     * Returns the item that is currently displayed at the given, zero-relative
     * index. Throws an exception if the index is out of range.
     *
     * @param index
     *          the visual index of the item to return
     * @return the item at the given visual index
     *
     * @exception IllegalArgumentException
     *              <ul>
     *              <li>ERROR_INVALID_RANGE - if the index is not between 0 and
     *              the number of elements in the list minus 1 (inclusive)</li>
     *              </ul>
     * @exception 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 CoolItem getItem(int index) {
        checkWidget();
        CoolItem item = null;
        int sIndex = index;
        if (index < 0)
            error(SWT.ERROR_INVALID_RANGE);
        for (int row = 0; row < items.length && item == null; row++) {
            if (items[row].length > sIndex) {
                item = items[row][sIndex];
            } else {
                sIndex -= items[row].length;
            }
        }
        if (item == null) {
            error(SWT.ERROR_INVALID_RANGE);
        }
        return item;
    }

    /**
     * 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 originalItems.length;
    }

    /**
     * Returns an array of <code>CoolItem</code>s in the order in which they
     * are currently being displayed.
     * <p>
     * Note: This is not the actual structure used by the receiver to maintain its
     * list of items, so modifying the array will not affect the receiver.
     * </p>
     *
     * @return the receiver's items in their current visual order
     *
     * @exception 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 CoolItem[] getItems() {
        checkWidget();
        CoolItem[] result = new CoolItem[getItemCount()];
        int offset = 0;
        for (int row = 0; row < items.length; row++) {
            System.arraycopy(items[row], 0, result, offset, items[row].length);
            offset += items[row].length;
        }
        return result;
    }

    Point findItem(CoolItem item) {
        for (int row = 0; row < items.length; row++) {
            for (int i = 0; i < items[row].length; i++) {
                if (items[row][i].equals(item))
                    return new Point(i, row);
            }
        }
        return new Point(-1, -1);
    }

    void fixEvent(Event event) {
        if ((style & SWT.VERTICAL) != 0) {
            int tmp = event.x;
            event.x = event.y;
            event.y = tmp;
        }
    }

    Rectangle fixRectangle(int x, int y, int width, int height) {
        if ((style & SWT.VERTICAL) != 0) {
            return new Rectangle(y, x, height, width);
        }
        return new Rectangle(x, y, width, height);
    }

    Point fixPoint(int x, int y) {
        if ((style & SWT.VERTICAL) != 0) {
            return new Point(y, x);
        }
        return new Point(x, y);
    }

    /**
     * Searches the receiver's items in the order they are currently being
     * displayed, starting at the first item (index 0), until an item is found
     * that is equal to the argument, and returns the index of that item. If no
     * item is found, returns -1.
     *
     * @param item
     *          the search item
     * @return the visual order index of the search item, or -1 if the item is not
     *         found
     *
     * @exception IllegalArgumentException
     *              <ul>
     *              <li>ERROR_NULL_ARGUMENT - if the item is null</li>
     *              <li>ERROR_INVALID_ARGUMENT - if the item is disposed</li>
     *              </ul>
     * @exception 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>
     */
    @SuppressWarnings("all")
    public int indexOf(CoolItem item) {
        checkWidget();
        if (item == null)
            error(SWT.ERROR_NULL_ARGUMENT);
        if (item.isDisposed())
            error(SWT.ERROR_INVALID_ARGUMENT);
        int answer = 0;
        for (int row = 0; row < items.length; row++) {
            for (int i = 0; i < items[row].length; i++) {
                if (items[row][i].equals(item)) {
                    return answer;
                } else {
                    answer++;
                }
            }
        }
        return -1;
    }

    /**
     * Insert the item into the row. Adjust the x and width values appropriately.
     */
    void insertItemIntoRow(CoolItem item, int rowIndex, int x_root) {
        int barWidth = getWidth();
        int rowY = items[rowIndex][0].internalGetBounds().y;
        int x = Math.max(0, x_root - toDisplay(new Point(0, 0)).x);

        /* Find the insertion index and add the item. */
        int index;
        for (index = 0; index < items[rowIndex].length; index++) {
            if (x < items[rowIndex][index].internalGetBounds().x)
                break;
        }
        if (index == 0) {
            item.wrap = true;
            items[rowIndex][0].wrap = false;
        }
        int oldLength = items[rowIndex].length;
        CoolItem[] newRow = new CoolItem[oldLength + 1];
        System.arraycopy(items[rowIndex], 0, newRow, 0, index);
        newRow[index] = item;
        System.arraycopy(items[rowIndex], index, newRow, index + 1, oldLength - index);
        items[rowIndex] = newRow;

        /* Adjust the width of the item to the left. */
        if (index > 0) {
            CoolItem left = items[rowIndex][index - 1];
            Rectangle leftBounds = left.internalGetBounds();
            int newWidth = x - leftBounds.x;
            if (newWidth < left.internalGetMinimumWidth()) {
                x += left.internalGetMinimumWidth() - newWidth;
                newWidth = left.internalGetMinimumWidth();
            }
            left.setBounds(leftBounds.x, leftBounds.y, newWidth, leftBounds.height);
            left.requestedWidth = newWidth;
        }

        /* Set the item's bounds. */
        int width = 0, height = item.internalGetBounds().height;
        if (index < items[rowIndex].length - 1) {
            CoolItem right = items[rowIndex][index + 1];
            width = right.internalGetBounds().x - x;
            if (width < right.internalGetMinimumWidth()) {
                moveRight(right, right.internalGetMinimumWidth() - width);
                width = right.internalGetBounds().x - x;
            }
            item.setBounds(x, rowY, width, height);
            if (width < item.internalGetMinimumWidth())
                moveLeft(item, item.internalGetMinimumWidth() - width);
        } else {
            width = Math.max(item.internalGetMinimumWidth(), barWidth - x);
            item.setBounds(x, rowY, width, height);
            if (x + width > barWidth)
                moveLeft(item, x + width - barWidth);
        }
        Rectangle bounds = item.internalGetBounds();
        item.requestedWidth = bounds.width;
        // internalRedraw(bounds.x, bounds.y, item.internalGetMinimumWidth(),
        // bounds.height);
    }

    // void internalRedraw (int x, int y, int width, int height) {
    // if ((style & SWT.VERTICAL) != 0) {
    // redraw (y, x, height, width, false);
    // } else {
    // redraw (x, y, width, height, false);
    // }
    // }
    void createItem(CoolItem item, int index) {
        int itemCount = getItemCount(), row = 0;
        if (!(0 <= index && index <= itemCount))
            error(SWT.ERROR_INVALID_RANGE);
        if (items.length == 0) {
            items = new CoolItem[1][1];
            items[0][0] = item;
        } else {
            int i = index;
            /* find the row to insert into */
            if (index < itemCount) {
                while (i > items[row].length) {
                    i -= items[row].length;
                    row++;
                }
            } else {
                row = items.length - 1;
                i = items[row].length;
            }

            // Set the last item in the row to the preferred size
            // and add the new one just to it's right
            int lastIndex = items[row].length - 1;
            CoolItem lastItem = items[row][lastIndex];
            if (lastItem.ideal) {
                Rectangle bounds = lastItem.internalGetBounds();
                bounds.width = lastItem.preferredWidth;
                bounds.height = lastItem.preferredHeight;
                lastItem.requestedWidth = lastItem.preferredWidth;
                lastItem.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
            }
            if (i == 0) {
                item.wrap = true;
                items[row][0].wrap = false;
            }
            int oldLength = items[row].length;
            CoolItem[] newRow = new CoolItem[oldLength + 1];
            System.arraycopy(items[row], 0, newRow, 0, i);
            newRow[i] = item;
            System.arraycopy(items[row], i, newRow, i + 1, oldLength - i);
            items[row] = newRow;
        }
        item.requestedWidth = CoolItem.MINIMUM_WIDTH;

        int length = originalItems.length;
        CoolItem[] newOriginals = new CoolItem[length + 1];
        System.arraycopy(originalItems, 0, newOriginals, 0, index);
        System.arraycopy(originalItems, index, newOriginals, index + 1, length - index);
        newOriginals[index] = item;
        originalItems = newOriginals;
        layoutItems();

    }

    void destroyItem(CoolItem item) {
        if (inDispose)
            return;
        int row = findItem(item).y;
        if (row == -1)
            return;
        // Rectangle bounds = item.internalGetBounds();
        removeItemFromRow(item, row, true);

        int index = 0;
        while (index < originalItems.length) {
            if (originalItems[index] == item)
                break;
            index++;
        }
        int length = originalItems.length - 1;
        CoolItem[] newOriginals = new CoolItem[length];
        System.arraycopy(originalItems, 0, newOriginals, 0, index);
        System.arraycopy(originalItems, index + 1, newOriginals, index, length - index);
        originalItems = newOriginals;

        // internalRedraw(bounds.x, bounds.y, CoolItem.MINIMUM_WIDTH,
        // bounds.height);
        relayout();
    }

    void moveDown(CoolItem item, int x_root) {
        int oldRowIndex = findItem(item).y;
        boolean resize = false;
        if (items[oldRowIndex].length == 1) {
            resize = true;
            /* If this is the only item in the bottom row, don't move it. */
            if (oldRowIndex == items.length - 1)
                return;
        }
        int newRowIndex = (items[oldRowIndex].length == 1) ? oldRowIndex : oldRowIndex + 1;
        removeItemFromRow(item, oldRowIndex, false);
        // Rectangle old = item.internalGetBounds();
        // internalRedraw(old.x, old.y, CoolItem.MINIMUM_WIDTH, old.height);
        if (newRowIndex == items.length) {
            /* Create a new bottom row for the item. */
            CoolItem[][] newRows = new CoolItem[items.length + 1][];
            System.arraycopy(items, 0, newRows, 0, items.length);
            int row = items.length;
            newRows[row] = new CoolItem[1];
            newRows[row][0] = item;
            items = newRows;
            resize = true;
            item.wrap = true;
        } else {
            insertItemIntoRow(item, newRowIndex, x_root);
        }
        if (resize) {
            relayout();
        } else {
            layoutItems();
        }
    }

    void moveLeft(CoolItem item, int pixels) {
        Point point = findItem(item);
        int row = point.y;
        int index = point.x;
        if (index == 0)
            return;
        Rectangle bounds = item.internalGetBounds();
        int minSpaceOnLeft = 0;
        for (int i = 0; i < index; i++) {
            minSpaceOnLeft += items[row][i].internalGetMinimumWidth();
        }
        int x = Math.max(minSpaceOnLeft, bounds.x - pixels);
        CoolItem left = items[row][index - 1];
        Rectangle leftBounds = left.internalGetBounds();
        if (leftBounds.x + left.internalGetMinimumWidth() > x) {
            int shift = leftBounds.x + left.internalGetMinimumWidth() - x;
            moveLeft(left, shift);
            leftBounds = left.internalGetBounds();
        }
        int leftWidth = Math.max(left.internalGetMinimumWidth(), leftBounds.width - pixels);
        left.setBounds(leftBounds.x, leftBounds.y, leftWidth, leftBounds.height);
        left.requestedWidth = leftWidth;
        int width = bounds.width + (bounds.x - x);
        item.setBounds(x, bounds.y, width, bounds.height);
        item.requestedWidth = width;

        // int damagedWidth = bounds.x - x + CoolItem.MINIMUM_WIDTH;
        // if (damagedWidth > CoolItem.MINIMUM_WIDTH) {
        // internalRedraw(x, bounds.y, damagedWidth, bounds.height);
        // }
    }

    void moveRight(CoolItem item, int pixels) {
        Point point = findItem(item);
        int row = point.y;
        int index = point.x;
        if (index == 0)
            return;
        Rectangle bounds = item.internalGetBounds();
        int minSpaceOnRight = 0;
        for (int i = index; i < items[row].length; i++) {
            minSpaceOnRight += items[row][i].internalGetMinimumWidth();
        }
        int max = getWidth() - minSpaceOnRight;
        int x = Math.min(max, bounds.x + pixels);
        int width = 0;
        if (index + 1 == items[row].length) {
            width = getWidth() - x;
        } else {
            CoolItem right = items[row][index + 1];
            Rectangle rightBounds = right.internalGetBounds();
            if (x + item.internalGetMinimumWidth() > rightBounds.x) {
                int shift = x + item.internalGetMinimumWidth() - rightBounds.x;
                moveRight(right, shift);
                rightBounds = right.internalGetBounds();
            }
            width = rightBounds.x - x;
        }
        item.setBounds(x, bounds.y, width, bounds.height);
        item.requestedWidth = width;
        CoolItem left = items[row][index - 1];
        Rectangle leftBounds = left.internalGetBounds();
        int leftWidth = x - leftBounds.x;
        left.setBounds(leftBounds.x, leftBounds.y, leftWidth, leftBounds.height);
        left.requestedWidth = leftWidth;

        // int damagedWidth = x - bounds.x + CoolItem.MINIMUM_WIDTH
        //     + CoolItem.MARGIN_WIDTH;
        // if (x - bounds.x > 0) {
        // internalRedraw(bounds.x - CoolItem.MARGIN_WIDTH, bounds.y, damagedWidth,
        // bounds.height);
        // }
    }

    void moveUp(CoolItem item, int x_root) {
        Point point = findItem(item);
        int oldRowIndex = point.y;
        boolean resize = false;
        if (items[oldRowIndex].length == 1) {
            resize = true;
            /* If this is the only item in the top row, don't move it. */
            if (oldRowIndex == 0)
                return;
        }
        removeItemFromRow(item, oldRowIndex, false);
        // Rectangle old = item.internalGetBounds();
        // internalRedraw(old.x, old.y, CoolItem.MINIMUM_WIDTH, old.height);
        int newRowIndex = Math.max(0, oldRowIndex - 1);
        if (oldRowIndex == 0) {
            /* Create a new top row for the item. */
            CoolItem[][] newRows = new CoolItem[items.length + 1][];
            System.arraycopy(items, 0, newRows, 1, items.length);
            newRows[0] = new CoolItem[1];
            newRows[0][0] = item;
            items = newRows;
            resize = true;
            item.wrap = true;
        } else {
            insertItemIntoRow(item, newRowIndex, x_root);
        }
        if (resize) {
            relayout();
        } else {
            layoutItems();
        }
    }

    void onDispose() {
        /*
         * Usually when an item is disposed, destroyItem will change the size of the
         * items array and reset the bounds of all the remaining cool items. Since
         * the whole cool bar is being disposed, this is not necessary. For speed
         * the inDispose flag is used to skip over this part of the item dispose.
         */
        if (inDispose)
            return;
        inDispose = true;
        // notifyListeners(SWT.Dispose, event);
        // event.type = SWT.None;
        for (int i = 0; i < items.length; i++) {
            for (int j = 0; j < items[i].length; j++) {
                items[i][j].dispose();
            }
        }
        // hoverCursor.dispose();
        // dragCursor.dispose();
        // cursor = null;
    }

    // void onMouseDown(Event event) {
    // if (isLocked || event.button != 1) return;
    // fixEvent(event);
    // dragging = getGrabbedItem(event.x, event.y);
    // if (dragging != null) {
    // mouseXOffset = event.x;
    // itemXOffset = mouseXOffset - dragging.internalGetBounds().x;
    // _setCursor(dragCursor);
    // }
    // fixEvent(event);
    // }
    // void onMouseExit() {
    // if (dragging == null) _setCursor(null);
    // }
    // void onMouseMove(Event event) {
    // if (isLocked) return;
    // fixEvent(event);
    // CoolItem grabbed = getGrabbedItem(event.x, event.y);
    // if (dragging != null) {
    // int left_root = toDisplay(new Point(event.x, event.y)).x - itemXOffset;
    // Rectangle bounds = dragging.internalGetBounds();
    // if (event.y < bounds.y) {
    // moveUp(dragging, left_root);
    // } else if (event.y > bounds.y + bounds.height){
    // moveDown(dragging, left_root);
    // } else if (event.x < mouseXOffset) {
    // int distance = Math.min(mouseXOffset, bounds.x + itemXOffset) - event.x;
    // if (distance > 0) moveLeft(dragging, distance);
    // } else if (event.x > mouseXOffset) {
    // int distance = event.x - Math.max(mouseXOffset, bounds.x + itemXOffset);
    // if (distance > 0) moveRight(dragging, distance);
    // }
    // mouseXOffset = event.x;
    // } else {
    // if (grabbed != null) {
    // _setCursor(hoverCursor);
    // } else {
    // _setCursor(null);
    // }
    // }
    // fixEvent(event);
    // }
    // void onMouseUp(Event event) {
    // _setCursor(null);
    // dragging = null;
    // }
    // void onMouseDoubleClick(Event event) {
    // if (isLocked) return;
    // dragging = null;
    // fixEvent(event);
    // CoolItem target = getGrabbedItem(event.x, event.y);
    // if (target == null) {
    // _setCursor(null);
    // } else {
    // Point location = findItem(target);
    // int row = location.y;
    // int index = location.x;
    // if (items[row].length > 1) {
    // Rectangle bounds = target.internalGetBounds();
    // int maxSize = getWidth ();
    // for (int i = 0; i < items[row].length; i++) {
    // if (i != index) {
    // maxSize -= items[row][i].internalGetMinimumWidth();
    // }
    // }
    // if (bounds.width == maxSize) {
    // /* The item is at its maximum width. It should be resized to its minimum
    // width. */
    // int distance = bounds.width - target.internalGetMinimumWidth();
    // if (index + 1 < items[row].length) {
    // /* There is an item to the right. Maximize it. */
    // CoolItem right = items[row][index + 1];
    // moveLeft(right, distance);
    // } else {
    // /* There is no item to the right. Move the item all the way right. */
    // moveRight(target, distance);
    // }
    // } else if (bounds.width < target.preferredWidth) {
    // /* The item is less than its preferredWidth. Resize to preferredWidth. */
    // int distance = target.preferredWidth - bounds.width;
    // if (index + 1 < items[row].length) {
    // CoolItem right = items[row][index + 1];
    // moveRight(right, distance);
    // distance = target.preferredWidth - target.internalGetBounds().width;
    // }
    // if (distance > 0) {
    // moveLeft(target, distance);
    // }
    // } else {
    // /* The item is at its minimum width. Maximize it. */
    // for (int i = 0; i < items[row].length; i++) {
    // if (i != index) {
    // CoolItem item = items[row][i];
    // item.requestedWidth = Math.max(item.internalGetMinimumWidth(),
    // CoolItem.MINIMUM_WIDTH);
    // }
    // }
    // target.requestedWidth = maxSize;
    // layoutItems();
    // }
    // _setCursor(hoverCursor);
    // }
    // }
    // fixEvent(event);
    // }
    // void onPaint(Event event) {
    // GC gc = event.gc;
    // if (items.length == 0) return;
    // Color shadowColor = display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
    // Color highlightColor =
    // display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
    // boolean vertical = (style & SWT.VERTICAL) != 0;
    // boolean flat = (style & SWT.FLAT) != 0;
    // int stopX = getWidth();
    // Rectangle rect;
    // Rectangle clipping = gc.getClipping();
    // for (int row = 0; row < items.length; row++) {
    // Rectangle bounds = new Rectangle(0, 0, 0, 0);
    // for (int i = 0; i < items[row].length; i++) {
    // bounds = items[row][i].internalGetBounds();
    // rect = fixRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
    // if (!clipping.intersects(rect)) continue;
    // boolean nativeGripper = false;
    //
    // /* Draw gripper. */
    // if (!isLocked) {
    // rect = fixRectangle(bounds.x, bounds.y, CoolItem.MINIMUM_WIDTH,
    // bounds.height);
    // if (!flat) nativeGripper = drawGripper(rect.x, rect.y, rect.width,
    // rect.height, vertical);
    // if (!nativeGripper) {
    // int grabberTrim = 2;
    // int grabberHeight = bounds.height - (2 * grabberTrim) - 1;
    // gc.setForeground(shadowColor);
    // rect = fixRectangle(
    // bounds.x + CoolItem.MARGIN_WIDTH,
    // bounds.y + grabberTrim,
    // 2,
    // grabberHeight);
    // gc.drawRectangle(rect);
    // gc.setForeground(highlightColor);
    // rect = fixRectangle(
    // bounds.x + CoolItem.MARGIN_WIDTH,
    // bounds.y + grabberTrim + 1,
    // bounds.x + CoolItem.MARGIN_WIDTH,
    // bounds.y + grabberTrim + grabberHeight - 1);
    // gc.drawLine(rect.x, rect.y, rect.width, rect.height);
    // rect = fixRectangle(
    // bounds.x + CoolItem.MARGIN_WIDTH,
    // bounds.y + grabberTrim,
    // bounds.x + CoolItem.MARGIN_WIDTH + 1,
    // bounds.y + grabberTrim);
    // gc.drawLine(rect.x, rect.y, rect.width, rect.height);
    // }
    // }
    //
    // /* Draw separator. */
    // if (!flat && !nativeGripper && i != 0) {
    // gc.setForeground(shadowColor);
    // rect = fixRectangle(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height
    // - 1);
    // gc.drawLine(rect.x, rect.y, rect.width, rect.height);
    // gc.setForeground(highlightColor);
    // rect = fixRectangle(bounds.x + 1, bounds.y, bounds.x + 1, bounds.y +
    // bounds.height - 1);
    // gc.drawLine(rect.x, rect.y, rect.width, rect.height);
    // }
    // }
    // if (!flat && row + 1 < items.length) {
    // /* Draw row separator. */
    // int separatorY = bounds.y + bounds.height;
    // gc.setForeground(shadowColor);
    // rect = fixRectangle(0, separatorY, stopX, separatorY);
    // gc.drawLine(rect.x, rect.y, rect.width, rect.height);
    // gc.setForeground(highlightColor);
    // rect = fixRectangle(0, separatorY + 1, stopX, separatorY + 1);
    // gc.drawLine(rect.x, rect.y, rect.width, rect.height);
    // }
    // }
    // }
    void onResize() {
        layoutItems();
    }

    @Override
    void removeChild(Control control) {
        super.removeChild(control);
        CoolItem[] items = getItems();
        for (int i = 0; i < items.length; i++) {
            CoolItem item = items[i];
            if (item.control == control)
                item.setControl(null);
        }
    }

    /**
     * Remove the item from the row. Adjust the x and width values appropriately.
     */
    void removeItemFromRow(CoolItem item, int rowIndex, boolean disposed) {
        int index = findItem(item).x;
        int newLength = items[rowIndex].length - 1;
        Rectangle itemBounds = item.internalGetBounds();
        item.wrap = false;
        if (newLength > 0) {
            CoolItem[] newRow = new CoolItem[newLength];
            System.arraycopy(items[rowIndex], 0, newRow, 0, index);
            System.arraycopy(items[rowIndex], index + 1, newRow, index, newRow.length - index);
            items[rowIndex] = newRow;
            items[rowIndex][0].wrap = true;
        } else {
            CoolItem[][] newRows = new CoolItem[items.length - 1][];
            System.arraycopy(items, 0, newRows, 0, rowIndex);
            System.arraycopy(items, rowIndex + 1, newRows, rowIndex, newRows.length - rowIndex);
            items = newRows;
            return;
        }
        if (!disposed) {
            if (index == 0) {
                CoolItem first = items[rowIndex][0];
                Rectangle bounds = first.internalGetBounds();
                int width = bounds.x + bounds.width;
                first.setBounds(0, bounds.y, width, bounds.height);
                first.requestedWidth = width;
                // internalRedraw(bounds.x, bounds.y, CoolItem.MINIMUM_WIDTH,
                // bounds.height);
            } else {
                CoolItem previous = items[rowIndex][index - 1];
                Rectangle bounds = previous.internalGetBounds();
                int width = bounds.width + itemBounds.width;
                previous.setBounds(bounds.x, bounds.y, width, bounds.height);
                previous.requestedWidth = width;
            }
        }
    }

    /**
     * Return the height of the bar after it has been properly laid out for the
     * given width.
     */
    int layoutItems() {
        int y = 0, width;
        if ((style & SWT.VERTICAL) != 0) {
            width = getClientArea().height;
        } else {
            width = getClientArea().width;
        }
        wrapItems(width);
        int rowSpacing = (style & SWT.FLAT) != 0 ? 0 : ROW_SPACING;
        for (int row = 0; row < items.length; row++) {
            int count = items[row].length;
            int x = 0;

            /* determine the height and the available width for the row */
            int rowHeight = 0;
            int available = width;
            for (int i = 0; i < count; i++) {
                CoolItem item = items[row][i];
                rowHeight = Math.max(rowHeight, item.preferredHeight);
                available -= item.internalGetMinimumWidth();
            }
            if (row > 0)
                y += rowSpacing;

            /* lay the items out */
            for (int i = 0; i < count; i++) {
                CoolItem child = items[row][i];
                int newWidth = available + child.internalGetMinimumWidth();
                if (i + 1 < count) {
                    newWidth = Math.min(newWidth, child.preferredWidth);
                    available -= (newWidth - child.internalGetMinimumWidth());
                }
                Rectangle oldBounds = child.internalGetBounds();
                Rectangle newBounds = new Rectangle(x, y, newWidth, rowHeight);
                if (!oldBounds.equals(newBounds)) {
                    child.setBounds(newBounds.x, newBounds.y, newBounds.width, newBounds.height);
                    // Rectangle damage = new Rectangle(0, 0, 0, 0);
                    /* Cases are in descending order from most area to redraw to least. */
                    // if (oldBounds.y != newBounds.y) {
                    // damage = newBounds;
                    // damage.add(oldBounds);
                    // /* Redraw the row separator as well. */
                    // damage.y -= rowSpacing;
                    // damage.height += 2 * rowSpacing;
                    // } else if (oldBounds.height != newBounds.height) {
                    // /*
                    // * Draw from the bottom of the gripper to the bottom of the new
                    // area.
                    // * (Bottom of the gripper is -3 from the bottom of the item).
                    // */
                    // damage.y = newBounds.y + Math.min(oldBounds.height,
                    // newBounds.height) - 3;
                    // damage.height = newBounds.y + newBounds.height + rowSpacing;
                    // damage.x = oldBounds.x - CoolItem.MARGIN_WIDTH;
                    // damage.width = oldBounds.width + CoolItem.MARGIN_WIDTH;
                    // } else if (oldBounds.x != newBounds.x) {
                    // /* Redraw only the difference between the separators. */
                    // damage.x = Math.min(oldBounds.x, newBounds.x);
                    // damage.width = Math.abs(oldBounds.x - newBounds.x) +
                    // CoolItem.MINIMUM_WIDTH;
                    // damage.y = oldBounds.y;
                    // damage.height = oldBounds.height;
                    // }
                    // internalRedraw(damage.x, damage.y, damage.width, damage.height);
                }
                x += newWidth;
            }
            y += rowHeight;
        }
        return y;
    }

    void relayout() {
        Point size = getSize();
        int height = layoutItems();
        if ((style & SWT.VERTICAL) != 0) {
            Rectangle trim = computeTrim(0, 0, height, 0);
            if (height != size.x)
                super.setSize(trim.width, size.y);
        } else {
            Rectangle trim = computeTrim(0, 0, 0, height);
            if (height != size.y)
                super.setSize(size.x, trim.height);
        }
    }

    /**
     * Returns an array of zero-relative ints that map the creation order of the
     * receiver's items to the order in which they are currently being displayed.
     * <p>
     * Specifically, the indices of the returned array represent the current
     * visual order of the items, and the contents of the array represent the
     * creation order of the items.
     * </p>
     * <p>
     * Note: This is not the actual structure used by the receiver to maintain its
     * list of items, so modifying the array will not affect the receiver.
     * </p>
     *
     * @return the current visual order of the receiver's items
     *
     * @exception 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[] getItemOrder() {
        checkWidget();
        int count = getItemCount();
        int[] indices = new int[count];
        count = 0;
        for (int i = 0; i < items.length; i++) {
            for (int j = 0; j < items[i].length; j++) {
                CoolItem item = items[i][j];
                int index = 0;
                while (index < originalItems.length) {
                    if (originalItems[index] == item)
                        break;
                    index++;
                }
                if (index == originalItems.length)
                    error(SWT.ERROR_CANNOT_GET_ITEM);
                indices[count++] = index;
            }
        }
        return indices;
    }

    void setItemOrder(int[] itemOrder) {
        if (itemOrder == null)
            error(SWT.ERROR_NULL_ARGUMENT);
        int count = originalItems.length;
        if (itemOrder.length != count)
            error(SWT.ERROR_INVALID_ARGUMENT);

        /* Ensure that itemOrder does not contain any duplicates. */
        boolean[] set = new boolean[count];
        for (int i = 0; i < set.length; i++)
            set[i] = false;
        for (int i = 0; i < itemOrder.length; i++) {
            if (itemOrder[i] < 0 || itemOrder[i] >= count)
                error(SWT.ERROR_INVALID_ARGUMENT);
            if (set[itemOrder[i]])
                error(SWT.ERROR_INVALID_ARGUMENT);
            set[itemOrder[i]] = true;

            //      CoolItem item = getItem( i );
            //      item.setOrder( itemOrder[i] );

        }

        CoolItem[] row = new CoolItem[count];
        for (int i = 0; i < count; i++) {
            row[i] = originalItems[itemOrder[i]];
        }
        items = new CoolItem[1][count];
        items[0] = row;

        layoutItems();
    }

    /**
     * Returns an array of points whose x and y coordinates describe the widths
     * and heights (respectively) of the items in the receiver in the order in
     * which they are currently being displayed.
     *
     * @return the receiver's item sizes in their current visual order
     *
     * @exception 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 Point[] getItemSizes() {
        checkWidget();
        CoolItem[] items = getItems();
        Point[] sizes = new Point[items.length];
        for (int i = 0; i < items.length; i++) {
            sizes[i] = items[i].getSize();
        }
        return sizes;
    }

    void setItemSizes(Point[] sizes) {
        if (sizes == null)
            error(SWT.ERROR_NULL_ARGUMENT);
        CoolItem[] items = getItems();
        if (sizes.length != items.length)
            error(SWT.ERROR_INVALID_ARGUMENT);
        for (int i = 0; i < items.length; i++) {
            items[i].setSize(sizes[i]);
        }
    }

    /**
     * Returns whether or not the receiver is 'locked'. When a coolbar is locked,
     * its items cannot be repositioned.
     *
     * @return true if the coolbar is locked, false otherwise
     *
     * @exception 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 boolean getLocked() {
        checkWidget();
        return isLocked;
    }

    int getWidth() {
        if ((style & SWT.VERTICAL) != 0)
            return getSize().y;
        return getSize().x;
    }

    /**
     * Returns an array of ints that describe the zero-relative indices of any
     * item(s) in the receiver that will begin on a new row. The 0th visible item
     * always begins the first row, therefore it does not count as a wrap index.
     *
     * @return an array containing the receiver's wrap indices, or an empty array
     *         if all items are in one row
     *
     * @exception 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[] getWrapIndices() {
        checkWidget();
        if (items.length <= 1)
            return new int[] {};
        int[] wrapIndices = new int[items.length - 1];
        int i = 0, nextWrap = items[0].length;
        for (int row = 1; row < items.length; row++) {
            if (items[row][0].wrap)
                wrapIndices[i++] = nextWrap;
            nextWrap += items[row].length;
        }
        if (i != wrapIndices.length) {
            int[] tmp = new int[i];
            System.arraycopy(wrapIndices, 0, tmp, 0, i);
            return tmp;
        }
        return wrapIndices;
    }

    /**
     * Sets whether or not the receiver is 'locked'. When a coolbar is locked, its
     * items cannot be repositioned.
     *
     * @param locked
     *          lock the coolbar if true, otherwise unlock the coolbar
     *
     * @exception 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 void setLocked(boolean locked) {
        checkWidget();
        if (isLocked != locked) {
            redraw();
        }
        isLocked = locked;
    }

    /**
     * Sets the indices of all item(s) in the receiver that will begin on a new
     * row. The indices are given in the order in which they are currently being
     * displayed. The 0th item always begins the first row, therefore it does not
     * count as a wrap index. If indices is null or empty, the items will be
     * placed on one line.
     *
     * @param indices
     *          an array of wrap indices, or null
     *
     * @exception 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 void setWrapIndices(int[] indices) {
        checkWidget();
        int[] newIndices = indices;
        if (newIndices == null)
            newIndices = new int[0];
        int count = originalItems.length;
        for (int i = 0; i < newIndices.length; i++) {
            if (newIndices[i] < 0 || newIndices[i] >= count) {
                error(SWT.ERROR_INVALID_ARGUMENT);
            }
        }
        for (int i = 0; i < originalItems.length; i++) {
            originalItems[i].wrap = false;
        }
        for (int i = 0; i < newIndices.length; i++) {
            int index = newIndices[i];
            for (int row = 0; row < items.length; row++) {
                if (items[row].length > index) {
                    items[row][index].wrap = true;
                    break;
                }
                index -= items[row].length;
            }
        }
        relayout();
    }

    // public void setCursor (Cursor cursor) {
    // checkWidget ();
    // super.setCursor (this.cursor = cursor);
    // }
    /**
     * Sets the receiver's item order, wrap indices, and item sizes all at once.
     * This method is typically used to restore the displayed state of the
     * receiver to a previously stored state.
     * <p>
     * The item order is the order in which the items in the receiver should be
     * displayed, given in terms of the zero-relative ordering of when the items
     * were added.
     * </p>
     * <p>
     * The wrap indices are the indices of all item(s) in the receiver that will
     * begin on a new row. The indices are given in the order specified by the
     * item order. The 0th item always begins the first row, therefore it does not
     * count as a wrap index. If wrap indices is null or empty, the items will be
     * placed on one line.
     * </p>
     * <p>
     * The sizes are specified in an array of points whose x and y coordinates
     * describe the new widths and heights (respectively) of the receiver's items
     * in the order specified by the item order.
     * </p>
     *
     * @param itemOrder
     *          an array of indices that describe the new order to display the
     *          items in
     * @param wrapIndices
     *          an array of wrap indices, or null
     * @param sizes
     *          an array containing the new sizes for each of the receiver's items
     *          in visual order
     *
     * @exception 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>
     * @exception IllegalArgumentException
     *              <ul>
     *              <li>ERROR_NULL_ARGUMENT - if item order or sizes is null</li>
     *              <li>ERROR_INVALID_ARGUMENT - if item order or sizes is not
     *              the same length as the number of items</li>
     *              </ul>
     */
    public void setItemLayout(int[] itemOrder, int[] wrapIndices, Point[] sizes) {
        checkWidget();
        setItemOrder(itemOrder);
        setWrapIndices(wrapIndices);
        setItemSizes(sizes);
        relayout();
    }

    void wrapItems(int maxWidth) {
        int itemCount = originalItems.length;
        if (itemCount < 2)
            return;
        CoolItem[] itemsVisual = new CoolItem[itemCount];
        int start = 0;
        for (int row = 0; row < items.length; row++) {
            System.arraycopy(items[row], 0, itemsVisual, start, items[row].length);
            start += items[row].length;
        }
        CoolItem[][] newItems = new CoolItem[itemCount][];
        int rowCount = 0, rowWidth = 0;
        start = 0;
        for (int i = 0; i < itemCount; i++) {
            CoolItem item = itemsVisual[i];
            int itemWidth = item.internalGetMinimumWidth();
            if ((i > 0 && item.wrap) || (maxWidth != SWT.DEFAULT && rowWidth + itemWidth > maxWidth)) {
                if (i == start) {
                    newItems[rowCount] = new CoolItem[1];
                    newItems[rowCount][0] = item;
                    start = i + 1;
                    rowWidth = 0;
                } else {
                    int count = i - start;
                    newItems[rowCount] = new CoolItem[count];
                    System.arraycopy(itemsVisual, start, newItems[rowCount], 0, count);
                    start = i;
                    rowWidth = itemWidth;
                }
                rowCount++;
            } else {
                rowWidth += itemWidth;
            }
        }
        if (start < itemCount) {
            int count = itemCount - start;
            newItems[rowCount] = new CoolItem[count];
            System.arraycopy(itemsVisual, start, newItems[rowCount], 0, count);
            rowCount++;
        }
        if (newItems.length != rowCount) {
            CoolItem[][] tmp = new CoolItem[rowCount][];
            System.arraycopy(newItems, 0, tmp, 0, rowCount);
            items = tmp;
        } else {
            items = newItems;
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getAdapter(Class<T> adapter) {
        if (adapter == IItemHolderAdapter.class) {
            if (itemHolder == null) {
                itemHolder = new CoolBarItemHolder();
            }
            return (T) itemHolder;
        }
        if (adapter == ICoolBarAdapter.class) {
            if (coolBarAdapter == null) {
                coolBarAdapter = new CoolBarAdapter();
            }
            return (T) coolBarAdapter;
        }
        if (adapter == WidgetLCA.class) {
            return (T) CoolBarLCA.INSTANCE;
        }
        return super.getAdapter(adapter);
    }

    ///////////////////
    // Skinning support

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

}