/*******************************************************************************
 * Copyright (c) 2000, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal.presentations.r21.widgets;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.accessibility.ACC;
import org.eclipse.swt.accessibility.Accessible;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleControlAdapter;
import org.eclipse.swt.accessibility.AccessibleControlEvent;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.TypedListener;

/**
 * Instances of this class implement the notebook user interface
 * metaphor.  It allows the user to select a notebook page from
 * set of pages.
 * <p>
 * The item children that may be added to instances of this class
 * must be of type <code>CTabItem</code>.
 * <code>Control</code> children are created and then set into a
 * tab item using <code>CTabItem#setControl</code>.
 * </p><p>
 * Note that although this class is a subclass of <code>Composite</code>,
 * it does not make sense to set a layout on it.
 * </p><p>
 * <dl>
 * <dt><b>Styles:</b></dt>
 * <dd>TOP, BOTTOM, FLAT</dd>
 * <dt><b>Events:</b></dt>
 * <dd>Selection</dd>
 * <dd>"CTabFolder"</dd>
 * </dl>
 * <p>
 * Note: Only one of the styles TOP and BOTTOM 
 * may be specified.
 * </p><p>
 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
 * </p>
 */

public class CTabFolder extends Composite {

    /**
     * marginWidth specifies the number of pixels of horizontal margin
     * that will be placed along the left and right edges of the form.
     *
     * The default value is 0.
     */
    public int marginWidth = 0;

    /**
     * marginHeight specifies the number of pixels of vertical margin
     * that will be placed along the top and bottom edges of the form.
     *
     * The default value is 0.
     */
    public int marginHeight = 0;

    /**
     * Color of innermost line of drop shadow border.
     */
    public static RGB borderInsideRGB = new RGB(132, 130, 132);

    /**
     * Color of middle line of drop shadow border.
     */
    public static RGB borderMiddleRGB = new RGB(143, 141, 138);

    /**
     * Color of outermost line of drop shadow border.
     */
    public static RGB borderOutsideRGB = new RGB(171, 168, 165);

    /*
     * A multiple of the tab height that specifies the minimum width to which a tab 
     * will be compressed before scrolling arrows are used to navigate the tabs.
     */
    public int MIN_TAB_WIDTH = 3;

    /* sizing, positioning */
    int xClient, yClient;

    boolean onBottom = false;

    boolean fixedTabHeight;

    int tabHeight;

    /* item management */
    private CTabItem items[] = new CTabItem[0];

    private int selectedIndex = -1;

    int topTabIndex = -1; // index of the left most visible tab.

    /* External Listener management */
    private CTabFolderListener[] tabListeners = new CTabFolderListener[0];

    /* Color appearance */
    Image backgroundImage;

    Color[] gradientColors;

    int[] gradientPercents;

    Color selectionForeground;

    Color background;

    // internal constants
    private static final int DEFAULT_WIDTH = 64;

    private static final int DEFAULT_HEIGHT = 64;

    // scrolling arrows
    private ToolBar arrowBar;

    private Image arrowLeftImage;

    private Image arrowRightImage;

    private Control topRight;

    // close button
    boolean showClose = false;

    private Image closeImage;

    ToolBar closeBar;

    private ToolBar inactiveCloseBar;

    private CTabItem inactiveItem;

    // borders
    boolean showBorders = false;

    private int borderBottom = 0;

    private int borderLeft = 0;

    private int borderRight = 0;

    private int borderTop = 0;

    private Color borderColor1;

    private Color borderColor2;

    private Color borderColor3;

    // when disposing CTabFolder, don't try to layout the items or 
    // change the selection as each child is destroyed.
    private boolean inDispose = false;

    // keep track of size changes in order to redraw only affected area
    // on Resize
    private Point oldSize;

    private Font oldFont;

    // insertion marker
    int insertionIndex = -2; // Index of insert marker.  Marker always shown after index.

    // -2 means no insert marker

    // tool tip
    private Shell tip;

    private Label label;

    private boolean showToolTip = false;

    private CTabItem toolTipItem;

    /**
     * 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 widget which will be the parent of the new instance (cannot be null)
     * @param style the style of widget to construct
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
     * </ul>
     * @exception SWTException <ul>
     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
     * </ul>
     *
     * @see SWT#TOP
     * @see SWT#BOTTOM
     * @see SWT#FLAT
     * @see #getStyle()
     */
    public CTabFolder(Composite parent, int style) {
        super(parent, checkStyle(style));

        onBottom = (getStyle() & SWT.BOTTOM) != 0;

        borderColor1 = new Color(getDisplay(), borderInsideRGB);
        borderColor2 = new Color(getDisplay(), borderMiddleRGB);
        borderColor3 = new Color(getDisplay(), borderOutsideRGB);

        // tool tip support
        tip = new Shell(getShell(), SWT.ON_TOP);
        label = new Label(tip, SWT.CENTER);

        // Add all listeners
        Listener listener = new Listener() {
            public void handleEvent(Event event) {
                switch (event.type) {
                case SWT.Dispose:
                    onDispose();
                    break;
                case SWT.Paint:
                    onPaint(event);
                    break;
                case SWT.Resize:
                    onResize();
                    break;
                case SWT.MouseDoubleClick:
                    onMouseDoubleClick(event);
                    break;
                case SWT.MouseDown:
                    onMouseDown(event);
                    break;
                case SWT.MouseExit:
                    onMouseExit(event);
                    break;
                case SWT.MouseHover:
                    onMouseHover(event);
                    break;
                case SWT.MouseMove:
                    onMouseMove(event);
                    break;
                case SWT.FocusIn:
                    onFocus(event);
                    break;
                case SWT.FocusOut:
                    onFocus(event);
                    break;
                case SWT.KeyDown:
                    onKeyDown(event);
                    break;
                case SWT.Traverse:
                    onTraverse(event);
                    break;
                }
            }
        };

        int[] folderEvents = new int[] { SWT.Dispose, SWT.Paint, SWT.Resize,
                SWT.MouseDoubleClick, SWT.MouseDown, SWT.MouseExit,
                SWT.MouseHover, SWT.MouseMove, SWT.FocusIn, SWT.FocusOut,
                SWT.KeyDown, SWT.Traverse, };
        for (int i = 0; i < folderEvents.length; i++) {
            addListener(folderEvents[i], listener);
        }

        createArrowBar();
        createCloseBar();

        setBorderVisible((style & SWT.BORDER) != 0);

        initAccessible();

    }

    private static int checkStyle(int style) {
        int mask = SWT.TOP | SWT.BOTTOM | SWT.FLAT | SWT.LEFT_TO_RIGHT
                | SWT.RIGHT_TO_LEFT;
        style = style & mask;
        // TOP and BOTTOM are mutually exlusive.
        // TOP is the default
        if ((style & SWT.TOP) != 0) {
			style = style & ~(SWT.TOP | SWT.BOTTOM) | SWT.TOP;
		}
        // reduce the flash by not redrawing the entire area on a Resize event
        style |= SWT.NO_REDRAW_RESIZE;
        return style;
    }

    /**	 
     * Adds the listener to receive events.
     * <p>
     *
     * @param listener the listener
     *
     * @exception SWTError <ul>
     *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 		<li>ERROR_NULL_ARGUMENT when listener is null</li>
     *	</ul>
     */
    public void addSelectionListener(SelectionListener listener) {
        checkWidget();
        if (listener == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        TypedListener typedListener = new TypedListener(listener);
        addListener(SWT.Selection, typedListener);
        addListener(SWT.DefaultSelection, typedListener);
    }

    /**
     * Adds the listener to the collection of listeners who will
     * be notified when a tab item is closed.
     *
     * @param listener the listener which should be notified
     *
     * @exception IllegalArgumentException <ul>
     *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     *      <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     *
     * @see CTabFolderListener
     * @see #removeCTabFolderListener
     */
    public void addCTabFolderListener(CTabFolderListener listener) {
        checkWidget();
        if (listener == null) {
			SWT.error(SWT.ERROR_NULL_ARGUMENT);
		}
        // add to array
        CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1];
        System.arraycopy(tabListeners, 0, newTabListeners, 0,
                tabListeners.length);
        tabListeners = newTabListeners;
        tabListeners[tabListeners.length - 1] = listener;
        showClose = true;
        setButtonBounds();
    }

    private void closeNotify(CTabItem item, int time) {
        if (item == null) {
			return;
		}

        CTabFolderEvent event = new CTabFolderEvent(this);
        event.widget = this;
        event.time = time;
        event.item = item;
        event.doit = true;
        if (tabListeners != null) {
            for (int i = 0; i < tabListeners.length; i++) {
                tabListeners[i].itemClosed(event);
            }
        }
        if (event.doit) {
            item.dispose();
        }
    }

    public Point computeSize(int wHint, int hHint, boolean changed) {
        checkWidget();
        int minWidth = 0;
        int minHeight = 0;

        // preferred width of tab area to show all tabs
        GC gc = new GC(this);
        for (int i = 0; i < items.length; i++) {
            minWidth += items[i].preferredWidth(gc);
        }
        gc.dispose();

        // preferred size of controls in tab items
        for (int i = 0; i < items.length; i++) {
            Control control = items[i].getControl();
            if (control != null && !control.isDisposed()) {
                Point size = control.computeSize(wHint, hHint);
                minWidth = Math.max(minWidth, size.x);
                minHeight = Math.max(minHeight, size.y);
            }
        }
        if (minWidth == 0) {
			minWidth = DEFAULT_WIDTH;
		}
        if (minHeight == 0) {
			minHeight = DEFAULT_HEIGHT;
		}

        if (wHint != SWT.DEFAULT) {
			minWidth = wHint;
		}
        if (hHint != SWT.DEFAULT) {
			minHeight = hHint;
		}

        Rectangle trim = computeTrim(0, 0, minWidth, minHeight);
        return new Point(trim.width, trim.height);
    }

    public Rectangle computeTrim(int x, int y, int width, int height) {
        checkWidget();
        if (items.length == 0) {
            if (!showBorders) {
				return new Rectangle(x, y, width, height);
			}
            int trimX = x - borderRight - 1;
            int trimY = y - borderBottom - 1;
            int trimWidth = width + borderRight + 2;
            int trimHeight = height + borderBottom + 2;
            return new Rectangle(trimX, trimY, trimWidth, trimHeight);
        } else {
            int trimX = x - marginWidth - borderLeft;
            int trimY = y - marginHeight - tabHeight - borderTop - 1;
            // -1 is for the line at the bottom of the tabs
            if (onBottom) {
                trimY = y - marginHeight - borderTop;
            }
            int trimWidth = width + borderLeft + borderRight + 2 * marginWidth;
            int trimHeight = height + borderTop + borderBottom + 2
                    * marginHeight + tabHeight + 1;
            return new Rectangle(trimX, trimY, trimWidth, trimHeight);
        }
    }

    /**
     * Create the specified item at 'index'.
     */
    void createItem(CTabItem item, int index) {
        if (0 > index || index > getItemCount()) {
            SWT.error(SWT.ERROR_INVALID_RANGE);
        }
        // grow by one and rearrange the array.
        CTabItem[] newItems = new CTabItem[items.length + 1];
        System.arraycopy(items, 0, newItems, 0, index);
        newItems[index] = item;
        System.arraycopy(items, index, newItems, index + 1, items.length
                - index);
        items = newItems;

        item.parent = this;

        if (selectedIndex >= index) {
            selectedIndex++;
        }
        if (items.length == 1) {
            topTabIndex = 0;
            resetTabSize(true);
        } else {
            setItemBounds();
            showItem(item);
        }

        if (items.length == 1) {
            redraw();
        } else {
            redrawTabArea(-1);
        }
    }

    private void createArrowBar() {
        // create arrow buttons for scrolling 
        arrowBar = new ToolBar(this, SWT.FLAT);
        arrowBar.setVisible(false);
        arrowBar.setBackground(background);
        ToolItem scrollLeft = new ToolItem(arrowBar, SWT.PUSH);
        scrollLeft.setEnabled(false);
        ToolItem scrollRight = new ToolItem(arrowBar, SWT.PUSH);
        scrollRight.setEnabled(false);

        scrollLeft.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                scroll_scrollLeft();
            }
        });
        scrollRight.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                scroll_scrollRight();
            }
        });

    }

    private void createCloseBar() {
        closeBar = new ToolBar(this, SWT.FLAT);
        closeBar.setVisible(false);
        if (gradientColors != null && gradientColors.length > 0) {
            closeBar.setBackground(gradientColors[gradientColors.length - 1]);
        } else {
            closeBar.setBackground(background);
        }
        ToolItem closeItem = new ToolItem(closeBar, SWT.PUSH);

        inactiveCloseBar = new ToolBar(this, SWT.FLAT);
        inactiveCloseBar.setVisible(false);
        inactiveCloseBar.setBackground(background);
        ToolItem inactiveCloseItem = new ToolItem(inactiveCloseBar, SWT.PUSH);

        closeItem.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                closeNotify(getSelection(), event.time);
            }
        });
        inactiveCloseItem.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                closeNotify(inactiveItem, event.time);
                inactiveCloseBar.setVisible(false);
                inactiveItem = null;
            }
        });
        inactiveCloseBar.addListener(SWT.MouseExit, new Listener() {
            public void handleEvent(Event event) {
                if (inactiveItem != null) {
                    Rectangle itemBounds = inactiveItem.getBounds();
                    if (itemBounds.contains(event.x, event.y)) {
						return;
					}
                }
                inactiveCloseBar.setVisible(false);
                inactiveItem = null;
            }
        });

    }

    /**
     * Destroy the specified item.
     */
    void destroyItem(CTabItem item) {
        if (inDispose) {
			return;
		}

        int index = indexOf(item);
        if (index == -1) {
			return; // should this trigger an error?
		}

        insertionIndex = -2;

        if (items.length == 1) {
            items = new CTabItem[0];
            selectedIndex = -1;
            topTabIndex = 0;

            Control control = item.getControl();
            if (control != null && !control.isDisposed()) {
                control.setVisible(false);
            }
            closeBar.setVisible(false);
            if (!fixedTabHeight) {
				tabHeight = 0;
			}
            redraw();
            return;
        }

        // shrink by one and rearrange the array.
        CTabItem[] newItems = new CTabItem[items.length - 1];
        System.arraycopy(items, 0, newItems, 0, index);
        System.arraycopy(items, index + 1, newItems, index, items.length
                - index - 1);
        items = newItems;

        if (topTabIndex == items.length) {
            --topTabIndex;
        }

        // move the selection if this item is selected
        if (selectedIndex == index) {
            Control control = item.getControl();
            if (control != null && !control.isDisposed()) {
                control.setVisible(false);
            }
            selectedIndex = -1;
            setSelection(Math.max(0, index - 1), true);
        } else if (selectedIndex > index) {
            selectedIndex--;
        }

        setItemBounds();
        redrawTabArea(-1);
    }

    private void onKeyDown(Event e) {
        if (e.keyCode != SWT.ARROW_LEFT && e.keyCode != SWT.ARROW_RIGHT) {
			return;
		}
        int leadKey = (getStyle() & SWT.MIRRORED) != 0 ? SWT.ARROW_RIGHT
                : SWT.ARROW_LEFT;
        if (e.keyCode == leadKey) {
            if (selectedIndex > 0) {
                setSelection(selectedIndex - 1, true);
            }
        } else {
            if (selectedIndex < items.length - 1) {
                setSelection(selectedIndex + 1, true);
            }
        }
    }

    /**
     * Dispose the items of the receiver
     */
    private void onDispose() {
        /*
         * Usually when an item is disposed, destroyItem will change the size of the items array, 
         * reset the bounds of all the tabs and manage the widget associated with the tab.
         * Since the whole folder is being disposed, this is not necessary.  For speed
         * the inDispose flag is used to skip over this part of the item dispose.
         */
        inDispose = true;

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

        // clean up resources
        if (tip != null && !tip.isDisposed()) {
            tip.dispose();
            tip = null;
            label = null;
        }

        if (arrowLeftImage != null) {
			arrowLeftImage.dispose();
		}
        arrowLeftImage = null;
        if (arrowRightImage != null) {
			arrowRightImage.dispose();
		}
        arrowRightImage = null;
        if (closeImage != null) {
			closeImage.dispose();
		}
        closeImage = null;

        gradientColors = null;
        gradientPercents = null;
        backgroundImage = null;

        if (borderColor1 != null) {
			borderColor1.dispose();
		}
        borderColor1 = null;

        if (borderColor2 != null) {
			borderColor2.dispose();
		}
        borderColor2 = null;

        if (borderColor3 != null) {
			borderColor3.dispose();
		}
        borderColor3 = null;
    }

    private void onFocus(Event e) {
        checkWidget();
        if (selectedIndex >= 0) {
            redrawTabArea(selectedIndex);
        } else {
            setSelection(0, true);
        }
    }

    /** 
     * Draw a border around the receiver.
     */
    private void drawBorder(GC gc) {

        Rectangle d = super.getClientArea();

        if (showBorders) {
            if ((getStyle() & SWT.FLAT) != 0) {
                gc.setForeground(borderColor1);
                gc.drawRectangle(d.x, d.y, d.x + d.width - 1, d.y + d.height
                        - 1);
            } else {
                gc.setForeground(borderColor1);
                gc.drawRectangle(d.x, d.y, d.x + d.width - 3, d.y + d.height
                        - 3);

                gc.setForeground(borderColor2);
                gc.drawLine(d.x + 1, d.y + d.height - 2, d.x + d.width - 1, d.y
                        + d.height - 2);
                gc.drawLine(d.x + d.width - 2, d.y + 1, d.x + d.width - 2, d.y
                        + d.height - 1);

                gc.setForeground(borderColor3);
                gc.drawLine(d.x + 2, d.y + d.height - 1, d.x + d.width - 2, d.y
                        + d.height - 1);
                gc.drawLine(d.x + d.width - 1, d.y + 2, d.x + d.width - 1, d.y
                        + d.height - 2);

                // fill in corners with parent's background
                gc.setForeground(getParent().getBackground());
                gc.drawLine(d.x + d.width - 2, d.y, d.x + d.width - 1, d.y);
                gc.drawLine(d.x + d.width - 1, d.y + 1, d.x + d.width - 1,
                        d.y + 1);

                gc.drawLine(d.x, d.y + d.height - 2, d.x, d.y + d.height - 2);
                gc.drawLine(d.x, d.y + d.height - 1, d.x + 1, d.y + d.height
                        - 1);

                gc.drawLine(d.x + d.width - 1, d.y + d.height - 1, d.x
                        + d.width - 1, d.y + d.height - 1);
            }

        }

        // draw a separator line
        if (items.length > 0) {
            int lineY = d.y + borderTop + tabHeight;
            if (onBottom) {
                lineY = d.y + d.height - borderBottom - tabHeight - 1;
            }
            gc.setForeground(borderColor1);
            gc.drawLine(d.x + borderLeft, lineY, d.x + d.width - borderRight,
                    lineY);
        }

        gc.setForeground(getForeground());
    }

    public Rectangle getClientArea() {
        checkWidget();
        Point size = getSize();
        if (items.length == 0) {
            if (!showBorders) {
				return super.getClientArea();
			}
            int width = size.x - borderRight - 2;
            int height = size.y - borderBottom - 2;
            return new Rectangle(borderRight + 1, borderBottom + 1, width,
                    height);
        } else {
            int width = size.x - 2 * marginWidth - borderLeft - borderRight;
            int height = size.y - 2 * marginHeight - borderTop - borderBottom
                    - tabHeight - 1;
            return new Rectangle(xClient, yClient, width, height);
        }
    }

    /**
     * Returns the height of the tab
     * 
     * @return the height of the tab
     * 
     * @exception SWTError <ul>
     *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     *	</ul>
     */
    public int getTabHeight() {
        checkWidget();
        return tabHeight;
    }

    /**
     * Return the tab that is located at the specified index.
     * 
     * @return the item at the specified index
     */
    public CTabItem getItem(int index) {
        //checkWidget();
        if (index < 0 || index >= items.length) {
			SWT.error(SWT.ERROR_INVALID_RANGE);
		}
        return items[index];
    }

    /**
     * Gets the item at a point in the widget.
     * <p>
     *
     * @return the item at a point
     */
    public CTabItem getItem(Point pt) {
        //checkWidget();
        if (items.length == 0) {
			return null;
		}
        int lastItem = getLastItem();
        lastItem = Math.min(items.length - 1, lastItem + 1);
        for (int i = topTabIndex; i <= lastItem; i++) {
            Rectangle bounds = items[i].getBounds();
            if (bounds.contains(pt)) {
				return items[i];
			}
        }
        return null;
    }

    /**
     * Return the number of tabs in the folder.
     * 
     * @return the number of tabs in the folder
     */
    public int getItemCount() {
        //checkWidget();
        return items.length;
    }

    /**
     * Return the tab items.
     * 
     * @return the tab items
     */
    public CTabItem[] getItems() {
        //checkWidget();
        CTabItem[] tabItems = new CTabItem[items.length];
        System.arraycopy(items, 0, tabItems, 0, items.length);
        return tabItems;
    }

    private int getLastItem() {
        if (items.length == 0) {
			return -1;
		}
        Rectangle area = getClientArea();
        if (area.width <= 0) {
			return 0;
		}
        Rectangle toolspace = getToolSpace();
        if (toolspace.width == 0) {
			return items.length - 1;
		}
        int width = area.width - toolspace.width;
        int index = topTabIndex;
        int tabWidth = items[index].width;
        while (index < items.length - 1) {
            tabWidth += items[index + 1].width;
            if (tabWidth > width) {
				break;
			}
            index++;
        }
        return index;
    }

    /**
     * Return the selected tab item, or an empty array if there
     * is no selection.
     * 
     * @return the selected tab item
     */
    public CTabItem getSelection() {
        //checkWidget();
        if (selectedIndex == -1) {
			return null;
		}
        return items[selectedIndex];
    }

    /**
     * Return the index of the selected tab item, or -1 if there
     * is no selection.
     * 
     * @return the index of the selected tab item or -1
     */
    public int getSelectionIndex() {
        //checkWidget();
        return selectedIndex;
    }

    private Rectangle getToolSpace() {
        boolean showArrows = scroll_leftVisible() || scroll_rightVisible();
        if (!showArrows && topRight == null) {
			return new Rectangle(0, 0, 0, 0);
		}
        Rectangle toolspace;
        if (showArrows) {
            toolspace = arrowBar.getBounds();
            toolspace.width += borderRight;
            if (topRight != null) {
				toolspace.width += topRight.getSize().x;
			}
        } else {
            toolspace = topRight.getBounds();
            toolspace.width += borderRight;
        }
        return toolspace;
    }

    /**
     * Returns the control in the top right corner of the tab folder. 
     * Typically this is a close button or a composite with a menu and close button.
     *
     * @since 2.1
     *
     * @return the control in the top right corner of the tab folder or null
     * 
     * @exception  SWTError <ul>
     *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     *	</ul>
     */
    public Control getTopRight() {
        checkWidget();
        return topRight;
    }

    /**
     * Return the index of the specified tab or -1 if the tab is not 
     * in the receiver.
     * 
     * @return the index of the specified tab item or -1
     * 
     * @exception SWTError <ul>
     *      <li>ERROR_NULL_ARGUMENT when the item is null</li>
     *	</ul>
     */
    public int indexOf(CTabItem item) {
        //checkWidget();
        if (item == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        for (int i = 0; i < items.length; i++) {
            if (items[i] == item) {
				return i;
			}
        }
        return -1;
    }

    private void initAccessible() {
        final Accessible accessible = getAccessible();
        accessible.addAccessibleListener(new AccessibleAdapter() {
            public void getName(AccessibleEvent e) {
                String name = null;
                int childID = e.childID;
                if (childID >= 0 && childID < items.length) {
                    name = items[childID].getText();
                    int index = name.indexOf('&');
                    if (index > 0) {
                        name = name.substring(0, index)
                                + name.substring(index + 1);
                    }
                }
                e.result = name;
            }

            public void getHelp(AccessibleEvent e) {
                String help = null;
                int childID = e.childID;
                if (childID == ACC.CHILDID_SELF) {
                    help = getToolTipText();
                } else if (childID >= 0 && childID < items.length) {
                    help = items[childID].getToolTipText();
                }
                e.result = help;
            }

            public void getKeyboardShortcut(AccessibleEvent e) {
                String shortcut = null;
                int childID = e.childID;
                if (childID >= 0 && childID < items.length) {
                    String text = items[childID].getText();
                    if (text != null) {
                        char mnemonic = getMnemonic(text);
                        if (mnemonic != '\0') {
                            shortcut = "Alt+" + mnemonic; //$NON-NLS-1$
                        }
                    }
                }
                e.result = shortcut;
            }
        });

        accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
            public void getChildAtPoint(AccessibleControlEvent e) {
                Point testPoint = toControl(new Point(e.x, e.y));
                int childID = ACC.CHILDID_NONE;
                for (int i = 0; i < items.length; i++) {
                    if (items[i].getBounds().contains(testPoint)) {
                        childID = i;
                        break;
                    }
                }
                if (childID == ACC.CHILDID_NONE) {
                    Rectangle location = getBounds();
                    location.height = location.height - getClientArea().height;
                    if (location.contains(testPoint)) {
                        childID = ACC.CHILDID_SELF;
                    }
                }
                e.childID = childID;
            }

            public void getLocation(AccessibleControlEvent e) {
                Rectangle location = null;
                int childID = e.childID;
                if (childID == ACC.CHILDID_SELF) {
                    location = getBounds();
                }
                if (childID >= 0 && childID < items.length) {
                    location = items[childID].getBounds();
                }
                if (location != null) {
                    Point pt = toDisplay(new Point(location.x, location.y));
                    e.x = pt.x;
                    e.y = pt.y;
                    e.width = location.width;
                    e.height = location.height;
                }
            }

            public void getChildCount(AccessibleControlEvent e) {
                e.detail = items.length;
            }

            public void getDefaultAction(AccessibleControlEvent e) {
                String action = null;
                int childID = e.childID;
                if (childID >= 0 && childID < items.length) {
                    action = "Switch"; //$NON-NLS-1$
                }
                e.result = action;
            }

            public void getFocus(AccessibleControlEvent e) {
                int childID = ACC.CHILDID_NONE;
                if (isFocusControl()) {
                    if (selectedIndex == -1) {
                        childID = ACC.CHILDID_SELF;
                    } else {
                        childID = selectedIndex;
                    }
                }
                e.childID = childID;
            }

            public void getRole(AccessibleControlEvent e) {
                int role = 0;
                int childID = e.childID;
                if (childID == ACC.CHILDID_SELF) {
                    role = ACC.ROLE_TABFOLDER;
                } else if (childID >= 0 && childID < items.length) {
                    role = ACC.ROLE_TABITEM;
                }
                e.detail = role;
            }

            public void getSelection(AccessibleControlEvent e) {
                e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE
                        : selectedIndex;
            }

            public void getState(AccessibleControlEvent e) {
                int state = 0;
                int childID = e.childID;
                if (childID == ACC.CHILDID_SELF) {
                    state = ACC.STATE_NORMAL;
                } else if (childID >= 0 && childID < items.length) {
                    state = ACC.STATE_SELECTABLE;
                    if (isFocusControl()) {
                        state |= ACC.STATE_FOCUSABLE;
                    }
                    if (selectedIndex == childID) {
                        state |= ACC.STATE_SELECTED;
                        if (isFocusControl()) {
                            state |= ACC.STATE_FOCUSED;
                        }
                    }
                }
                e.detail = state;
            }

            public void getChildren(AccessibleControlEvent e) {
                Object[] children = new Object[items.length];
                for (int i = 0; i < items.length; i++) {
                    children[i] = new Integer(i);
                }
                e.children = children;
            }
        });

        addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                if (isFocusControl()) {
                    if (selectedIndex == -1) {
                        accessible.setFocus(ACC.CHILDID_SELF);
                    } else {
                        accessible.setFocus(selectedIndex);
                    }
                }
            }
        });

        addListener(SWT.FocusIn, new Listener() {
            public void handleEvent(Event event) {
                if (selectedIndex == -1) {
                    accessible.setFocus(ACC.CHILDID_SELF);
                } else {
                    accessible.setFocus(selectedIndex);
                }
            }
        });
    }

    private void setButtonBounds() {

        updateArrowBar();
        updateCloseBar();

        Rectangle area = super.getClientArea();

        int offset = 0;
        if (topRight != null) {
            Point size = topRight.computeSize(SWT.DEFAULT, tabHeight);
            int x = area.x + area.width - borderRight - size.x;
            int y = onBottom ? area.y + area.height - borderBottom - size.y
                    : area.y + borderTop;
            topRight.setBounds(x, y, size.x, size.y);
            offset = size.x;
        }
        boolean leftVisible = scroll_leftVisible();
        boolean rightVisible = scroll_rightVisible();
        if (leftVisible || rightVisible) {
            Point size = arrowBar.computeSize(SWT.DEFAULT, tabHeight);
            int x = area.x + area.width - borderRight - size.x - offset;
            int y = (onBottom) ? area.y + area.height - borderBottom - size.y
                    : area.y + borderTop;

            arrowBar.setBounds(x, y, size.x, size.y);
            ToolItem[] items = arrowBar.getItems();
            items[0].setEnabled(leftVisible);
            items[1].setEnabled(rightVisible);
            arrowBar.setVisible(true);
        } else {
            arrowBar.setVisible(false);
        }

        // When the close button is right at the edge of the Tab folder, hide it because
        // otherwise it may block off a part of the border on the right
        if (showClose) {
            inactiveCloseBar.setVisible(false);
            CTabItem item = getSelection();
            if (item == null) {
                closeBar.setVisible(false);
            } else {
                int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN
                        - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
                Point size = closeBar.computeSize(SWT.DEFAULT, toolbarHeight);
                int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
                int y = item.y + Math.max(0, (item.height - toolbarHeight) / 2);
                closeBar.setBounds(x, y, size.x, toolbarHeight);
                Rectangle toolspace = getToolSpace();
                Point folderSize = getSize();
                boolean visible = (toolspace.width == 0 || x < toolspace.x)
                        && x + size.x < folderSize.x - borderRight;
                closeBar.setVisible(visible);
            }
        }
    }

    private boolean setItemLocation() {
        if (items.length == 0) {
			return false;
		}
        Rectangle area = super.getClientArea();
        int x = area.x;
        int y = area.y + borderTop;
        if (onBottom) {
			y = Math.max(0, area.y + area.height - borderBottom - tabHeight);
		}

        boolean changed = false;
        for (int i = topTabIndex - 1; i >= 0; i--) {
            // if the first visible tab is not the first tab
            CTabItem tab = items[i];
            x -= tab.width;
            if (!changed && (tab.x != x || tab.y != y)) {
				changed = true;
			}
            // layout tab items from right to left thus making them invisible
            tab.x = x;
            tab.y = y;
        }

        x = area.x + borderLeft;
        for (int i = topTabIndex; i < items.length; i++) {
            // continue laying out remaining, visible items left to right 
            CTabItem tab = items[i];
            tab.x = x;
            tab.y = y;
            x = x + tab.width;
        }
        setButtonBounds();
        return changed;
    }

    private void setLastItem(int index) {
        if (index < 0 || index > items.length - 1) {
			return;
		}
        Rectangle area = getClientArea();
        if (area.width <= 0) {
			return;
		}
        int maxWidth = area.width;
        Rectangle toolspace = getToolSpace();
        if (toolspace.width > 0) {
            maxWidth -= toolspace.width;
        }
        int tabWidth = items[index].width;
        while (index > 0) {
            tabWidth += items[index - 1].width;
            if (tabWidth > maxWidth) {
				break;
			}
            index--;
        }
        topTabIndex = index;
        setItemLocation();
        redrawTabArea(-1);
    }

    /**
     * Layout the items and store the client area size.
     */
    boolean setItemBounds() {
        boolean changed = false;
        if (isDisposed()) {
			return changed;
		}
        Rectangle area = super.getClientArea();

        xClient = area.x + borderLeft + marginWidth;
        if (onBottom) {
            yClient = area.y + borderTop + marginHeight;
        } else {
            yClient = area.y + borderTop + tabHeight + 1 + marginHeight;
            // +1 is for the line at the bottom of the tabs
        }

        if (area.width <= 0 || area.height <= 0 || items.length == 0) {
			return changed;
		}

        int[] widths = new int[items.length];
        GC gc = new GC(this);
        for (int i = 0; i < items.length; i++) {
            widths[i] = items[i].preferredWidth(gc);
        }
        gc.dispose();

        int oldAverageWidth = 0;
        int averageWidth = (area.width - borderLeft - borderRight)
                / items.length;
        while (averageWidth > oldAverageWidth) {
            int width = area.width - borderLeft - borderRight;
            int count = items.length;
            for (int i = 0; i < items.length; i++) {
                if (widths[i] < averageWidth) {
                    width -= widths[i];
                    count--;
                }
            }
            oldAverageWidth = averageWidth;
            if (count > 0) {
                averageWidth = width / count;
            }
        }
        averageWidth = Math.max(averageWidth, MIN_TAB_WIDTH * tabHeight);
        for (int i = 0; i < items.length; i++) {
            if (widths[i] > averageWidth) {
                widths[i] = averageWidth;
            }
        }

        int totalWidth = 0;
        for (int i = 0; i < items.length; i++) {
            CTabItem tab = items[i];
            if (tab.height != tabHeight || tab.width != widths[i]) {
				changed = true;
			}
            tab.height = tabHeight;
            tab.width = widths[i];
            totalWidth += widths[i];
        }

        int areaWidth = area.x + area.width - borderRight;
        if (totalWidth <= areaWidth) {
            topTabIndex = 0;
        }
        if (setItemLocation()) {
			changed = true;
		}

        // Is there a gap after last item showing
        if (correctLastItem()) {
			changed = true;
		}
        return changed;
    }

    private boolean onMnemonic(Event event) {
        char key = event.character;
        for (int i = 0; i < items.length; i++) {
            if (items[i] != null) {
                char mnemonic = getMnemonic(items[i].getText());
                if (mnemonic != '\0') {
                    if (Character.toUpperCase(key) == Character
                            .toUpperCase(mnemonic)) {
                        setSelection(i, true);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /** 
     * Paint the receiver.
     */
    private void onPaint(Event event) {
        Font font = getFont();
        if (oldFont == null || !oldFont.equals(font)) {
            oldFont = font;
            resetTabSize(true);
        }
        GC gc = event.gc;
        Rectangle rect = super.getClientArea();
        if (items.length == 0) {
            if (showBorders) {
                if ((getStyle() & SWT.FLAT) != 0) {
                    gc.setForeground(borderColor1);
                    gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 1,
                            rect.y + rect.height - 1);
                } else {
                    gc.setForeground(borderColor1);
                    gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 3,
                            rect.y + rect.height - 3);

                    // fill in right and bottom edges with parent's background
                    gc.setBackground(getParent().getBackground());
                    gc.fillRectangle(rect.x + rect.width - 2, rect.y, 2,
                            rect.height);
                    gc.fillRectangle(rect.x, rect.y + rect.height - 2,
                            rect.width, 2);
                }
                gc.setForeground(getForeground());
            }
            return;
        }

        // redraw the Border
        drawBorder(gc);

        rect.x += borderLeft;
        rect.y += borderTop;
        rect.width -= borderLeft + borderRight;
        rect.height -= borderTop + borderBottom;
        Rectangle clip = gc.getClipping();
        gc.setClipping(clip.intersection(rect));

        // Draw the unselected tabs first.
        for (int i = 0; i < items.length; i++) {
            if (i != selectedIndex
                    && event.getBounds().intersects(items[i].getBounds())) {
                items[i].onPaint(gc, false);
            }
        }
        // Selected tab comes last
        if (selectedIndex != -1) {
            items[selectedIndex].onPaint(gc, true);
        }

        // draw insertion mark
        if (insertionIndex > -2) {
            gc.setForeground(getDisplay().getSystemColor(
                    SWT.COLOR_LIST_SELECTION));
            if (insertionIndex == -1) {
                Rectangle bounds = items[0].getBounds();
                gc.drawLine(bounds.x, bounds.y, bounds.x, bounds.y
                        + bounds.height - 1);
                gc.drawLine(bounds.x - 2, bounds.y, bounds.x + 2, bounds.y);
                gc.drawLine(bounds.x - 1, bounds.y + 1, bounds.x + 1,
                        bounds.y + 1);
                gc.drawLine(bounds.x - 1, bounds.y + bounds.height - 2,
                        bounds.x + 1, bounds.y + bounds.height - 2);
                gc.drawLine(bounds.x - 2, bounds.y + bounds.height - 1,
                        bounds.x + 2, bounds.y + bounds.height - 1);

            } else {
                Rectangle bounds = items[insertionIndex].getBounds();
                gc.drawLine(bounds.x + bounds.width, bounds.y, bounds.x
                        + bounds.width, bounds.y + bounds.height - 1);
                gc.drawLine(bounds.x + bounds.width - 2, bounds.y, bounds.x
                        + bounds.width + 2, bounds.y);
                gc.drawLine(bounds.x + bounds.width - 1, bounds.y + 1, bounds.x
                        + bounds.width + 1, bounds.y + 1);
                gc.drawLine(bounds.x + bounds.width - 1, bounds.y
                        + bounds.height - 2, bounds.x + bounds.width + 1,
                        bounds.y + bounds.height - 2);
                gc.drawLine(bounds.x + bounds.width - 2, bounds.y
                        + bounds.height - 1, bounds.x + bounds.width + 2,
                        bounds.y + bounds.height - 1);
            }
        }

        gc.setForeground(getForeground());
        gc.setBackground(getBackground());
    }

    private void redrawTabArea(int index) {
        int x = 0, y = 0, width = 0, height = 0;
        if (index == -1) {
            Rectangle area = super.getClientArea();
            if (area.width == 0 || area.height == 0) {
				return;
			}
            width = area.x + area.width - borderLeft - borderRight;
            height = tabHeight + 1; // +1 causes top line between content and tabs to be redrawn
            x = area.x + borderLeft;
            y = area.y + borderTop;
            if (onBottom) {
                y = Math.max(0, area.y + area.height - borderBottom - height);
            }
        } else {
            CTabItem item = items[index];
            x = item.x;
            y = item.y;
            Rectangle area = super.getClientArea();
            width = area.x + area.width - x;
            height = item.height;
        }
        redraw(x, y, width, height, false);
    }

    /**	 
     * Removes the listener.
     *
     * @param listener the listener
     *
     * @exception SWTError
     *	<ul><li>ERROR_THREAD_INVALID_ACCESS	when called from the wrong thread</li>
     * 		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 		<li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
     */
    public void removeSelectionListener(SelectionListener listener) {
        checkWidget();
        if (listener == null) {
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        }
        removeListener(SWT.Selection, listener);
        removeListener(SWT.DefaultSelection, listener);
    }

    /**	 
     * Removes the listener.
     *
     * @param listener the listener
     *
     * @exception SWTError
     *	<ul><li>ERROR_THREAD_INVALID_ACCESS	when called from the wrong thread</li>
     * 		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 		<li>ERROR_NULL_ARGUMENT when listener is null</li></ul>
     */
    public void removeCTabFolderListener(CTabFolderListener listener) {
        checkWidget();
        if (listener == null) {
			SWT.error(SWT.ERROR_NULL_ARGUMENT);
		}
        if (tabListeners.length == 0) {
			return;
		}
        int index = -1;
        for (int i = 0; i < tabListeners.length; i++) {
            if (listener == tabListeners[i]) {
                index = i;
                break;
            }
        }
        if (index == -1) {
			return;
		}
        if (tabListeners.length == 1) {
            tabListeners = new CTabFolderListener[0];
            showClose = false;
            setButtonBounds();
            return;
        }
        CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
        System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
        System.arraycopy(tabListeners, index + 1, newTabListeners, index,
                tabListeners.length - index - 1);
        tabListeners = newTabListeners;
    }

    /**
     * The widget was resized. Adjust the size of the currently selected page.
     */
    private void onResize() {

        if (items.length == 0) {
            redraw();
            return;
        }

        if (setItemBounds()) {
            redrawTabArea(-1);
        }

        Point size = getSize();
        if (oldSize == null) {
            redraw();
        } else {
            if (onBottom && size.y != oldSize.y) {
                redraw();
            } else {
                int x1 = Math.min(size.x, oldSize.x);
                if (size.x != oldSize.x) {
					x1 -= 10;
				}
                int y1 = Math.min(size.y, oldSize.y);
                if (size.y != oldSize.y) {
					y1 -= 10;
				}
                int x2 = Math.max(size.x, oldSize.x);
                int y2 = Math.max(size.y, oldSize.y);
                redraw(0, y1, x2 + 10, y2 - y1, false);
                redraw(x1, 0, x2 - x1, y2, false);
            }
        }
        oldSize = size;

        // resize content
        if (selectedIndex != -1) {
            Control control = items[selectedIndex].getControl();
            if (control != null && !control.isDisposed()) {
                control.setBounds(getClientArea());
            }
        }
    }

    public void setBackground(Color color) {
        super.setBackground(color);
        background = color;
        // init inactive close button
        inactiveCloseBar.setBackground(color);

        // init scroll buttons
        arrowBar.setBackground(color);

        //init topRight control
        if (topRight != null) {
			topRight.setBackground(color);
		}

        // init close button
        if (gradientColors == null) {
            closeBar.setBackground(color);
        }
    }

    /**
     * Specify a gradient of colours to be draw in the background of the selected tab.
     * For example to draw a gradient that varies from dark blue to blue and then to
     * white, use the following call to setBackground:
     * <pre>
     *	cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE), 
     *		                           display.getSystemColor(SWT.COLOR_BLUE),
     *		                           display.getSystemColor(SWT.COLOR_WHITE), 
     *		                           display.getSystemColor(SWT.COLOR_WHITE)},
     *		               new int[] {25, 50, 100});
     * </pre>
     *
     * @param colors an array of Color that specifies the colors to appear in the gradient 
     *               in order of appearance left to right.  The value <code>null</code> clears the
     *               background gradient. The value <code>null</code> can be used inside the array of 
     *               Color to specify the background color.
     * @param percents an array of integers between 0 and 100 specifying the percent of the width 
     *                 of the widget at which the color should change.  The size of the percents array must be one 
     *                 less than the size of the colors array.
     * 
     * @exception SWTError <ul>
     *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     *	</ul>
     */

    public void setSelectionBackground(Color[] colors, int[] percents) {
        checkWidget();
        if (colors != null) {
            if (percents == null || percents.length != colors.length - 1) {
                SWT.error(SWT.ERROR_INVALID_ARGUMENT);
            }
            if (getDisplay().getDepth() < 15) {
                // Don't use gradients on low color displays
                colors = new Color[] { colors[0] };
                percents = new int[] {};
            }
            for (int i = 0; i < percents.length; i++) {
                if (percents[i] < 0 || percents[i] > 100) {
                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
                }
                if (i > 0 && percents[i] < percents[i - 1]) {
                    SWT.error(SWT.ERROR_INVALID_ARGUMENT);
                }
            }
        }

        // Are these settings the same as before?
        if (backgroundImage == null) {
            if ((gradientColors != null) && (colors != null)
                    && (gradientColors.length == colors.length)) {
                boolean same = false;
                for (int i = 0; i < gradientColors.length; i++) {
                    if (gradientColors[i] == null) {
                        same = colors[i] == null;
                    } else {
                        same = gradientColors[i].equals(colors[i]);
                    }
                    if (!same) {
						break;
					}
                }
                if (same) {
                    for (int i = 0; i < gradientPercents.length; i++) {
                        same = gradientPercents[i] == percents[i];
                        if (!same) {
							break;
						}
                    }
                }
                if (same) {
					return;
				}
            }
        } else {
            backgroundImage = null;
        }
        // Store the new settings
        if (colors == null) {
            gradientColors = null;
            gradientPercents = null;
            closeBar.setBackground(background);
        } else {
            gradientColors = new Color[colors.length];
            for (int i = 0; i < colors.length; ++i) {
				gradientColors[i] = colors[i];
			}
            gradientPercents = new int[percents.length];
            for (int i = 0; i < percents.length; ++i) {
				gradientPercents[i] = percents[i];
			}
            if (getDisplay().getDepth() < 15) {
				closeBar.setBackground(background);
			} else {
				closeBar
                        .setBackground(gradientColors[gradientColors.length - 1]);
			}
        }

        // Refresh with the new settings
        if (selectedIndex > -1) {
			redrawTabArea(selectedIndex);
		}
    }

    /**
     * Set the image to be drawn in the background of the selected tab.
     * 
     * @param image the image to be drawn in the background
     * 
     * @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 setSelectionBackground(Image image) {
        checkWidget();
        if (image == backgroundImage) {
			return;
		}
        if (image != null) {
            gradientColors = null;
            gradientPercents = null;
        }
        backgroundImage = image;
        redrawTabArea(selectedIndex);
    }

    /**
     * Toggle the visibility of the border
     * 
     * @param show true if the border should be displayed
     * 
     * @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 setBorderVisible(boolean show) {
        checkWidget();
        //	if (showBorders == show) return;

        showBorders = show;
        if (showBorders) {
            if ((getStyle() & SWT.FLAT) != 0) {
                borderBottom = borderTop = borderLeft = borderRight = 1;
            } else {
                borderLeft = borderTop = 1;
                borderRight = borderBottom = 3;
            }
        } else {
            borderBottom = borderTop = borderLeft = borderRight = 0;
        }
        oldSize = null;
        notifyListeners(SWT.Resize, new Event());
    }

    public void setFont(Font font) {
        checkWidget();
        if (font != null && font.equals(getFont())) {
			return;
		}
        super.setFont(font);
        oldFont = getFont();
        resetTabSize(true);
    }

    /**
     * Set the foreground color of the selected tab.
     * 
     * @param color the color of the text displayed in the selected tab
     * 
     * @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 setSelectionForeground(Color color) {
        checkWidget();
        if (selectionForeground == color) {
			return;
		}
        if (color == null) {
			color = getForeground();
		}
        selectionForeground = color;
        if (selectedIndex > -1) {
            redrawTabArea(selectedIndex);
        }
    }

    /**
     * Display an insert marker before or after the specified tab item. 
     * 
     * A value of null will clear the mark.
     * 
     * @param item the item with which the mark is associated or null
     * 
     * @param after true if the mark should be displayed after the specified item
     * 
     * @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 setInsertMark(CTabItem item, boolean after) {
        checkWidget();
        int index = -1;
        if (item != null) {
            index = indexOf(item);
        }
        setInsertMark(index, after);
    }

    /**
     * Display an insert marker before or after the specified tab item.
     * 
     * A value of -1 will clear the mark.
     * 
     * @param index the index of the item with which the mark is associated or null
     * 
     * @param after true if the mark should be displayed after the specified item
     * 
     * @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 setInsertMark(int index, boolean after) {
        checkWidget();
        if (index < -1 || index >= getItemCount()) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }

        if (index == -1) {
            index = -2;
        } else {
            index = after ? index : --index;
        }

        if (insertionIndex == index) {
			return;
		}
        int oldIndex = insertionIndex;
        insertionIndex = index;
        if (index > -1) {
			redrawTabArea(index);
		}
        if (oldIndex > 1) {
			redrawTabArea(oldIndex);
		}
    }

    /**
     * Set the selection to the tab at the specified index.
     * 
     * @param index the index of the tab item to be selected
     * 
     * @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 setSelection(int index) {
        checkWidget();
        if (index < 0 || index >= items.length) {
			return;
		}
        if (selectedIndex == index) {
			return;
		}

        int oldIndex = selectedIndex;
        selectedIndex = index;

        Control control = items[index].control;
        if (control != null && !control.isDisposed()) {
            control.setBounds(getClientArea());
            control.setVisible(true);
        }

        if (oldIndex != -1) {
            control = items[oldIndex].control;
            if (control != null && !control.isDisposed()) {
                control.setVisible(false);
            }
        }
        showItem(items[selectedIndex]);
        setButtonBounds();
        redrawTabArea(-1);
    }

    /**
     * Set the control that appears in the top right corner of the tab folder.
     * Typically this is a close button or a composite with a Menu and close button. 
     * The topRight control is optional.  Setting the top right control to null will remove it from the tab folder.
     *
     * @since 2.1
     * 
     * @param control the control to be displayed in the top right corner 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>
     *    <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
     * </ul>
     */
    public void setTopRight(Control control) {
        checkWidget();
        if (control != null && control.getParent() != this) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }
        topRight = control;
        resetTabSize(true);
    }

    /**
     * Shows the item.  If the item is already showing in the receiver,
     * this method simply returns.  Otherwise, the items are scrolled until
     * the item is visible.
     *
     * @param item the item to be shown
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
     *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
     * </ul>
     * @exception 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>
     *
     * @see CTabFolder#showSelection()
     * 
     * @since 2.0
     */
    public void showItem(CTabItem item) {
        checkWidget();
        if (item == null) {
			SWT.error(SWT.ERROR_NULL_ARGUMENT);
		}
        if (item.isDisposed()) {
			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
		}

        int index = indexOf(item);
        if (index < topTabIndex) {
            topTabIndex = index;
            setItemLocation();
            redrawTabArea(-1);
            return;
        }
        Rectangle area = getClientArea();
        if (area.width <= 0) {
            topTabIndex = index;
            return;
        }
        int rightEdge = area.x + area.width;
        Rectangle rect = getToolSpace();
        if (rect.width > 0) {
            rightEdge -= rect.width;
        }
        if (item.x + item.width < rightEdge) {
			return;
		}
        setLastItem(index);
    }

    /**
     * Shows the selection.  If the selection is already showing in the receiver,
     * this method simply returns.  Otherwise, the items are scrolled until
     * the selection is visible.
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener 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>
     *
     * @see CTabFolder#showItem(CTabItem)
     * 
     * @since 2.0
     * 
     */
    public void showSelection() {
        checkWidget();
        if (selectedIndex != -1) {
            showItem(getSelection());
        }
    }

    char getMnemonic(String string) {
        int index = 0;
        int length = string.length();
        do {
            while ((index < length) && (string.charAt(index) != '&')) {
				index++;
			}
            if (++index >= length) {
				return '\0';
			}
            if (string.charAt(index) != '&') {
				return string.charAt(index);
			}
            index++;
        } while (index < length);
        return '\0';
    }

    /**
     * Set the selection to the tab at the specified item.
     * 
     * @param item the tab item to be selected
     * 
     * @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>
     *    <li>ERROR_NULL_ARGUMENT - if argument is null</li>
     * </ul>
     */
    public void setSelection(CTabItem item) {
        checkWidget();
        if (item == null) {
			SWT.error(SWT.ERROR_NULL_ARGUMENT);
		}
        int index = indexOf(item);
        setSelection(index);
    }

    /**
     * Set the selection to the tab at the specified index.
     */
    private void setSelection(int index, boolean notify) {
        int oldSelectedIndex = selectedIndex;
        setSelection(index);
        if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
            Event event = new Event();
            event.item = getItem(selectedIndex);
            notifyListeners(SWT.Selection, event);
        }
    }

    private Image scaleImage(Image image, int oldSize, int newSize) {
        Display display = getDisplay();
        Color foreground = getForeground();
        Color black = display.getSystemColor(SWT.COLOR_BLACK);
        Color background = getBackground();
        PaletteData palette = new PaletteData(new RGB[] { foreground.getRGB(),
                background.getRGB(), black.getRGB() });
        ImageData imageData = new ImageData(newSize, newSize, 4, palette);
        imageData.transparentPixel = 1;
        Image temp = new Image(display, imageData);
        GC gc = new GC(temp);
        gc.setBackground(background);
        gc.fillRectangle(0, 0, newSize, newSize);
        gc.drawImage(image, 0, 0, oldSize, oldSize, 0, 0, newSize, newSize);
        gc.dispose();
        return temp;
    }

    private void updateCloseBar() {
        //Temporary code - need a better way to determine toolBar trim
        int toolbarTrim = 4;
        String platform = SWT.getPlatform();
        if ("photon".equals(platform)) { //$NON-NLS-1$
			toolbarTrim = 6;
		}
        if ("gtk".equals(platform)) { //$NON-NLS-1$
			toolbarTrim = 8;
		}

        int maxHeight = tabHeight - CTabItem.TOP_MARGIN
                - CTabItem.BOTTOM_MARGIN - toolbarTrim;
        if (maxHeight < 3) {
			return;
		}
        int imageHeight = (maxHeight < 9) ? 9 : maxHeight;

        if (closeImage != null && closeImage.getBounds().height == imageHeight) {
			return;
		}

        if (closeBar != null) {
			closeBar.dispose();
		}
        closeBar = null;
        if (inactiveCloseBar != null) {
			inactiveCloseBar.dispose();
		}
        inactiveCloseBar = null;
        createCloseBar();

        ToolItem closeItem = closeBar.getItems()[0];
        ToolItem inactiveCloseItem = inactiveCloseBar.getItems()[0];

        if (closeImage != null) {
			closeImage.dispose();
		}

        Display display = getDisplay();
        Color foreground = getForeground();
        Color black = display.getSystemColor(SWT.COLOR_BLACK);
        Color background = getBackground();

        PaletteData palette = new PaletteData(new RGB[] { foreground.getRGB(),
                background.getRGB(), black.getRGB() });
        ImageData imageData = new ImageData(imageHeight, imageHeight, 4,
                palette);
        imageData.transparentPixel = 1;
        closeImage = new Image(display, imageData);
        GC gc = new GC(closeImage);
        gc.setBackground(background);
        gc.fillRectangle(0, 0, imageHeight, imageHeight);
        gc.setForeground(black);

        //draw an 9x8 'x' centered in image
        int h = (imageHeight / 2) * 2;
        int inset = (h - 8) / 2;
        gc.drawLine(inset, inset, h - inset - 1, h - inset - 1);
        gc.drawLine(inset + 1, inset, h - inset, h - inset - 1);
        gc.drawLine(inset, h - inset - 1, h - inset - 1, inset);
        gc.drawLine(inset + 1, h - inset - 1, h - inset, inset);

        gc.dispose();

        if (maxHeight < imageHeight) {
            //rescale image
            Image temp = scaleImage(closeImage, imageHeight, maxHeight);
            closeImage.dispose();
            closeImage = temp;
        }
        closeItem.setImage(closeImage);
        inactiveCloseItem.setImage(closeImage);
    }

    private void updateArrowBar() {
        //Temporary code - need a better way to determine toolBar trim
        int toolbarTrim = 6; // Windows needs 6, photon needs 6, gtk needs 8
        String platform = SWT.getPlatform();
        if ("gtk".equals(platform)) { //$NON-NLS-1$
			toolbarTrim = 8;
		}

        int maxHeight = tabHeight - toolbarTrim;
        if (maxHeight < 3) {
			return;
		}
        int imageHeight = (maxHeight < 9) ? 9 : maxHeight;

        if (arrowLeftImage != null
                && arrowLeftImage.getBounds().height == imageHeight) {
			return;
		}

        if (arrowBar != null) {
			arrowBar.dispose();
		}
        arrowBar = null;
        if (arrowLeftImage != null) {
			arrowLeftImage.dispose();
		}
        if (arrowRightImage != null) {
			arrowRightImage.dispose();
		}

        createArrowBar();
        ToolItem[] items = arrowBar.getItems();
        ToolItem left = items[0];
        ToolItem right = items[1];

        Display display = getDisplay();
        Color foreground = getForeground();
        Color black = display.getSystemColor(SWT.COLOR_BLACK);
        Color background = getBackground();

        PaletteData palette = new PaletteData(new RGB[] { foreground.getRGB(),
                background.getRGB(), black.getRGB() });
        ImageData imageData = new ImageData(7, imageHeight, 4, palette);
        imageData.transparentPixel = 1;
        arrowLeftImage = new Image(display, imageData);
        GC gc = new GC(arrowLeftImage);
        gc.setBackground(background);
        gc.fillRectangle(0, 0, 7, imageHeight);
        gc.setBackground(black);
        //draw a 9x5 '<' centered vertically in image
        int h = (imageHeight / 2) * 2;
        int midpoint = h / 2 - 1;
        int[] pointArr = new int[] { 6, midpoint - 5, 1, midpoint, 6,
                midpoint + 5, };
        gc.fillPolygon(pointArr);
        gc.dispose();

        palette = new PaletteData(new RGB[] { foreground.getRGB(),
                background.getRGB(), black.getRGB() });
        imageData = new ImageData(7, imageHeight, 4, palette);
        imageData.transparentPixel = 1;
        arrowRightImage = new Image(display, imageData);
        gc = new GC(arrowRightImage);
        gc.setBackground(background);
        gc.fillRectangle(0, 0, 7, imageHeight);
        gc.setBackground(black);
        //draw a 9x5 '>' centered vertically in image
        pointArr = new int[] { 1, midpoint - 5, 6, midpoint, 1, midpoint + 5, };
        gc.fillPolygon(pointArr);
        gc.dispose();

        if (maxHeight < imageHeight) {
            //rescale image
            Image leftTemp = scaleImage(arrowLeftImage, imageHeight, maxHeight);
            arrowLeftImage.dispose();
            arrowLeftImage = leftTemp;

            Image rightTemp = scaleImage(arrowRightImage, imageHeight,
                    maxHeight);
            arrowRightImage.dispose();
            arrowRightImage = rightTemp;
        }
        left.setImage(arrowLeftImage);
        right.setImage(arrowRightImage);
    }

    private void onMouseDoubleClick(Event event) {
        Event e = new Event();
        e.item = getItem(new Point(event.x, event.y));
        notifyListeners(SWT.DefaultSelection, e);
    }

    /** 
     * A mouse button was pressed down. 
     * If a tab was hit select the tab.
     */
    private void onMouseDown(Event event) {
        for (int i = 0; i < items.length; i++) {
            if (items[i].getBounds().contains(new Point(event.x, event.y))) {
                if (i == selectedIndex) {
                    showSelection();
                    return;
                }
                forceFocus();
                setSelection(i, true);
                if (isFocusControl()) {
					setFocus();
				}
                return;
            }
        }
    }

    private void onMouseExit(Event event) {
        Rectangle inactiveBounds = inactiveCloseBar.getBounds();
        if (inactiveBounds.contains(event.x, event.y)) {
			return;
		}
        inactiveCloseBar.setVisible(false);
        inactiveItem = null;

        showToolTip = false;
        toolTipItem = null;
        if (tip != null && !tip.isDisposed() && tip.isVisible()) {
			tip.setVisible(false);
		}
    }

    private void onMouseHover(Event event) {
        if (tip == null || tip.isDisposed()) {
			return;
		}
        showToolTip = true;
        showToolTip(event.x, event.y);
    }

    private void showToolTip(int x, int y) {
        CTabItem item = getItem(new Point(x, y));
        if (item != null) {
            if (item == toolTipItem) {
				return;
			}
            toolTipItem = item;
            String tooltip = item.getToolTipText();
            if (tooltip != null && tooltip.length() > 0) {
                Display display = tip.getDisplay();
                label.setForeground(display
                        .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
                label.setBackground(display
                        .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
                label.setText(tooltip);
                Point labelSize = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
                labelSize.x += 2;
                labelSize.y += 2;
                label.setSize(labelSize);
                tip.pack();
                /*
                 * On some platforms, there is a minimum size for a shell  
                 * which may be greater than the label size.
                 * To avoid having the background of the tip shell showing
                 * around the label, force the label to fill the entire client area.
                 */
                Rectangle area = tip.getClientArea();
                label.setSize(area.width, area.height);
                /*
                 * Position the tooltip and ensure that it is not located off
                 * the screen.
                 */
                Point pt = new Point(item.x + item.width / 4, item.y
                        + item.height + 2);
                pt = toDisplay(pt);
                Rectangle rect = display.getBounds();
                Point tipSize = tip.getSize();
                pt.x = Math.max(0, Math.min(pt.x, rect.width - tipSize.x));
                pt.y = Math.max(0, Math.min(pt.y, rect.height - tipSize.y));
                tip.setLocation(pt);
                tip.setVisible(true);
                return;
            }
        }

        toolTipItem = null;
        if (tip != null && !tip.isDisposed() && tip.isVisible()) {
			tip.setVisible(false);
		}
    }

    private void onMouseMove(Event event) {
        if (showToolTip) {
            showToolTip(event.x, event.y);
        }

        if (!showClose) {
			return;
		}

        CTabItem item = null;
        for (int i = 0; i < items.length; i++) {
            Rectangle rect = items[i].getBounds();
            if (rect.contains(new Point(event.x, event.y))) {
                item = items[i];
                break;
            }
        }
        if (item == inactiveItem) {
			return;
		}

        inactiveCloseBar.setVisible(false);
        inactiveItem = null;

        if (item == null || item == getSelection()) {
			return;
		}

        int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN
                - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle
        Point size = inactiveCloseBar.computeSize(SWT.DEFAULT, toolbarHeight);
        int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim
        int y = item.y + Math.max(0, (item.height - toolbarHeight) / 2);
        Rectangle toolspace = getToolSpace();
        Point folderSize = getSize();
        if ((toolspace.width == 0 || x < toolspace.x)
                && x + size.x < folderSize.x - borderRight) {
            inactiveCloseBar.setBounds(x, y, size.x, toolbarHeight);
            inactiveCloseBar.setVisible(true);
            inactiveItem = item;
        }
    }

    private void onTraverse(Event event) {
        switch (event.detail) {
        case SWT.TRAVERSE_ESCAPE:
        // TEMPORARY CODE See bug report 17372
        //		case SWT.TRAVERSE_RETURN:
        case SWT.TRAVERSE_TAB_NEXT:
        case SWT.TRAVERSE_TAB_PREVIOUS:
            event.doit = true;
            break;
        case SWT.TRAVERSE_MNEMONIC:
            event.doit = onMnemonic(event);
            if (event.doit) {
				event.detail = SWT.TRAVERSE_NONE;
			}
            break;
        case SWT.TRAVERSE_PAGE_NEXT:
        case SWT.TRAVERSE_PAGE_PREVIOUS:
            event.doit = onPageTraversal(event);
            if (event.doit) {
				event.detail = SWT.TRAVERSE_NONE;
			}
            break;
        }
    }

    private boolean onPageTraversal(Event event) {
        int count = getItemCount();
        if (count == 0) {
			return false;
		}
        int index = getSelectionIndex();
        if (index == -1) {
            index = 0;
        } else {
            int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
            index = (index + offset + count) % count;
        }
        setSelection(index, true);
        return true;
    }

    /**
     * Answer true if not all tabs can be visible in the receive
     * thus requiring the scroll buttons to be visible.
     */
    private boolean scroll_leftVisible() {
        return topTabIndex > 0;
    }

    /**
     * Answer true if not all tabs can be visible in the receive
     * thus requiring the scroll buttons to be visible.
     */
    private boolean scroll_rightVisible() {
        // only show Scroll buttons if there is more than one item
        // and if we are not already at the last item
        if (items.length < 2) {
			return false;
		}
        Rectangle area = getClientArea();
        int rightEdge = area.x + area.width;
        if (rightEdge <= 0) {
			return false;
		}
        if (topTabIndex > 0) {
            rightEdge -= arrowBar.getSize().x;
        }
        if (topRight != null) {
            rightEdge -= topRight.getSize().x;
        }
        CTabItem item = items[items.length - 1];
        return (item.x + item.width > rightEdge);
    }

    /**
     * Scroll the tab items to the left.
     */
    private void scroll_scrollLeft() {
        if (items.length == 0) {
			return;
		}
        setLastItem(topTabIndex - 1);
    }

    /**
     * Scroll the tab items to the right.
     */
    private void scroll_scrollRight() {
        int lastIndex = getLastItem();
        topTabIndex = lastIndex + 1;
        setItemLocation();
        correctLastItem();
        redrawTabArea(-1);
    }

    private boolean correctLastItem() {
        Rectangle area = getClientArea();
        int rightEdge = area.x + area.width;
        if (rightEdge <= 0) {
			return false;
		}
        Rectangle toolspace = getToolSpace();
        if (toolspace.width > 0) {
            rightEdge -= toolspace.width;
        }
        CTabItem item = items[items.length - 1];
        if (item.x + item.width < rightEdge) {
            setLastItem(items.length - 1);
            return true;
        }
        return false;
    }

    /**
     * Specify a fixed height for the tab items.  If no height is specified,
     * the default height is the height of the text or the image, whichever 
     * is greater. Specifying a height of 0 will revert to the default height.
     * 
     * @param height the pixel value of the height or 0
     * 
     * @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>
     *    <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
     * </ul>
     */
    public void setTabHeight(int height) {
        checkWidget();
        if (height < 0) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }
        fixedTabHeight = true;
        if (tabHeight == height) {
			return;
		}
        tabHeight = height;
        oldSize = null;
        notifyListeners(SWT.Resize, new Event());
    }

    void resetTabSize(boolean checkHeight) {
        int oldHeight = tabHeight;
        if (!fixedTabHeight && checkHeight) {
            int tempHeight = 0;
            GC gc = new GC(this);
            for (int i = 0; i < items.length; i++) {
                tempHeight = Math.max(tempHeight, items[i].preferredHeight(gc));
            }
            gc.dispose();
            if (topRight != null) {
				tempHeight = Math.max(tempHeight, topRight.computeSize(
                        SWT.DEFAULT, SWT.DEFAULT).y);
			}
            tabHeight = tempHeight;
        }

        if (tabHeight != oldHeight) {
            oldSize = null;
            notifyListeners(SWT.Resize, new Event());
        } else {
            setItemBounds();
            redraw();
        }
    }

    /**
     * 
     * @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>
     *    <li>ERROR_INVALID_ARGUMENT - if the position value is not either SWT.TOP or SWT.BOTTOM</li>
     * </ul>
     * 
     * UNDER CONSTRUCTION
     * @since 3.0
     */
    public void setTabPosition(int position) {
        checkWidget();
        if (position != SWT.TOP && position != SWT.BOTTOM) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }
        if (onBottom != (position == SWT.BOTTOM)) {
            onBottom = position == SWT.BOTTOM;
            setBorderVisible(showBorders);
            resetTabSize(true);
            //		updateTabHeight(true);
            //		Rectangle rectBefore = getClientArea();
            //		updateItems();
            //		Rectangle rectAfter = getClientArea();
            //		if (!rectBefore.equals(rectAfter)) {
            //			notifyListeners(SWT.Resize, new Event());
            //		}
            //		setItemBounds();
            //		redrawTabArea(-1);
            //		redraw();
        }
    }

    public int getTabPosition() {
        if (onBottom) {
			return SWT.BOTTOM;
		}
        return SWT.TOP;
    }

}
