]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/custom/CTabFolder.java
Merge branch 'bug-623' into release/1.43.0
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / custom / CTabFolder.java
diff --git a/bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/custom/CTabFolder.java b/bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/custom/CTabFolder.java
new file mode 100644 (file)
index 0000000..63d9676
--- /dev/null
@@ -0,0 +1,4137 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.custom;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.accessibility.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
+import org.eclipse.swt.internal.DPIUtil.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ *
+ * 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>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>CLOSE, TOP, BOTTOM, FLAT, BORDER, SINGLE, MULTI</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection</dd>
+ * <dd>"CTabFolder2"</dd>
+ * </dl>
+ * Note: Only one of the styles TOP and BOTTOM
+ * may be specified.
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/snippets/#ctabfolder">CTabFolder, CTabItem snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: CustomControlExample</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+
+public class CTabFolder extends Composite {
+
+       /**
+        * marginWidth specifies the number of points 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 points 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;
+
+       /**
+        * 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.
+        *
+        * NOTE This field is badly named and can not be fixed for backwards compatibility.
+        * It should not be capitalized.
+        *
+        * @deprecated This field is no longer used.  See setMinimumCharacters(int)
+        */
+       @Deprecated
+       public int MIN_TAB_WIDTH = 4;
+
+       /**
+        * Color of innermost line of drop shadow border.
+        *
+        * NOTE This field is badly named and can not be fixed for backwards compatibility.
+        * It should be capitalized.
+        *
+        * @deprecated drop shadow border is no longer drawn in 3.0
+        */
+       @Deprecated
+       public static RGB borderInsideRGB  = new RGB (132, 130, 132);
+       /**
+        * Color of middle line of drop shadow border.
+        *
+        * NOTE This field is badly named and can not be fixed for backwards compatibility.
+        * It should be capitalized.
+        *
+        * @deprecated drop shadow border is no longer drawn in 3.0
+        */
+       @Deprecated
+       public static RGB borderMiddleRGB  = new RGB (143, 141, 138);
+       /**
+        * Color of outermost line of drop shadow border.
+        *
+        * NOTE This field is badly named and can not be fixed for backwards compatibility.
+        * It should be capitalized.
+        *
+        * @deprecated drop shadow border is no longer drawn in 3.0
+        */
+       @Deprecated
+       public static RGB borderOutsideRGB = new RGB (171, 168, 165);
+
+       /* sizing, positioning */
+       boolean onBottom = false;
+       boolean single = false;
+       boolean simple = true;
+       int fixedTabHeight = SWT.DEFAULT;
+       int tabHeight;
+       int minChars = 20;
+       boolean borderVisible = false;
+
+       /* item management */
+       CTabFolderRenderer renderer;
+       CTabItem items[] = new CTabItem[0];
+       /** index of the left most visible tab. */
+       int firstIndex = -1;
+       int selectedIndex = -1;
+
+       /**
+        * Indices of the elements in the {@link #items} array, used to manage tab
+        * visibility and candidates to be hidden/shown next.
+        * <p>
+        * If there is not enough place for all tabs, tabs starting from the end of
+        * the {@link #priority} array will be hidden first (independently from the
+        * {@link #mru} flag!) =&gt; the right most elements have the highest priority
+        * to be hidden.
+        * <p>
+        * If there is more place to show previously hidden tabs, tabs starting from
+        * the beginning of the {@link #priority} array will be made visible first
+        * (independently from the {@link #mru} flag!) =&gt; the left most elements
+        * have the highest priority to be shown.
+        * <p>
+        * The update strategy of the {@link #priority} array however depends on the
+        * {@link #mru} flag.
+        * <p>
+        * If {@link #mru} flag is set, the first index is always the index of the
+        * currently selected tab, next one is the tab selected before current
+        * etc...
+        * <p>
+        * Example: [4,2,5,1,3,0], just representing the last selection order.
+        * <p>
+        * If {@link #mru} flag is not set, the first index is always the index of
+        * the left most visible tab ({@link #firstIndex} field), next indices are
+        * incremented by one up to <code>priority.length-1</code>, and the rest
+        * filled with indices starting with <code>firstIndex-1</code> and
+        * decremented by one until 0 index is reached.
+        * <p>
+        * The tabs between first index and the index of the currently selected tab
+        * are always visible.
+        * <p>
+        * Example: 6 tabs, 2 and 3 are indices of currently shown tabs:
+        * [2,3,4,5,1,0]. The array consists of two blocks: sorted ascending from
+        * first visible (2) to last available (5), and the rest sorted descending
+        * (1,0). 4 and 5 are the hidden tabs on the right side, 0 and 1 are the
+        * hidden tabs on the left side from the visible tabs 2 and 3.
+        *
+        * @see #updateItems(int)
+        * @see #setItemLocation(GC)
+        */
+       int[] priority = new int[0];
+       boolean mru = false;
+       Listener listener;
+       boolean ignoreTraverse;
+       boolean useDefaultRenderer;
+
+       /* External Listener management */
+       CTabFolder2Listener[] folderListeners = new CTabFolder2Listener[0];
+       // support for deprecated listener mechanism
+       CTabFolderListener[] tabListeners = new CTabFolderListener[0];
+
+       /* Selected item appearance */
+       Image selectionBgImage;
+       Color[] selectionGradientColors;
+       int[] selectionGradientPercents;
+       boolean selectionGradientVertical;
+       Color selectionForeground;
+       Color selectionBackground;
+
+       /* Unselected item appearance */
+       Color[] gradientColors;
+       int[] gradientPercents;
+       boolean gradientVertical;
+       boolean showUnselectedImage = true;
+
+       // close, min/max and chevron buttons
+       boolean showClose = false;
+       boolean showUnselectedClose = true;
+
+       boolean showMin = false;
+       boolean minimized = false;
+       boolean showMax = false;
+       boolean maximized = false;
+       ToolBar minMaxTb;
+       ToolItem maxItem;
+       ToolItem minItem;
+       Image maxImage;
+       Image minImage;
+       boolean hoverTb;
+       Rectangle hoverRect = new Rectangle(0,0,0,0);
+       boolean hovering;
+       boolean hoverTimerRunning;
+       boolean highlight;
+       boolean highlightEnabled = true;
+
+       boolean showChevron = false;
+       Menu showMenu;
+       ToolBar chevronTb;
+       ToolItem chevronItem;
+       int chevronCount;
+       boolean chevronVisible = true;
+
+       Image chevronImage;
+       Control topRight;
+       int topRightAlignment = SWT.RIGHT;
+       boolean ignoreResize;
+       Control[] controls;
+       int[] controlAlignments;
+       Rectangle[] controlRects;
+       Image[] controlBkImages;
+
+       int updateFlags;
+       final static int REDRAW = 1 << 1;
+       final static int REDRAW_TABS = 1 << 2;
+       final static int UPDATE_TAB_HEIGHT = 1 << 3;
+       Runnable updateRun;
+
+       // when disposing CTabFolder, don't try to layout the items or
+       // change the selection as each child is destroyed.
+       boolean inDispose = false;
+
+       // keep track of size changes in order to redraw only affected area
+       // on Resize
+       Point oldSize;
+       Font oldFont;
+
+       // internal constants
+       static final int DEFAULT_WIDTH = 64;
+       static final int DEFAULT_HEIGHT = 64;
+
+       static final int SELECTION_FOREGROUND = SWT.COLOR_LIST_FOREGROUND;
+       static final int SELECTION_BACKGROUND = SWT.COLOR_LIST_BACKGROUND;
+
+       static final int FOREGROUND = SWT.COLOR_WIDGET_FOREGROUND;
+       static final int BACKGROUND = SWT.COLOR_WIDGET_BACKGROUND;
+
+       //TODO: add setter for spacing?
+       static final int SPACING = 3;
+/**
+ * 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 SWT#BORDER
+ * @see SWT#SINGLE
+ * @see SWT#MULTI
+ * @see #getStyle()
+ */
+public CTabFolder(Composite parent, int style) {
+       super(parent, checkStyle (parent, style));
+       init(style);
+}
+
+void init(int style) {
+       super.setLayout(new CTabFolderLayout());
+       int style2 = super.getStyle();
+       oldFont = getFont();
+       onBottom = (style2 & SWT.BOTTOM) != 0;
+       showClose = (style2 & SWT.CLOSE) != 0;
+//     showMin = (style2 & SWT.MIN) != 0; - conflicts with SWT.TOP
+//     showMax = (style2 & SWT.MAX) != 0; - conflicts with SWT.BOTTOM
+       single = (style2 & SWT.SINGLE) != 0;
+       borderVisible = (style & SWT.BORDER) != 0;
+       //set up default colors
+       Display display = getDisplay();
+       selectionForeground = display.getSystemColor(SELECTION_FOREGROUND);
+       selectionBackground = display.getSystemColor(SELECTION_BACKGROUND);
+       renderer = new CTabFolderRenderer(this);
+       useDefaultRenderer = true;
+       controls = new Control[0];
+       controlAlignments = new int[0];
+       controlRects = new Rectangle[0];
+       controlBkImages = new Image[0];
+       updateTabHeight(false);
+
+       // Add all listeners
+       listener = event -> {
+               switch (event.type) {
+                       case SWT.Dispose:          onDispose(event); break;
+                       case SWT.DragDetect:       onDragDetect(event); break;
+                       case SWT.FocusIn:          onFocus(event);      break;
+                       case SWT.FocusOut:         onFocus(event);      break;
+                       case SWT.KeyDown:          onKeyDown(event); break;
+                       case SWT.MenuDetect:       onMenuDetect(event); break;
+                       case SWT.MouseDoubleClick: onMouseDoubleClick(event); break;
+                       case SWT.MouseDown:        onMouse(event);      break;
+                       case SWT.MouseEnter:       onMouse(event);      break;
+                       case SWT.MouseExit:        onMouse(event);      break;
+                       case SWT.MouseHover:       onMouse(event); break;
+                       case SWT.MouseMove:        onMouse(event); break;
+                       case SWT.MouseUp:          onMouse(event); break;
+                       case SWT.Paint:            onPaint(event);      break;
+                       case SWT.Resize:           onResize(event);     break;
+                       case SWT.Traverse:         onTraverse(event); break;
+                       case SWT.Selection:        onSelection(event); break;
+                       case SWT.Activate:         onActivate(event); break;
+                       case SWT.Deactivate:       onDeactivate(event); break;
+               }
+       };
+
+       int[] folderEvents = new int[]{
+               SWT.Dispose,
+               SWT.DragDetect,
+               SWT.FocusIn,
+               SWT.FocusOut,
+               SWT.KeyDown,
+               SWT.MenuDetect,
+               SWT.MouseDoubleClick,
+               SWT.MouseDown,
+               SWT.MouseEnter,
+               SWT.MouseExit,
+               SWT.MouseHover,
+               SWT.MouseMove,
+               SWT.MouseUp,
+               SWT.Paint,
+               SWT.Resize,
+               SWT.Traverse,
+               SWT.Activate,
+               SWT.Deactivate
+       };
+       for (int i = 0; i < folderEvents.length; i++) {
+               addListener(folderEvents[i], listener);
+       }
+
+       initAccessible();
+}
+void onDeactivate(Event event) {
+       if (!highlightEnabled) {
+               return;
+       }
+       this.highlight = false;
+       redraw();
+}
+
+void onActivate(Event event) {
+       if (!highlightEnabled) {
+               return;
+       }
+       this.highlight = true;
+       redraw();
+}
+
+static int checkStyle (Composite parent, int style) {
+       int mask = SWT.CLOSE | SWT.TOP | SWT.BOTTOM | SWT.FLAT | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.SINGLE | SWT.MULTI;
+       style = style & mask;
+       // TOP and BOTTOM are mutually exclusive.
+       // TOP is the default
+       if ((style & SWT.TOP) != 0) style = style & ~SWT.BOTTOM;
+       // SINGLE and MULTI are mutually exclusive.
+       // MULTI is the default
+       if ((style & SWT.MULTI) != 0) style = style & ~SWT.SINGLE;
+       // reduce the flash by not redrawing the entire area on a Resize event
+       style |= SWT.NO_REDRAW_RESIZE;
+
+       //TEMPORARY CODE
+       /*
+        * In Right To Left orientation on Windows, all GC calls that use a brush are drawing
+        * offset by one pixel.  This results in some parts of the CTabFolder not drawing correctly.
+        * To alleviate some of the appearance problems, allow the OS to draw the background.
+        * This does not draw correctly but the result is less obviously wrong.
+        */
+       if ((style & SWT.RIGHT_TO_LEFT) != 0) return style;
+       if ((parent.getStyle() & SWT.MIRRORED) != 0 && (style & SWT.LEFT_TO_RIGHT) == 0) return style;
+
+       return style | SWT.DOUBLE_BUFFERED;
+}
+
+/**
+ *
+ * Adds the listener to the collection of listeners who will
+ * be notified when a tab item is closed, minimized, maximized,
+ * restored, or to show the list of items that are not
+ * currently visible.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @exception SWTException <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>
+ *
+ * @see CTabFolder2Listener
+ * @see #removeCTabFolder2Listener(CTabFolder2Listener)
+ *
+ * @since 3.0
+ */
+public void addCTabFolder2Listener(CTabFolder2Listener listener) {
+       checkWidget();
+       if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+       // add to array
+       CTabFolder2Listener[] newListeners = new CTabFolder2Listener[folderListeners.length + 1];
+       System.arraycopy(folderListeners, 0, newListeners, 0, folderListeners.length);
+       folderListeners = newListeners;
+       folderListeners[folderListeners.length - 1] = listener;
+}
+/**
+ * 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_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <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>
+ *
+ * @see CTabFolderListener
+ * @see #removeCTabFolderListener(CTabFolderListener)
+ *
+ * @deprecated use addCTabFolder2Listener(CTabFolder2Listener)
+ */
+@Deprecated
+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;
+       // display close button to be backwards compatible
+       if (!showClose) {
+               showClose = true;
+               updateFolder(REDRAW);
+       }
+}
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's selection, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the user changes the selected tab.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the user changes the receiver's selection
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception 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 SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+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);
+}
+
+Rectangle[] computeControlBounds (Point size, boolean[][] position) {
+       if (controls == null || controls.length == 0) return new Rectangle[0];
+       Rectangle[] rects = new Rectangle[controls.length];
+       for (int i = 0; i < rects.length; i++) {
+               rects[i] = new Rectangle(0, 0, 0, 0);
+       }
+       Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
+       int borderRight = trim.width + trim.x;
+       int borderLeft = -trim.x;
+       int borderBottom = trim.height + trim.y;
+       int borderTop = -trim.y;
+
+       Point[] tabControlSize = new Point[controls.length];
+       boolean[] overflow = new boolean [controls.length];
+       //Left Control
+       int leftWidth = 0;
+       int x = borderLeft + SPACING;
+       int rightWidth = 0;
+       int allWidth = 0;
+       for (int i = 0; i < controls.length; i++) {
+               Point ctrlSize = tabControlSize[i] = !controls[i].isDisposed() && controls[i].getVisible() ? controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT) : new Point(0,0);
+               int alignment = controlAlignments[i];
+               if ((alignment & SWT.LEAD) != 0) {
+                       rects[i].width = ctrlSize.x;
+                       rects[i].height = getControlHeight(ctrlSize);
+                       rects[i].x = x;
+                       rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
+                       x += ctrlSize.x;
+                       leftWidth += ctrlSize.x;
+               } else {
+                       if ((alignment & (SWT.FILL | SWT.WRAP)) == 0) {
+                               rightWidth += ctrlSize.x;
+                       }
+                       allWidth += ctrlSize.x;
+               }
+       }
+       if (leftWidth > 0) leftWidth += SPACING * 2;
+
+       int itemWidth = 0;
+       for (int i = 0; i < items.length; i++) {
+               if (items[i].showing) itemWidth += items[i].width;
+       }
+
+       int maxWidth = size.x - borderLeft - leftWidth - borderRight;
+       int availableWidth = Math.max(0, maxWidth - itemWidth - rightWidth);
+       if (rightWidth > 0) availableWidth -= SPACING * 2;
+       x =  size.x  - borderRight - SPACING;
+       if (itemWidth + allWidth <= maxWidth) {
+               //Everything fits
+               for (int i = 0; i < controls.length; i++) {
+                       int alignment = controlAlignments[i];
+                       if ((alignment & SWT.TRAIL) != 0) {
+                               Point ctrlSize = tabControlSize[i];
+                               x -= ctrlSize.x;
+                               rects[i].width = ctrlSize.x;
+                               rects[i].height = getControlHeight(ctrlSize);
+                               rects[i].x = x;
+                               rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
+                               if ((alignment & (SWT.FILL | SWT.WRAP)) != 0) availableWidth -= ctrlSize.x;
+                       }
+                       if (tabControlSize[i].y >= tabHeight && fixedTabHeight == SWT.DEFAULT) {
+                               overflow[i] = true;
+                       }
+               }
+       } else {
+               for (int i = 0; i < controls.length; i++) {
+                       int alignment = controlAlignments[i];
+                       Point ctrlSize = tabControlSize[i];
+                       if ((alignment & SWT.TRAIL) != 0) {
+                               if ((alignment & (SWT.FILL | SWT.WRAP)) == 0) {
+                                       x -= ctrlSize.x;
+                                       rects[i].width = ctrlSize.x;
+                                       rects[i].height = getControlHeight(ctrlSize);
+                                       rects[i].x = x;
+                                       rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
+                               } else if (((alignment & (SWT.WRAP)) != 0 && ctrlSize.x < availableWidth)) {
+                                       x -= ctrlSize.x;
+                                       rects[i].width = ctrlSize.x;
+                                       rects[i].height = getControlHeight(ctrlSize);
+                                       rects[i].x = x;
+                                       rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
+                                       availableWidth -= ctrlSize.x;
+                               } else if ((alignment & (SWT.FILL)) != 0 && (alignment & (SWT.WRAP)) == 0) {
+                                       rects[i].width = 0;
+                                       rects[i].height = getControlHeight(ctrlSize);
+                                       rects[i].x = x;
+                                       rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
+                               } else {
+                                       if ((alignment & (SWT.WRAP)) != 0) {
+                                               overflow[i] = true;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       //Any space, distribute amongst FILL
+       if (availableWidth > 0) {
+               int fillCount = 0;
+               for (int i = 0; i < controls.length; i++) {
+                       int alignment = controlAlignments[i];
+                       if ((alignment & SWT.TRAIL) != 0 && (alignment & SWT.FILL) != 0 && !overflow[i]) {
+                               fillCount++;
+                       }
+               }
+               if (fillCount != 0) {
+                       int extraSpace = availableWidth/fillCount;
+                       int addedSpace = 0;
+                       for (int i = 0; i < controls.length; i++) {
+                               int alignment = controlAlignments[i];
+                               if ((alignment & SWT.TRAIL) != 0) {
+                                       if ((alignment & SWT.FILL) != 0 && !overflow[i]) {
+                                               rects[i].width += extraSpace;
+                                               addedSpace += extraSpace;
+                                       }
+                                       if (!overflow[i]) {
+                                               rects[i].x -= addedSpace;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       //Go through overflow laying out all wrapped controls
+       Rectangle bodyTrim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
+       int bodyRight = bodyTrim.width + bodyTrim.x;
+       int bodyLeft = -bodyTrim.x;
+       int bodyWidth = size.x - bodyLeft - bodyRight;
+       x = size.x - bodyRight;
+       int y = onBottom ? this.getSize().y - getTabHeight() + 2*bodyTrim.y : -bodyTrim.y;
+       availableWidth = bodyWidth;
+       int maxHeight = 0;
+       for (int i = 0; i < controls.length; i++) {
+               Point ctrlSize = tabControlSize[i];
+               if (overflow[i]) {
+                       if (availableWidth > ctrlSize.x) {
+                               x -= ctrlSize.x;
+                               rects[i].width = ctrlSize.x;
+                               rects[i].y = onBottom ? y - ctrlSize.y : y;
+                               rects[i].height = ctrlSize.y;
+                               rects[i].x = x;
+                               availableWidth -= ctrlSize.x;
+                               maxHeight = Math.max(maxHeight, ctrlSize.y);
+                       } else {
+                               x = size.x - bodyRight;
+                               y += maxHeight;
+                               maxHeight = 0;
+                               availableWidth = bodyWidth;
+                               if (availableWidth > ctrlSize.x) {
+                                       //Relayout this control in the next line
+                                       i--;
+                               } else {
+                                       ctrlSize = controls[i].isDisposed() ? new Point(0,0) : controls[i].computeSize(bodyWidth, SWT.DEFAULT);
+                                       rects[i].width = bodyWidth;
+                                       rects[i].y = onBottom ? y - ctrlSize.y : y;
+                                       rects[i].height = ctrlSize.y;
+                                       rects[i].x = size.x - ctrlSize.x - bodyRight;
+                                       y += ctrlSize.y;
+                               }
+                       }
+               }
+       }
+
+       if (showChevron) {
+               int i = 0, lastIndex = -1;
+               while (i < priority.length && items[priority[i]].showing) {
+                       lastIndex = Math.max(lastIndex, priority[i++]);
+               }
+               if (lastIndex == -1) lastIndex = selectedIndex;
+               if (lastIndex != -1) {
+                       CTabItem lastItem = items[lastIndex];
+                       int w = lastItem.x + lastItem.width + SPACING;
+                       if (!simple && lastIndex == selectedIndex) w -= (renderer.curveIndent - 7);
+                       rects[controls.length - 1].x = w;
+               }
+       }
+
+       if (position != null) position[0] = overflow;
+       return rects;
+}
+
+int getControlHeight(Point ctrlSize) {
+       return fixedTabHeight == SWT.DEFAULT ?  Math.max(tabHeight - 1, ctrlSize.y) : ctrlSize.y;
+}
+/*
+* This class was not intended to be subclassed but this restriction
+* cannot be enforced without breaking backward compatibility.
+*/
+//protected void checkSubclass () {
+//     String name = getClass ().getName ();
+//     int index = name.lastIndexOf ('.');
+//     if (!name.substring (0, index + 1).equals ("org.eclipse.swt.custom.")) {
+//             SWT.error (SWT.ERROR_INVALID_SUBCLASS);
+//     }
+//}
+@Override
+public Rectangle computeTrim (int x, int y, int width, int height) {
+       checkWidget();
+       Rectangle trim =  renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, x, y, width, height);
+       Point size = new Point(width, height);
+       int wrapHeight = getWrappedHeight(size);
+       if (onBottom) {
+               trim.height += wrapHeight;
+       } else {
+               trim.y -= wrapHeight;
+               trim.height += wrapHeight;
+       }
+       return trim;
+}
+Image createButtonImage(Display display, int button) {
+       GC tempGC = new GC (this);
+       Point size = renderer.computeSize(button, SWT.NONE, tempGC, SWT.DEFAULT, SWT.DEFAULT);
+       tempGC.dispose();
+
+       Rectangle trim = renderer.computeTrim(button, SWT.NONE, 0, 0, 0, 0);
+       Image image = new Image (display, size.x - trim.width, size.y - trim.height);
+       GC gc = new GC (image);
+       Color transColor = renderer.parent.getBackground();
+       gc.setBackground(transColor);
+       gc.fillRectangle(image.getBounds());
+       renderer.draw(button, SWT.NONE, new Rectangle(trim.x, trim.y, size.x, size.y), gc);
+       gc.dispose ();
+
+       final ImageData imageData = image.getImageData (DPIUtil.getDeviceZoom ());
+       imageData.transparentPixel = imageData.palette.getPixel(transColor.getRGB());
+       image.dispose();
+       image = new Image(display, new AutoScaleImageDataProvider(display, imageData, DPIUtil.getDeviceZoom()));
+       return image;
+}
+void createItem (CTabItem item, int index) {
+       if (0 > index || index > getItemCount ())SWT.error (SWT.ERROR_INVALID_RANGE);
+       item.parent = this;
+       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;
+       if (selectedIndex >= index) selectedIndex ++;
+       int[] newPriority = new int[priority.length + 1];
+       int next = 0,  priorityIndex = priority.length;
+       for (int i = 0; i < priority.length; i++) {
+               if (!mru && priority[i] == index) {
+                       priorityIndex = next++;
+               }
+               newPriority[next++] = priority[i] >= index ? priority[i] + 1 : priority[i];
+       }
+       newPriority[priorityIndex] = index;
+       priority = newPriority;
+
+       if (items.length == 1) {
+               updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
+       } else {
+               updateFolder(REDRAW_TABS);
+       }
+}
+void destroyItem (CTabItem item) {
+       if (inDispose) return;
+       int index = indexOf(item);
+       if (index == -1) return;
+
+       if (items.length == 1) {
+               items = new CTabItem[0];
+               priority = new int[0];
+               firstIndex = -1;
+               selectedIndex = -1;
+
+               Control control = item.control;
+               if (control != null && !control.isDisposed()) {
+                       control.setVisible(false);
+               }
+               setToolTipText(null);
+               GC gc = new GC(this);
+               setButtonBounds(gc);
+               gc.dispose();
+               redraw();
+               return;
+       }
+
+       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;
+
+       int[] newPriority = new int[priority.length - 1];
+       int next = 0;
+       for (int i = 0; i < priority.length; i++) {
+               if (priority [i] == index) continue;
+               newPriority[next++] = priority[i] > index ? priority[i] - 1 : priority [i];
+       }
+       priority = newPriority;
+
+       // move the selection if this item is selected
+       if (selectedIndex == index) {
+               Control control = item.getControl();
+               selectedIndex = -1;
+               int nextSelection = mru ? priority[0] : Math.max(0, index - 1);
+               setSelection(nextSelection, true);
+               if (control != null && !control.isDisposed()) {
+                       control.setVisible(false);
+               }
+       } else if (selectedIndex > index) {
+               selectedIndex --;
+       }
+
+       requestLayout();
+       updateFolder(UPDATE_TAB_HEIGHT | REDRAW_TABS);
+}
+
+/**
+ * Returns <code>true</code> if the receiver's border is visible.
+ *
+ * @return the receiver's border visibility state
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public boolean getBorderVisible() {
+       checkWidget();
+       return borderVisible;
+}
+ToolBar getChevron() {
+       if (chevronTb == null) {
+               chevronTb = new ToolBar(this, SWT.FLAT);
+               initAccessibleChevronTb();
+               addTabControl(chevronTb, SWT.TRAIL, -1, false);
+       }
+       if (chevronItem == null) {
+               chevronItem = new ToolItem(chevronTb, SWT.PUSH);
+               chevronItem.setToolTipText(SWT.getMessage("SWT_ShowList"));
+               chevronItem.addListener(SWT.Selection, listener);
+       }
+       return chevronTb;
+}
+/**
+ * Returns <code>true</code> if the chevron button
+ * is visible when necessary.
+ *
+ * @return the visibility of the chevron button
+ *
+ * @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 getChevronVisible() {
+       checkWidget();
+       return chevronVisible;
+}
+@Override
+public Rectangle getClientArea() {
+       checkWidget();
+       //TODO: HACK - find a better way to get padding
+       Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.FILL, 0, 0, 0, 0);
+       Point size = getSize();
+       int wrapHeight = getWrappedHeight(size);
+       if (onBottom) {
+               trim.height += wrapHeight;
+       } else {
+               trim.y -= wrapHeight;
+               trim.height += wrapHeight;
+       }
+       if (minimized) return new Rectangle(-trim.x, -trim.y, 0, 0);
+       int width = size.x - trim.width;
+       int height = size.y - trim.height;
+       return new Rectangle(-trim.x, -trim.y, width, height);
+}
+
+/**
+ * Return the tab that is located at the specified index.
+ *
+ * @param index the index of the tab item
+ * @return the item at the specified index
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is out of range</li>
+ * </ul>
+ * @exception SWTException <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 CTabItem getItem (int index) {
+       /*
+        * This call is intentionally commented out, to allow this getter method to be
+        * called from a thread which is different from one that created the widget.
+        */
+       //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.
+ *
+ * @param pt the point in coordinates relative to the CTabFolder
+ * @return the item at a point or null
+ *
+ * @exception SWTException <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 CTabItem getItem (Point pt) {
+       /*
+        * This call is intentionally commented out, to allow this getter method to be
+        * called from a thread which is different from one that created the widget.
+        */
+       //checkWidget();
+       if (items.length == 0) return null;
+       runUpdate();
+       Point size = getSize();
+       Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
+       if (size.x <= trim.width) return null;
+       for (int i = 0; i < priority.length; i++) {
+               CTabItem item = items[priority[i]];
+               Rectangle rect = item.getBounds();
+               if (rect.contains(pt)) return item;
+       }
+       return null;
+}
+/**
+ * Return the number of tabs in the folder.
+ *
+ * @return the number of tabs in the folder
+ *
+ * @exception SWTException <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 getItemCount(){
+       //checkWidget();
+       return items.length;
+}
+/**
+ * Return the tab items.
+ *
+ * @return the tab items
+ *
+ * @exception SWTException <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 CTabItem [] getItems() {
+       /*
+        * This call is intentionally commented out, to allow this getter method to be
+        * called from a thread which is different from one that created the widget.
+        */
+       //checkWidget();
+       CTabItem[] tabItems = new CTabItem [items.length];
+       System.arraycopy(items, 0, tabItems, 0, items.length);
+       return tabItems;
+}
+int getLeftItemEdge (GC gc, int part){
+       Rectangle trim = renderer.computeTrim(part, SWT.NONE, 0, 0, 0, 0);
+       int x = -trim.x;
+       int width = 0;
+       for (int i = 0; i < controls.length; i++) {
+               if ((controlAlignments[i] & SWT.LEAD) != 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
+                       width += controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+               }
+       }
+       if (width != 0) width += SPACING * 2;
+       x += width;
+       return Math.max(0, x);
+}
+/*
+ * Return the lowercase of the first non-'&' character following
+ * an '&' character in the given string. If there are no '&'
+ * characters in the given string, return '\0'.
+ */
+char _findMnemonic (String string) {
+       if (string == null) return '\0';
+       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 Character.toLowerCase (string.charAt (index));
+               index++;
+       } while (index < length);
+       return '\0';
+}
+String stripMnemonic (String string) {
+       int index = 0;
+       int length = string.length ();
+       do {
+               while ((index < length) && (string.charAt (index) != '&')) index++;
+               if (++index >= length) return string;
+               if (string.charAt (index) != '&') {
+                       return string.substring(0, index-1) + string.substring(index, length);
+               }
+               index++;
+       } while (index < length);
+       return string;
+}
+/**
+ * Returns <code>true</code> if the receiver is minimized.
+ *
+ * @return the receiver's minimized state
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public boolean getMinimized() {
+       checkWidget();
+       return minimized;
+}
+/**
+ * Returns <code>true</code> if the minimize button
+ * is visible.
+ *
+ * @return the visibility of the minimized button
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public boolean getMinimizeVisible() {
+       checkWidget();
+       return showMin;
+}
+/**
+ * Returns the number of characters that will
+ * appear in a fully compressed tab.
+ *
+ * @return number of characters that will appear in a fully compressed tab
+ *
+ * @since 3.0
+ */
+public int getMinimumCharacters() {
+       checkWidget();
+       return minChars;
+}
+
+/**
+ * Returns <code>true</code> if the receiver is maximized.
+ * <p>
+ *
+ * @return the receiver's maximized state
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public boolean getMaximized() {
+       checkWidget();
+       return maximized;
+}
+/**
+ * Returns <code>true</code> if the maximize button
+ * is visible.
+ *
+ * @return the visibility of the maximized button
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public boolean getMaximizeVisible() {
+       checkWidget();
+       return showMax;
+}
+/**
+ * Returns <code>true</code> if the receiver displays most
+ * recently used tabs and <code>false</code> otherwise.
+ * <p>
+ * When there is not enough horizontal space to show all the tabs,
+ * by default, tabs are shown sequentially from left to right in
+ * order of their index.  When the MRU visibility is turned on,
+ * the tabs that are visible will be the tabs most recently selected.
+ * Tabs will still maintain their left to right order based on index
+ * but only the most recently selected tabs are visible.
+ * <p>
+ * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
+ * "Tab 3" and "Tab 4" (in order by index).  The user selects
+ * "Tab 1" and then "Tab 3".  If the CTabFolder is now
+ * compressed so that only two tabs are visible, by default,
+ * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
+ * selected and "Tab 2" because it is the previous item in index order).
+ * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
+ * and "Tab 3" (in that order from left to right).</p>
+ *
+ * @return the receiver's header's visibility state
+ *
+ * @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>
+ *
+ * @since 3.1
+ */
+public boolean getMRUVisible() {
+       checkWidget();
+       return mru;
+}
+/**
+ * Returns the receiver's renderer.
+ *
+ * @return the receiver's renderer
+ *
+ * @exception SWTException <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>
+ *
+ * @see #setRenderer(CTabFolderRenderer)
+ * @see CTabFolderRenderer
+ *
+ * @since 3.6
+ */
+public CTabFolderRenderer getRenderer() {
+       checkWidget();
+       return renderer;
+}
+int getRightItemEdge (GC gc){
+       Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
+       int x = getSize().x - (trim.width + trim.x);
+       int width = 0;
+       for (int i = 0; i < controls.length; i++) {
+               int align = controlAlignments[i];
+               if ((align & SWT.WRAP) == 0 && (align & SWT.LEAD) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
+                       Point rightSize = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT);
+                       width += rightSize.x;
+               }
+       }
+       if (width != 0) width += SPACING * 2;
+       x -= width;
+       return Math.max(0, x);
+}
+/**
+ * Return the selected tab item, or null if there is no selection.
+ *
+ * @return the selected tab item, or null if none has been selected
+ *
+ * @exception SWTException <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 CTabItem getSelection() {
+       /*
+        * This call is intentionally commented out, to allow this getter method to be
+        * called from a thread which is different from one that created the widget.
+        */
+       //checkWidget();
+       if (selectedIndex == -1) return null;
+       return items[selectedIndex];
+}
+/**
+ * Returns the receiver's selection background color.
+ *
+ * @return the selection background color of the receiver
+ *
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Color getSelectionBackground() {
+       checkWidget();
+       return selectionBackground;
+}
+/**
+ * Returns the receiver's selection foreground color.
+ *
+ * @return the selection foreground color of the receiver
+ *
+ * @exception SWTException <ul>
+ *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Color getSelectionForeground() {
+       checkWidget();
+       return selectionForeground;
+}
+/**
+ * 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
+ *
+ * @exception SWTException <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 getSelectionIndex() {
+       /*
+        * This call is intentionally commented out, to allow this getter method to be
+        * called from a thread which is different from one that created the widget.
+        */
+       //checkWidget();
+       return selectedIndex;
+}
+/**
+ * Returns <code>true</code> if the CTabFolder is rendered
+ * with a simple, traditional shape.
+ *
+ * @return <code>true</code> if the CTabFolder is rendered with a simple shape
+ *
+ * @since 3.0
+ */
+public boolean getSimple() {
+       checkWidget();
+       return simple;
+}
+/**
+ * Returns <code>true</code> if the CTabFolder only displays the selected tab
+ * and <code>false</code> if the CTabFolder displays multiple tabs.
+ *
+ * @return <code>true</code> if the CTabFolder only displays the selected tab and <code>false</code> if the CTabFolder displays multiple tabs
+ *
+ * @since 3.0
+ */
+public boolean getSingle() {
+       checkWidget();
+       return single;
+}
+
+@Override
+public int getStyle() {
+       int style = super.getStyle();
+       style &= ~(SWT.TOP | SWT.BOTTOM);
+       style |= onBottom ? SWT.BOTTOM : SWT.TOP;
+       style &= ~(SWT.SINGLE | SWT.MULTI);
+       style |= single ? SWT.SINGLE : SWT.MULTI;
+       if (borderVisible) style |= SWT.BORDER;
+       style &= ~SWT.CLOSE;
+       if (showClose) style |= SWT.CLOSE;
+       return style;
+}
+/**
+ * Returns the height of the tab
+ *
+ * @return the height of the tab
+ *
+ * @exception SWTException <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();
+       if (fixedTabHeight != SWT.DEFAULT) return fixedTabHeight;
+       return tabHeight - 1; // -1 for line drawn across top of tab //TODO: replace w/ computeTrim of tab area?
+}
+/**
+ * Returns the position of the tab.  Possible values are SWT.TOP or SWT.BOTTOM.
+ *
+ * @return the position of the tab
+ *
+ * @exception SWTException <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 getTabPosition(){
+       checkWidget();
+       return onBottom ? SWT.BOTTOM : SWT.TOP;
+}
+/**
+ * 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.
+ *
+ * @return the control in the top right corner of the tab folder or null
+ *
+ * @exception  SWTException <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>
+ *
+ * @since 2.1
+ */
+public Control getTopRight() {
+       checkWidget();
+       return topRight;
+}
+/**
+ * Returns the alignment of the top right control.
+ *
+ * @return the alignment of the top right control which is either
+ * <code>SWT.RIGHT</code> or <code>SWT.FILL</code>
+ *
+ * @exception  SWTException <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>
+ *
+ * @since 3.6
+ */
+public int getTopRightAlignment() {
+       checkWidget();
+       return topRightAlignment;
+}
+/**
+ * Returns <code>true</code> if the close button appears
+ * when the user hovers over an unselected tabs.
+ *
+ * @return <code>true</code> if the close button appears on unselected tabs
+ *
+ * @since 3.0
+ */
+public boolean getUnselectedCloseVisible() {
+       checkWidget();
+       return showUnselectedClose;
+}
+/**
+ * Returns <code>true</code> if an image appears
+ * in unselected tabs.
+ *
+ * @return <code>true</code> if an image appears in unselected tabs
+ *
+ * @since 3.0
+ */
+public boolean getUnselectedImageVisible() {
+       checkWidget();
+       return showUnselectedImage;
+}
+/**
+ * Return the index of the specified tab or -1 if the tab is not
+ * in the receiver.
+ *
+ * @param item the tab item for which the index is required
+ *
+ * @return the index of the specified tab item or -1
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @exception SWTException <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 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;
+}
+void initAccessible() {
+       final Accessible accessible = getAccessible();
+       accessible.addAccessibleListener(new AccessibleAdapter() {
+               @Override
+               public void getName(AccessibleEvent e) {
+                       CTabItem item = null;
+                       int childID = e.childID;
+                       if (childID == ACC.CHILDID_SELF) {
+                               if (selectedIndex != -1) {
+                                       item = items[selectedIndex];
+                               }
+                       } else if (childID >= 0 && childID < items.length) {
+                               item = items[childID];
+                       }
+                       e.result = item == null ? null : stripMnemonic(item.getText());
+               }
+
+               @Override
+               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;
+               }
+
+               @Override
+               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 = _findMnemonic(text);
+                                       if (mnemonic != '\0') {
+                                               shortcut = SWT.getMessage ("SWT_Page_Mnemonic", new Object[] {Character.valueOf(mnemonic)}); //$NON-NLS-1$
+                                       }
+                               }
+                       }
+                       if (childID == ACC.CHILDID_SELF) {
+                               shortcut = SWT.getMessage ("SWT_SwitchPage_Shortcut"); //$NON-NLS-1$
+                       }
+                       e.result = shortcut;
+               }
+       });
+
+       accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
+               @Override
+               public void getChildAtPoint(AccessibleControlEvent e) {
+                       Point testPoint = toControl(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.x = location.y = 0;
+                               location.height = location.height - getClientArea().height;
+                               if (location.contains(testPoint)) {
+                                       childID = ACC.CHILDID_SELF;
+                               }
+                       }
+                       e.childID = childID;
+               }
+
+               @Override
+               public void getLocation(AccessibleControlEvent e) {
+                       Rectangle location = null;
+                       Point pt = null;
+                       int childID = e.childID;
+                       if (childID == ACC.CHILDID_SELF) {
+                               location = getBounds();
+                               pt = getParent().toDisplay(location.x, location.y);
+                       } else {
+                               if (childID >= 0 && childID < items.length && items[childID].showing) {
+                                       location = items[childID].getBounds();
+                               }
+                               if (location != null) {
+                                       pt = toDisplay(location.x, location.y);
+                               }
+                       }
+                       if (location != null && pt != null) {
+                               e.x = pt.x;
+                               e.y = pt.y;
+                               e.width = location.width;
+                               e.height = location.height;
+                       }
+               }
+
+               @Override
+               public void getChildCount(AccessibleControlEvent e) {
+                       e.detail = items.length;
+               }
+
+               @Override
+               public void getDefaultAction(AccessibleControlEvent e) {
+                       String action = null;
+                       int childID = e.childID;
+                       if (childID >= 0 && childID < items.length) {
+                               action = SWT.getMessage ("SWT_Switch"); //$NON-NLS-1$
+                       }
+                       e.result = action;
+               }
+
+               @Override
+               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;
+               }
+
+               @Override
+               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;
+               }
+
+               @Override
+               public void getSelection(AccessibleControlEvent e) {
+                       e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex;
+               }
+
+               @Override
+               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;
+               }
+
+               @Override
+               public void getChildren(AccessibleControlEvent e) {
+                       int childIdCount = items.length;
+                       Object[] children = new Object[childIdCount];
+                       for (int i = 0; i < childIdCount; i++) {
+                               children[i] = Integer.valueOf(i);
+                       }
+                       e.children = children;
+               }
+       });
+
+       addListener(SWT.Selection, event -> {
+               if (isFocusControl()) {
+                       if (selectedIndex == -1) {
+                               accessible.setFocus(ACC.CHILDID_SELF);
+                       } else {
+                               accessible.setFocus(selectedIndex);
+                       }
+               }
+       });
+
+       addListener(SWT.FocusIn, event -> {
+               if (selectedIndex == -1) {
+                       accessible.setFocus(ACC.CHILDID_SELF);
+               } else {
+                       accessible.setFocus(selectedIndex);
+               }
+       });
+}
+void initAccessibleMinMaxTb() {
+       minMaxTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
+               @Override
+               public void getName(AccessibleEvent e) {
+                       if (e.childID != ACC.CHILDID_SELF) {
+                               if (minItem != null && e.childID == minMaxTb.indexOf(minItem)) {
+                                       e.result = minItem.getToolTipText();
+                               } else if (maxItem != null && e.childID == minMaxTb.indexOf(maxItem)) {
+                                       e.result = maxItem.getToolTipText();
+                               }
+                       }
+               }
+       });
+}
+void initAccessibleChevronTb() {
+       chevronTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
+               @Override
+               public void getName(AccessibleEvent e) {
+                       if (e.childID != ACC.CHILDID_SELF) {
+                               if (chevronItem != null && e.childID == chevronTb.indexOf(chevronItem)) {
+                                       e.result = chevronItem.getToolTipText();
+                               }
+                       }
+               }
+       });
+}
+void onKeyDown (Event event) {
+       runUpdate();
+       switch (event.keyCode) {
+               case SWT.ARROW_LEFT:
+               case SWT.ARROW_RIGHT:
+                       int count = items.length;
+                       if (count == 0) return;
+                       if (selectedIndex  == -1) return;
+                       int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
+                       int offset =  event.keyCode == leadKey ? -1 : 1;
+                       int index;
+                       if (!mru) {
+                               index = selectedIndex + offset;
+                       } else {
+                               int[] visible = new int[items.length];
+                               int idx = 0;
+                               int current = -1;
+                               for (int i = 0; i < items.length; i++) {
+                                       if (items[i].showing) {
+                                               if (i == selectedIndex) current = idx;
+                                               visible [idx++] = i;
+                                       }
+                               }
+                               if (current + offset >= 0 && current + offset < idx){
+                                       index = visible [current + offset];
+                               } else {
+                                       if (showChevron) {
+                                               Rectangle chevronRect = chevronItem.getBounds();
+                                               chevronRect = event.display.map(chevronTb, this, chevronRect);
+                                               CTabFolderEvent e = new CTabFolderEvent(this);
+                                               e.widget = this;
+                                               e.time = event.time;
+                                               e.x = chevronRect.x;
+                                               e.y = chevronRect.y;
+                                               e.width = chevronRect.width;
+                                               e.height = chevronRect.height;
+                                               e.doit = true;
+                                               for (int i = 0; i < folderListeners.length; i++) {
+                                                       folderListeners[i].showList(e);
+                                               }
+                                               if (e.doit && !isDisposed()) {
+                                                       showList(chevronRect);
+                                               }
+                                       }
+                                       return;
+                               }
+                       }
+                       if (index < 0 || index >= count) return;
+                       setSelection (index, true);
+                       forceFocus();
+       }
+}
+void onDispose(Event event) {
+       removeListener(SWT.Dispose, listener);
+       notifyListeners(SWT.Dispose, event);
+       event.type = SWT.None;
+       /*
+        * 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;
+
+       if (showMenu != null && !showMenu.isDisposed()) {
+               showMenu.dispose();
+               showMenu = null;
+       }
+       int length = items.length;
+       for (int i = 0; i < length; i++) {
+               if (items[i] != null) {
+                       items[i].dispose();
+               }
+       }
+
+       gradientColors = null;
+
+       selectionGradientColors = null;
+       selectionGradientPercents = null;
+       selectionBgImage = null;
+
+       selectionBackground = null;
+       selectionForeground = null;
+
+       if (controlBkImages != null) {
+               for (int i = 0; i < controlBkImages.length; i++) {
+                       if (controlBkImages[i] != null) {
+                               controlBkImages[i].dispose();
+                               controlBkImages[i] = null;
+                       }
+               }
+               controlBkImages = null;
+       }
+       controls = null;
+       controlAlignments = null;
+       controlRects = null;
+
+       if (maxImage != null) maxImage.dispose();
+       maxImage = null;
+
+       if (minImage != null) minImage.dispose();
+       minImage = null;
+
+       if (chevronImage != null) chevronImage.dispose();
+       chevronImage = null;
+
+       if (renderer != null) renderer.dispose();
+       renderer = null;
+
+       minItem = null;
+       maxItem = null;
+       minMaxTb = null;
+
+       chevronItem = null;
+       chevronTb = null;
+
+       if (folderListeners.length != 0) folderListeners = new CTabFolder2Listener[0];
+       if (tabListeners.length != 0) tabListeners = new CTabFolderListener[0];
+}
+void onDragDetect(Event event) {
+       boolean consume = false;
+       for (int i = 0; i < items.length; i++) {
+               if (items[i].closeRect.contains(event.x, event.y)) {
+                               consume = true;
+                               break;
+               }
+       }
+       if (consume) {
+               event.type = SWT.None;
+       }
+}
+void onFocus(Event event) {
+       checkWidget();
+       if (selectedIndex >= 0) {
+               redraw();
+       } else {
+               setSelection(0, true);
+       }
+}
+boolean onMnemonic (Event event, boolean doit) {
+       char key = event.character;
+       for (int i = 0; i < items.length; i++) {
+               if (items[i] != null) {
+                       char mnemonic = _findMnemonic (items[i].getText ());
+                       if (mnemonic != '\0') {
+                               if (Character.toLowerCase (key) == mnemonic) {
+                                       if (doit) {
+                                               setSelection(i, true);
+                                               forceFocus();
+                                       }
+                                       return true;
+                               }
+                       }
+               }
+       }
+       return false;
+}
+void onMenuDetect(Event event) {
+       if (event.detail == SWT.MENU_KEYBOARD) {
+               if (selectedIndex != -1) {
+                       CTabItem item = items[selectedIndex];
+                       Rectangle rect = getDisplay().map(this, null, item.getBounds());
+                       if (!rect.contains(event.x, event.y)) {
+                               /* If the mouse is not in the currently-selected tab,
+                                * then pop up the menu near the top-right corner of the current tab.
+                                */
+                               Rectangle itemTrim = renderer.computeTrim(selectedIndex, SWT.NONE, 0, 0, 0, 0);
+                               Rectangle closeTrim = renderer.computeTrim(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, 0, 0, 0, 0);
+                               event.x = rect.x + rect.width - item.closeRect.width + itemTrim.x - closeTrim.width;
+                               event.y = rect.y - itemTrim.y - closeTrim.y;
+                       }
+               }
+       }
+}
+void onMouseDoubleClick(Event event) {
+       if (event.button != 1 ||
+               (event.stateMask & SWT.BUTTON2) != 0 ||
+               (event.stateMask & SWT.BUTTON3) != 0) return;
+       Event e = new Event();
+       e.item = getItem(new Point(event.x, event.y));
+       if (e.item != null) {
+               notifyListeners(SWT.DefaultSelection, e);
+       }
+}
+void onMouse(Event event) {
+       if( isDisposed() ) {
+               return;
+       }
+       int x = event.x, y = event.y;
+       switch (event.type) {
+               case SWT.MouseEnter: {
+                       setToolTipText(null);
+                       break;
+               }
+               case SWT.MouseExit: {
+                       for (int i=0; i<items.length; i++) {
+                               CTabItem item = items[i];
+                               if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND) {
+                                       item.closeImageState = SWT.BACKGROUND;
+                                       redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
+                               }
+                               if ((item.state & SWT.HOT) != 0) {
+                                       item.state &= ~SWT.HOT;
+                                       redraw(item.x, item.y, item.width, item.height, false);
+                               }
+                               if (i == selectedIndex && item.closeImageState != SWT.NONE) {
+                                       item.closeImageState = SWT.NONE;
+                                       redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
+                               }
+                       }
+                       break;
+               }
+               case SWT.MouseHover:
+               case SWT.MouseDown: {
+                       if (hoverTb && hoverRect.contains(x, y) && !hovering) {
+                               hovering = true;
+                               updateItems();
+                               hoverTimerRunning = true;
+                               event.display.timerExec(2000, new Runnable() {
+                                       @Override
+                                       public void run() {
+                                               if (isDisposed()) return;
+                                               if (hovering) {
+                                                       Display display = getDisplay();
+                                                       Control c = display.getCursorControl();
+                                                       boolean reschedule = false;
+                                                       if (c != null) {
+                                                               for (int i = 0; i < controls.length; i++) {
+                                                                       Control temp = c;
+                                                                       do {
+                                                                               if (temp.equals(controls[i])) {
+                                                                                       reschedule = true;
+                                                                               } else {
+                                                                                       temp = temp.getParent();
+                                                                                       if (temp == null || temp.equals(CTabFolder.this)) break;
+                                                                               }
+                                                                       } while (!reschedule);
+                                                                       if (reschedule) break;
+                                                               }
+                                                       }
+                                                       if (reschedule && hoverTimerRunning) {
+                                                               display.timerExec(2000, this);
+                                                       } else {
+                                                               hovering = false;
+                                                               updateItems();
+                                                       }
+                                               }
+                                       }
+                               });
+                               return;
+                       }
+                       if (event.button != 1) return;
+                       CTabItem item = null;
+                       if (single) {
+                               if (selectedIndex != -1) {
+                                       Rectangle bounds = items[selectedIndex].getBounds();
+                                       if (bounds.contains(x, y)){
+                                               item = items[selectedIndex];
+                                       }
+                               }
+                       } else {
+                               for (int i=0; i<items.length; i++) {
+                                       Rectangle bounds = items[i].getBounds();
+                                       if (bounds.contains(x, y)){
+                                               item = items[i];
+                                       }
+                               }
+                       }
+                       if (item != null) {
+                               if (item.closeRect.contains(x,y)){
+                                       item.closeImageState = SWT.SELECTED;
+                                       redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
+                                       update();
+                                       return;
+                               }
+                               int index = indexOf(item);
+                               if (item.showing){
+                                       int oldSelectedIndex = selectedIndex;
+                                       setSelection(index, true);
+                                       if (oldSelectedIndex == selectedIndex) {
+                                               /* If the click is on the selected tabitem, then set focus to the tabfolder */
+                                               forceFocus();
+                                       }
+                               }
+                               return;
+                       }
+                       break;
+               }
+               case SWT.MouseMove: {
+                       _setToolTipText(event.x, event.y);
+                       boolean close = false;
+                       for (int i=0; i<items.length; i++) {
+                               CTabItem item = items[i];
+                               close = false;
+                               if (item.getBounds().contains(x, y)) {
+                                       close = true;
+                                       if (item.closeRect.contains(x, y)) {
+                                               if (item.closeImageState != SWT.SELECTED && item.closeImageState != SWT.HOT) {
+                                                       item.closeImageState = SWT.HOT;
+                                                       redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
+                                               }
+                                       } else {
+                                               if (item.closeImageState != SWT.NONE) {
+                                                       item.closeImageState = SWT.NONE;
+                                                       redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
+                                               }
+                                       }
+                                       if ((item.state & SWT.HOT) == 0) {
+                                               item.state |= SWT.HOT;
+                                               redraw(item.x, item.y, item.width, item.height, false);
+                                       }
+                               }
+                               if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND && !close) {
+                                       item.closeImageState = SWT.BACKGROUND;
+                                       redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
+                               }
+                               if ((item.state & SWT.HOT) != 0 && !close) {
+                                       item.state &= ~SWT.HOT;
+                                       redraw(item.x, item.y, item.width, item.height, false);
+                               }
+                               if (i == selectedIndex && item.closeImageState != SWT.NONE && !close) {
+                                       item.closeImageState = SWT.NONE;
+                                       redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
+                               }
+                       }
+                       break;
+               }
+               case SWT.MouseUp: {
+                       if (event.button != 1) return;
+                       CTabItem item = null;
+                       if (single) {
+                               if (selectedIndex != -1) {
+                                       Rectangle bounds = items[selectedIndex].getBounds();
+                                       if (bounds.contains(x, y)){
+                                               item = items[selectedIndex];
+                                       }
+                               }
+                       } else {
+                               for (int i=0; i<items.length; i++) {
+                                       Rectangle bounds = items[i].getBounds();
+                                       if (bounds.contains(x, y)){
+                                               item = items[i];
+                                       }
+                               }
+                       }
+                       if (item != null) {
+                               if (item.closeRect.contains(x,y)) {
+                                       boolean selected = item.closeImageState == SWT.SELECTED;
+                                       item.closeImageState = SWT.HOT;
+                                       redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
+                                       if (!selected) return;
+                                       CTabFolderEvent e = new CTabFolderEvent(this);
+                                       e.widget = this;
+                                       e.time = event.time;
+                                       e.item = item;
+                                       e.doit = true;
+                                       for (int j = 0; j < folderListeners.length; j++) {
+                                               CTabFolder2Listener listener = folderListeners[j];
+                                               listener.close(e);
+                                       }
+                                       for (int j = 0; j < tabListeners.length; j++) {
+                                               CTabFolderListener listener = tabListeners[j];
+                                               listener.itemClosed(e);
+                                       }
+                                       if (e.doit) item.dispose();
+                                       if (!isDisposed() && item.isDisposed()) {
+                                               Display display = getDisplay();
+                                               Point pt = display.getCursorLocation();
+                                               pt = display.map(null, this, pt.x, pt.y);
+                                               CTabItem nextItem = getItem(pt);
+                                               if (nextItem != null) {
+                                                       if (nextItem.closeRect.contains(pt)) {
+                                                               if (nextItem.closeImageState != SWT.SELECTED && nextItem.closeImageState != SWT.HOT) {
+                                                                       nextItem.closeImageState = SWT.HOT;
+                                                                       redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
+                                                               }
+                                                       } else {
+                                                               if (nextItem.closeImageState != SWT.NONE) {
+                                                                       nextItem.closeImageState = SWT.NONE;
+                                                                       redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       return;
+                               }
+                       }
+               }
+       }
+}
+void onPageTraversal(Event event) {
+       int count = items.length;
+       if (count == 0) return;
+       int index = selectedIndex;
+       if (index  == -1) {
+               index = 0;
+       } else {
+               int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
+               if (!mru) {
+                       index = (selectedIndex + offset + count) % count;
+               } else {
+                       int[] visible = new int[items.length];
+                       int idx = 0;
+                       int current = -1;
+                       for (int i = 0; i < items.length; i++) {
+                               if (items[i].showing) {
+                                       if (i == selectedIndex) current = idx;
+                                       visible [idx++] = i;
+                               }
+                       }
+                       if (current + offset >= 0 && current + offset < idx){
+                               index = visible [current + offset];
+                       } else {
+                               if (showChevron) {
+                                       Rectangle chevronRect = chevronItem.getBounds();
+                                       chevronRect = event.display.map(chevronTb, this, chevronRect);
+                                       CTabFolderEvent e = new CTabFolderEvent(this);
+                                       e.widget = this;
+                                       e.time = event.time;
+                                       e.x = chevronRect.x;
+                                       e.y = chevronRect.y;
+                                       e.width = chevronRect.width;
+                                       e.height = chevronRect.height;
+                                       e.doit = true;
+                                       for (int i = 0; i < folderListeners.length; i++) {
+                                               folderListeners[i].showList(e);
+                                       }
+                                       if (e.doit && !isDisposed()) {
+                                               showList(chevronRect);
+                                       }
+                               }
+                       }
+               }
+       }
+       setSelection (index, true);
+}
+void onPaint(Event event) {
+       if (inDispose) return;
+       Font font = getFont();
+       if (oldFont == null || !oldFont.equals(font)) {
+               // handle case where  default font changes
+               oldFont = font;
+               if (!updateTabHeight(false)) {
+                       updateItems();
+                       redraw();
+                       return;
+               }
+       }
+
+       GC gc = event.gc;
+       Font gcFont = gc.getFont();
+       Color gcBackground = gc.getBackground();
+       Color gcForeground = gc.getForeground();
+
+// Useful for debugging paint problems
+//{
+//Point size = getSize();
+//gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN));
+//gc.fillRectangle(-10, -10, size.x + 20, size.y+20);
+//}
+
+       Point size = getSize();
+       Rectangle bodyRect = new Rectangle(0, 0, size.x, size.y);
+       renderer.draw(CTabFolderRenderer.PART_BODY, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
+
+       gc.setFont(gcFont);
+       gc.setForeground(gcForeground);
+       gc.setBackground(gcBackground);
+
+       renderer.draw(CTabFolderRenderer.PART_HEADER, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
+
+       gc.setFont(gcFont);
+       gc.setForeground(gcForeground);
+       gc.setBackground(gcBackground);
+
+       if (!single) {
+               for (int i=0; i < items.length; i++) {
+                       Rectangle itemBounds = items[i].getBounds();
+                       if (i != selectedIndex && event.getBounds().intersects(itemBounds)) {
+                               renderer.draw(i, SWT.BACKGROUND | SWT.FOREGROUND | items[i].state , itemBounds, gc);
+                       }
+               }
+       }
+
+       gc.setFont(gcFont);
+       gc.setForeground(gcForeground);
+       gc.setBackground(gcBackground);
+
+       if (selectedIndex != -1) {
+               renderer.draw(selectedIndex, items[selectedIndex].state | SWT.BACKGROUND | SWT.FOREGROUND, items[selectedIndex].getBounds(), gc);
+       }
+
+       gc.setFont(gcFont);
+       gc.setForeground(gcForeground);
+       gc.setBackground(gcBackground);
+
+       if (hoverTb) {
+               Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
+               int x = getSize().x - (trim.width + trim.x);
+               hoverRect = new Rectangle(x - 16 - SPACING, 2, 16, getTabHeight() - 2);
+               gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
+               x = hoverRect.x;
+               int y = hoverRect.y;
+               gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
+               gc.fillRectangle(x + hoverRect.width - 6, y, 5, 5);
+               gc.drawRectangle(x + hoverRect.width - 6, y, 5, 5);
+               gc.drawLine(x + hoverRect.width - 6, y+2, x + hoverRect.width - 6 + 5, y + 2);
+               gc.fillRectangle(x, y, 5 , 2);
+               gc.drawRectangle(x, y, 5 , 2);
+       }
+       gc.setFont(gcFont);
+       gc.setForeground(gcForeground);
+       gc.setBackground(gcBackground);
+}
+
+void onResize(Event event) {
+       if (inDispose) return;
+       if (ignoreResize) return;
+       if (updateItems()) {
+               redrawTabs();
+       }
+       Point size = getSize();
+       if (oldSize == null) {
+               redraw();
+       } else {
+               if (onBottom && size.y != oldSize.y) {
+                       redraw();
+               } else {
+                       int x1 = Math.min(size.x, oldSize.x);
+                       Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
+                       if (size.x != oldSize.x) x1 -= trim.width + trim.x - marginWidth + 2;
+                       if (!simple) x1 -= 5; // rounded top right corner
+                       int y1 = Math.min(size.y, oldSize.y);
+                       if (size.y != oldSize.y) y1 -= trim.height + trim.y - marginHeight;
+                       int x2 = Math.max(size.x, oldSize.x);
+                       int y2 = Math.max(size.y, oldSize.y);
+                       redraw(0, y1, x2, y2 - y1, false);
+                       redraw(x1, 0, x2 - x1, y2, false);
+                       if (hoverTb) {
+                               redraw(hoverRect.x, hoverRect.y, hoverRect.width, hoverRect.height, false);
+                       }
+               }
+       }
+       oldSize = size;
+}
+void onSelection(Event event) {
+       if (hovering) {
+               hovering = false;
+               updateItems();
+       }
+       if (event.widget == maxItem) {
+               CTabFolderEvent e = new CTabFolderEvent(this);
+               e.widget = CTabFolder.this;
+               e.time = event.time;
+               for (int i = 0; i < folderListeners.length; i++) {
+                       if (maximized) {
+                               folderListeners[i].restore(e);
+                       } else {
+                               folderListeners[i].maximize(e);
+                       }
+               }
+       } else if (event.widget == minItem) {
+               CTabFolderEvent e = new CTabFolderEvent(this);
+               e.widget = CTabFolder.this;
+               e.time = event.time;
+               for (int i = 0; i < folderListeners.length; i++) {
+                       if (minimized) {
+                               folderListeners[i].restore(e);
+                       } else {
+                               folderListeners[i].minimize(e);
+                       }
+               }
+       } else if (event.widget == chevronItem) {
+               Rectangle chevronRect = chevronItem.getBounds();
+               chevronRect = event.display.map(chevronTb, this, chevronRect);
+               CTabFolderEvent e = new CTabFolderEvent(this);
+               e.widget = this;
+               e.time = event.time;
+               e.x = chevronRect.x;
+               e.y = chevronRect.y;
+               e.width = chevronRect.width;
+               e.height = chevronRect.height;
+               e.doit = true;
+               for (int i = 0; i < folderListeners.length; i++) {
+                       folderListeners[i].showList(e);
+               }
+               if (e.doit && !isDisposed()) {
+                       showList(chevronRect);
+               }
+       }
+}
+void onTraverse (Event event) {
+       if (ignoreTraverse) return;
+       runUpdate();
+       switch (event.detail) {
+               case SWT.TRAVERSE_ESCAPE:
+               case SWT.TRAVERSE_RETURN:
+               case SWT.TRAVERSE_TAB_NEXT:
+               case SWT.TRAVERSE_TAB_PREVIOUS:
+                       Control focusControl = getDisplay().getFocusControl();
+                       if (focusControl == this) event.doit = true;
+                       break;
+               case SWT.TRAVERSE_MNEMONIC:
+                       event.doit = onMnemonic(event, false);
+                       break;
+               case SWT.TRAVERSE_PAGE_NEXT:
+               case SWT.TRAVERSE_PAGE_PREVIOUS:
+                       event.doit = items.length > 0;
+                       break;
+       }
+       ignoreTraverse = true;
+       notifyListeners(SWT.Traverse, event);
+       ignoreTraverse = false;
+       event.type = SWT.None;
+       if (isDisposed()) return;
+       if (!event.doit) return;
+       switch (event.detail) {
+               case SWT.TRAVERSE_MNEMONIC:
+                       onMnemonic(event, true);
+                       event.detail = SWT.TRAVERSE_NONE;
+                       break;
+               case SWT.TRAVERSE_PAGE_NEXT:
+               case SWT.TRAVERSE_PAGE_PREVIOUS:
+                       onPageTraversal(event);
+                       event.detail = SWT.TRAVERSE_NONE;
+                       break;
+       }
+}
+void redrawTabs() {
+       Point size = getSize();
+       Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
+       if (onBottom) {
+               int h = trim.height + trim.y - marginHeight;
+               redraw(0, size.y - h - 1, size.x, h + 1, false);
+       } else {
+               redraw(0, 0, size.x, -trim.y - marginHeight + 1, false);
+       }
+}
+/**
+ * Removes the listener.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @exception SWTException <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>
+ *
+ * @see #addCTabFolder2Listener(CTabFolder2Listener)
+ *
+ * @since 3.0
+ */
+public void removeCTabFolder2Listener(CTabFolder2Listener listener) {
+       checkWidget();
+       if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+       if (folderListeners.length == 0) return;
+       int index = -1;
+       for (int i = 0; i < folderListeners.length; i++) {
+               if (listener == folderListeners[i]){
+                       index = i;
+                       break;
+               }
+       }
+       if (index == -1) return;
+       if (folderListeners.length == 1) {
+               folderListeners = new CTabFolder2Listener[0];
+               return;
+       }
+       CTabFolder2Listener[] newTabListeners = new CTabFolder2Listener[folderListeners.length - 1];
+       System.arraycopy(folderListeners, 0, newTabListeners, 0, index);
+       System.arraycopy(folderListeners, index + 1, newTabListeners, index, folderListeners.length - index - 1);
+       folderListeners = newTabListeners;
+}
+/**
+ * Removes the listener.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @exception SWTException <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>
+ *
+ * @deprecated see removeCTabFolderCloseListener(CTabFolderListener)
+ */
+@Deprecated
+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];
+               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;
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's selection.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception 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 SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener(SelectionListener listener) {
+       checkWidget();
+       if (listener == null) {
+               SWT.error(SWT.ERROR_NULL_ARGUMENT);
+       }
+       removeListener(SWT.Selection, listener);
+       removeListener(SWT.DefaultSelection, listener);
+}
+
+@Override
+public void reskin(int flags) {
+       super.reskin(flags);
+       for (int i = 0; i < items.length; i++) {
+               items[i].reskin(flags);
+       }
+}
+
+@Override
+public void setBackground (Color color) {
+       super.setBackground(color);
+       renderer.createAntialiasColors(); //TODO: need better caching strategy
+       updateBkImages();
+       redraw();
+}
+/**
+ * Specify a gradient of colors to be drawn in the background of the unselected tabs.
+ * 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 <code>percents</code>
+ *                 array must be one less than the size of the <code>colors</code> array.
+ *
+ * @exception SWTException <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>
+ *
+ * @since 3.6
+ */
+public void setBackground(Color[] colors, int[] percents) {
+       setBackground(colors, percents, false);
+}
+/**
+ * Specify a gradient of colors to be drawn in the background of the unselected tab.
+ * For example to draw a vertical 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}, true);
+ * </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 <code>percents</code>
+ *                 array must be one less than the size of the <code>colors</code> array.
+ *
+ * @param vertical indicate the direction of the gradient. <code>True</code> is vertical and <code>false</code> is horizontal.
+ *
+ * @exception SWTException <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>
+ *
+ * @since 3.6
+ */
+public void setBackground(Color[] colors, int[] percents, boolean vertical) {
+       checkWidget();
+       if (colors != null) {
+               if (percents == null || percents.length != colors.length - 1) {
+                       SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+               }
+               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);
+                       }
+               }
+               if (getDisplay().getDepth() < 15) {
+                       // Don't use gradients on low color displays
+                       colors = new Color[] {colors[colors.length - 1]};
+                       percents = new int[] {};
+               }
+       }
+
+       // Are these settings the same as before?
+       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 && this.gradientVertical == vertical) return;
+       }
+       // Store the new settings
+       if (colors == null) {
+               gradientColors = null;
+               gradientPercents = null;
+               gradientVertical = false;
+               setBackground((Color)null);
+       } 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];
+               }
+               gradientVertical = vertical;
+               setBackground(gradientColors[gradientColors.length-1]);
+       }
+
+       // Refresh with the new settings
+       redraw();
+}
+@Override
+public void setBackgroundImage(Image image) {
+               super.setBackgroundImage(image);
+               renderer.createAntialiasColors(); //TODO: need better caching strategy
+               redraw();
+}
+/**
+ * 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 (borderVisible == show) return;
+       this.borderVisible = show;
+       updateFolder(REDRAW);
+}
+void setButtonBounds(GC gc) {
+       Point size = getSize();
+       // max button
+       Display display = getDisplay();
+       if (showMax) {
+               if (minMaxTb == null) {
+                       minMaxTb = new ToolBar(this, SWT.FLAT);
+                       initAccessibleMinMaxTb();
+                       addTabControl(minMaxTb, SWT.TRAIL, 0, false);
+               }
+               if (maxItem == null) {
+                       maxItem = new ToolItem(minMaxTb, SWT.PUSH);
+                       if (maxImage == null) {
+                               maxImage = createButtonImage(display, CTabFolderRenderer.PART_MAX_BUTTON);
+                       }
+                       maxItem.setImage(maxImage);
+                       maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
+                       maxItem.addListener(SWT.Selection, listener);
+               }
+       } else {
+               //might need to remove it if already there
+               if (maxItem != null) {
+                       maxItem.dispose();
+                       maxItem = null;
+               }
+       }
+       // min button
+       if (showMin) {
+               if (minMaxTb == null) {
+                       minMaxTb = new ToolBar(this, SWT.FLAT);
+                       initAccessibleMinMaxTb();
+                       addTabControl(minMaxTb, SWT.TRAIL, 0, false);
+               }
+               if (minItem == null) {
+                       minItem = new ToolItem(minMaxTb, SWT.PUSH, 0);
+                       if (minImage == null) {
+                               minImage = createButtonImage(display, CTabFolderRenderer.PART_MIN_BUTTON);
+                       }
+                       minItem.setImage(minImage);
+                       minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
+                       minItem.addListener(SWT.Selection, listener);
+               }
+       } else {
+               //might need to remove it if already there
+               if (minItem != null) {
+                       minItem.dispose();
+                       minItem = null;
+               }
+       }
+       if (minMaxTb != null && minMaxTb.getItemCount() == 0) {
+               removeTabControl(minMaxTb, false);
+               minMaxTb.dispose();
+               minMaxTb = null;
+       }
+       if (showChevron) {
+               int itemCount = items.length;
+               int count;
+               if (single) {
+                       count = selectedIndex == -1 ? itemCount : itemCount - 1;
+               } else {
+                       int showCount = 0;
+                       while (showCount < priority.length && items[priority[showCount]].showing) {
+                               showCount++;
+                       }
+                       count = itemCount - showCount;
+               }
+               if (count != chevronCount) {
+                       chevronCount = count;
+                       if (chevronImage != null) chevronImage.dispose();
+                       chevronImage = createButtonImage(display, CTabFolderRenderer.PART_CHEVRON_BUTTON);
+                       chevronItem.setImage(chevronImage);
+               }
+       }
+
+       boolean[][] overflow = new boolean[1][0];
+       Rectangle[] rects = computeControlBounds(size, overflow);
+       if (fixedTabHeight != SWT.DEFAULT) {
+               int height = fixedTabHeight;
+               if (!hovering) {
+                       hoverTb = false;
+                       Rectangle tabBounds = this.getBounds();
+                       for (int i = 0; i < rects.length; i++) {
+                               if (!(overflow[0][i])) {
+                                       if (rects[i].height > height) {
+                                               hoverTb = true;
+                                               break;
+                                       }
+                               }
+                       }
+                       if (hoverTb) {
+                               for (int i = 0; i < rects.length; i++) {
+                                       if (!(overflow[0][i])) {
+                                               if (rects[i].height > height) {
+                                                       rects[i].x = tabBounds.width + 20;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       int headerHeight = 0;
+       for (int i = 0; i < rects.length; i++) {
+               if (!overflow[0][i]) headerHeight = Math.max(rects[i].height, headerHeight);
+       }
+       boolean changed = false;
+       ignoreResize = true;
+       for (int i = 0; i < controls.length; i++) {
+               if (!controls[i].isDisposed()) {
+                       if (overflow[0][i]) {
+                               controls[i].setBounds(rects[i]);
+                       } else {
+                               controls[i].moveAbove(null);
+                               controls[i].setBounds(rects[i].x, rects[i].y, rects[i].width, headerHeight);
+                       }
+               }
+               if (!changed && !rects[i].equals(controlRects[i])) changed = true;
+       }
+       ignoreResize = false;
+       controlRects = rects;
+       if (changed || hovering) updateBkImages();
+}
+@Override
+public boolean setFocus () {
+       checkWidget ();
+
+       /*
+       * Feature in SWT.  When a new tab item is selected
+       * and the previous tab item had focus, removing focus
+       * from the previous tab item causes fixFocus() to give
+       * focus to the first child, which is usually one of the
+       * toolbars. This is unexpected.
+       * The fix is to try to set focus on the first tab item
+       * if fixFocus() is called.
+       */
+       Control focusControl = getDisplay().getFocusControl ();
+       boolean fixFocus = isAncestor (focusControl);
+       if (fixFocus) {
+               CTabItem item = getSelection();
+               if (item != null) {
+                       if (item.setFocus ()) return true;
+               }
+       }
+       return super.setFocus ();
+}
+/* Copy of isFocusAncestor from Control. */
+boolean isAncestor (Control control) {
+       while (control != null && control != this && !(control instanceof Shell)) {
+               control = control.getParent();
+       }
+       return control == this;
+}
+@Override
+public void setFont(Font font) {
+       checkWidget();
+       if (font != null && font.equals(getFont())) return;
+       super.setFont(font);
+       oldFont = getFont();
+       updateFolder(REDRAW);
+}
+@Override
+public void setForeground (Color color) {
+       super.setForeground(color);
+       redraw();
+}
+/**
+ * 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();
+}
+/**
+ * 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 -1
+ *
+ * @param after true if the mark should be displayed after the specified item
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</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 void setInsertMark(int index, boolean after) {
+       checkWidget();
+       if (index < -1 || index >= getItemCount()) {
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+       }
+}
+boolean setItemLocation(GC gc) {
+       boolean changed = false;
+       if (items.length == 0) return false;
+       Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
+       int borderBottom = trim.height + trim.y;
+       int borderTop = -trim.y;
+       Point size = getSize();
+       int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop;
+       Point closeButtonSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, 0, gc, SWT.DEFAULT, SWT.DEFAULT);
+       int leftItemEdge = getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
+       if (single) {
+               int defaultX = getDisplay().getBounds().width + 10; // off screen
+               for (int i = 0; i < items.length; i++) {
+                       CTabItem item = items[i];
+                       if (i == selectedIndex) {
+                               firstIndex = selectedIndex;
+                               int oldX = item.x, oldY = item.y;
+                               item.x = leftItemEdge;
+                               item.y = y;
+                               item.showing = true;
+                               if (showClose || item.showClose) {
+                                       item.closeRect.x = leftItemEdge - renderer.computeTrim(i, SWT.NONE, 0, 0, 0, 0).x;
+                                       item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
+                               }
+                               if (item.x != oldX || item.y != oldY) changed = true;
+                       } else {
+                               item.x = defaultX;
+                               item.showing = false;
+                       }
+               }
+       } else {
+               int rightItemEdge = getRightItemEdge(gc);
+               int maxWidth = rightItemEdge - leftItemEdge;
+               int width = 0;
+               for (int i = 0; i < priority.length; i++) {
+                       CTabItem item = items[priority[i]];
+                       width += item.width;
+                       item.showing = i == 0 ? true : item.width > 0 && width <= maxWidth;
+               }
+               int x = getLeftItemEdge(gc, CTabFolderRenderer.PART_HEADER);
+               int defaultX = getDisplay().getBounds().width + 10; // off screen
+               firstIndex = items.length - 1;
+               for (int i = 0; i < items.length; i++) {
+                       CTabItem item = items[i];
+                       if (!item.showing) {
+                               if (item.x != defaultX) changed = true;
+                               item.x = defaultX;
+                       } else {
+                               firstIndex = Math.min(firstIndex, i);
+                               if (item.x != x || item.y != y) changed = true;
+                               item.x = x;
+                               item.y = y;
+                               int state = SWT.NONE;
+                               if (i == selectedIndex) state |= SWT.SELECTED;
+                               Rectangle edgeTrim = renderer.computeTrim(i, state, 0, 0, 0, 0);
+                               item.closeRect.x = item.x + item.width  - (edgeTrim.width + edgeTrim.x) - closeButtonSize.x;
+                               item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
+                               x = x + item.width;
+                               if (!simple && i == selectedIndex) x -= renderer.curveIndent; //TODO: fix next item position
+                       }
+               }
+       }
+       return changed;
+}
+/**
+ * Reorder the items of the receiver.
+ * @param indices an array containing the new indices for all items
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
+ *    <li>ERROR_INVALID_ARGUMENT - if the indices array is not the same length as the number of items,
+ *    if there are duplicate indices or an index is out of range.</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*/ void setItemOrder (int[] indices) {
+       checkWidget();
+       if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+       if (indices.length != items.length) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+       int newSelectedIndex = -1;
+       boolean[] seen = new boolean[items.length];
+       CTabItem[] temp = new CTabItem[items.length];
+       for (int i=0; i<indices.length; i++) {
+               int index = indices[i];
+               if (!(0 <= index && index < items.length)) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+               if (seen[index]) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+               seen[index] = true;
+               if (index == selectedIndex) newSelectedIndex = i;
+               temp[i] = items[index];
+       }
+       items = temp;
+       selectedIndex = newSelectedIndex;
+       updateFolder(REDRAW);
+}
+boolean setItemSize(GC gc) {
+       boolean changed = false;
+       if (isDisposed()) return changed;
+       Point size = getSize();
+       if (size.x <= 0 || size.y <= 0) return changed;
+       ToolBar chevron = getChevron();
+       if (chevron != null) chevron.setVisible(false);
+       showChevron = false;
+       if (single) {
+               showChevron = chevronVisible && items.length > 1;
+               if (showChevron) {
+                       chevron.setVisible(true);
+               }
+               if (selectedIndex != -1) {
+                       CTabItem tab = items[selectedIndex];
+                       int width = renderer.computeSize(selectedIndex, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT).x;
+                       width = Math.min(width, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
+                       if (tab.height != tabHeight || tab.width != width) {
+                               changed = true;
+                               tab.shortenedText = null;
+                               tab.shortenedTextWidth = 0;
+                               tab.height = tabHeight;
+                               tab.width = width;
+                               tab.closeRect.width = tab.closeRect.height = 0;
+                               if (showClose || tab.showClose) {
+                                       Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT);
+                                       tab.closeRect.width = closeSize.x;
+                                       tab.closeRect.height = closeSize.y;
+                               }
+                       }
+               }
+               return changed;
+       }
+
+       if (items.length == 0) return changed;
+       int[] widths;
+       int tabAreaWidth = Math.max(0, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
+       // First, try the minimum tab size at full compression.
+       int minWidth = 0;
+       int[] minWidths = new int[items.length];
+       for (int i = 0; i < priority.length; i++) {
+               int index = priority[i];
+               int state = CTabFolderRenderer.MINIMUM_SIZE;
+               if (index == selectedIndex) state |= SWT.SELECTED;
+               minWidths[index] = renderer.computeSize(index, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
+               minWidth += minWidths[index];
+               if (minWidth > tabAreaWidth) break;
+       }
+       if (minWidth > tabAreaWidth) {
+               // full compression required and a chevron
+               showChevron = chevronVisible && items.length > 1;
+               if (showChevron) {
+                       tabAreaWidth -= chevron.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+                       chevron.setVisible(true);
+               }
+               widths = minWidths;
+               int index = selectedIndex != -1 ? selectedIndex : 0;
+               if (tabAreaWidth < widths[index]) {
+                       widths[index] = Math.max(0, tabAreaWidth);
+               }
+       } else {
+               int maxWidth = 0;
+               int[] maxWidths = new int[items.length];
+               for (int i = 0; i < items.length; i++) {
+                       int state = 0;
+                       if (i == selectedIndex) state |= SWT.SELECTED;
+                       maxWidths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
+                       maxWidth += maxWidths[i];
+               }
+               if (maxWidth <= tabAreaWidth) {
+                       // no compression required
+                       widths = maxWidths;
+               } else {
+                       // determine compression for each item
+                       int extra = (tabAreaWidth - minWidth) / items.length;
+                       while (true) {
+                               int large = 0, totalWidth = 0;
+                               for (int i = 0 ; i < items.length; i++) {
+                                       if (maxWidths[i] > minWidths[i] + extra) {
+                                               totalWidth += minWidths[i] + extra;
+                                               large++;
+                                       } else {
+                                               totalWidth += maxWidths[i];
+                                       }
+                               }
+                               if (totalWidth >= tabAreaWidth) {
+                                       extra--;
+                                       break;
+                               }
+                               if (large == 0 || tabAreaWidth - totalWidth < large) break;
+                               extra++;
+                       }
+                       widths = new int[items.length];
+                       for (int i = 0; i < items.length; i++) {
+                               widths[i] = Math.min(maxWidths[i], minWidths[i] + extra);
+                       }
+               }
+       }
+
+       for (int i = 0; i < items.length; i++) {
+               CTabItem tab = items[i];
+               int width = widths[i];
+               if (tab.height != tabHeight || tab.width != width) {
+                       changed = true;
+                       tab.shortenedText = null;
+                       tab.shortenedTextWidth = 0;
+                       tab.height = tabHeight;
+                       tab.width = width;
+                       tab.closeRect.width = tab.closeRect.height = 0;
+                       if (showClose || tab.showClose) {
+                               if (i == selectedIndex || showUnselectedClose) {
+                                       Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT);
+                                       tab.closeRect.width = closeSize.x;
+                                       tab.closeRect.height = closeSize.y;
+                               }
+                       }
+               }
+       }
+       return changed;
+}
+/**
+ * Marks the receiver's maximize button as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ *
+ * @param visible the new visibility state
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public void setMaximizeVisible(boolean visible) {
+       checkWidget();
+       if (showMax == visible) return;
+       // display maximize button
+       showMax = visible;
+       updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
+}
+/**
+ * Sets the layout which is associated with the receiver to be
+ * the argument which may be null.
+ * <p>
+ * Note: No Layout can be set on this Control because it already
+ * manages the size and position of its children.
+ * </p>
+ *
+ * @param layout the receiver's new layout 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>
+ */
+@Override
+public void setLayout (Layout layout) {
+       checkWidget();
+       return;
+}
+/**
+ * Sets the maximized state of the receiver.
+ *
+ * @param maximize the new maximized state
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public void setMaximized(boolean maximize) {
+       checkWidget ();
+       if (this.maximized == maximize) return;
+       if (maximize && this.minimized) setMinimized(false);
+       this.maximized = maximize;
+       if (minMaxTb != null && maxItem != null) {
+               if (maxImage != null) maxImage.dispose();
+               maxImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MAX_BUTTON);
+               maxItem.setImage(maxImage);
+               maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
+       }
+}
+/**
+ * Marks the receiver's minimize button as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ *
+ * @param visible the new visibility state
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public void setMinimizeVisible(boolean visible) {
+       checkWidget();
+       if (showMin == visible) return;
+       // display minimize button
+       showMin = visible;
+       updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
+}
+/**
+ * Sets the minimized state of the receiver.
+ *
+ * @param minimize the new minimized state
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public void setMinimized(boolean minimize) {
+       checkWidget ();
+       if (this.minimized == minimize) return;
+       if (minimize && this.maximized) setMaximized(false);
+       this.minimized = minimize;
+       if (minMaxTb != null && minItem != null) {
+               if (minImage != null) minImage.dispose();
+               minImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MIN_BUTTON);
+               minItem.setImage(minImage);
+               minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
+       }
+}
+
+/**
+ * Sets the minimum number of characters that will
+ * be displayed in a fully compressed tab.
+ *
+ * @param count the minimum number of characters that will be displayed in a fully compressed 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>
+ *    <li>ERROR_INVALID_RANGE - if the count is less than zero</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setMinimumCharacters(int count) {
+       checkWidget ();
+       if (count < 0) SWT.error(SWT.ERROR_INVALID_RANGE);
+       if (minChars == count) return;
+       minChars = count;
+       updateFolder(REDRAW_TABS);
+}
+
+/**
+ * When there is not enough horizontal space to show all the tabs,
+ * by default, tabs are shown sequentially from left to right in
+ * order of their index.  When the MRU visibility is turned on,
+ * the tabs that are visible will be the tabs most recently selected.
+ * Tabs will still maintain their left to right order based on index
+ * but only the most recently selected tabs are visible.
+ * <p>
+ * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
+ * "Tab 3" and "Tab 4" (in order by index).  The user selects
+ * "Tab 1" and then "Tab 3".  If the CTabFolder is now
+ * compressed so that only two tabs are visible, by default,
+ * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
+ * selected and "Tab 2" because it is the previous item in index order).
+ * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
+ * and "Tab 3" (in that order from left to right).</p>
+ *
+ * @param show the new visibility state
+ *
+ * @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>
+ *
+ * @since 3.1
+ */
+public void setMRUVisible(boolean show) {
+       checkWidget();
+       if (mru == show) return;
+       mru = show;
+       if (!mru) {
+               if (firstIndex == -1) return;
+               int idx = firstIndex;
+               int next = 0;
+               for (int i = firstIndex; i < items.length; i++) {
+                       priority[next++] = i;
+               }
+               for (int i = 0; i < idx; i++) {
+                       priority[next++] = i;
+               }
+               updateFolder(REDRAW_TABS);
+       }
+}
+/**
+ * Sets the renderer which is associated with the receiver to be
+ * the argument which may be null. In the case of null, the default
+ * renderer is used.
+ *
+ * @param renderer a new renderer
+ *
+ * @exception SWTException <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>
+ *
+ * @see CTabFolderRenderer
+ *
+ * @since 3.6
+ */
+public void setRenderer(CTabFolderRenderer renderer) {
+       checkWidget();
+       if (this.renderer == renderer || (useDefaultRenderer && renderer == null)) return;
+       if (this.renderer != null) this.renderer.dispose();
+       useDefaultRenderer = renderer == null;
+       if (useDefaultRenderer) renderer = new CTabFolderRenderer(this);
+       this.renderer = renderer;
+       updateFolder(REDRAW);
+}
+/**
+ * Set the selection to the tab at the specified item.
+ *
+ * @param item the tab item to be selected
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * </ul>
+ *
+ * @exception SWTException <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 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.
+ *
+ * @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;
+       CTabItem selection = items[index];
+       if (selectedIndex == index) {
+               showItem(selection);
+               return;
+       }
+
+       int oldIndex = selectedIndex;
+       selectedIndex = index;
+       if (oldIndex != -1) {
+               items[oldIndex].closeImageState = SWT.BACKGROUND;
+               items[oldIndex].state &= ~SWT.SELECTED;
+       }
+       selection.closeImageState = SWT.NONE;
+       selection.showing = false;
+       selection.state |= SWT.SELECTED;
+
+       Control newControl = selection.control;
+       Control oldControl = null;
+       if (oldIndex != -1) {
+               oldControl = items[oldIndex].control;
+       }
+
+       if (newControl != oldControl) {
+               if (newControl != null && !newControl.isDisposed()) {
+                       newControl.setBounds(getClientArea());
+                       newControl.setVisible(true);
+               }
+               if (oldControl != null && !oldControl.isDisposed()) {
+                       oldControl.setVisible(false);
+               }
+       }
+       showItem(selection);
+       redraw();
+}
+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);
+       }
+}
+/**
+ * Sets the receiver's selection background color to the color specified
+ * by the argument, or to the default system color for the control
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception 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>
+ *
+ * @since 3.0
+ */
+public void setSelectionBackground (Color color) {
+       if (inDispose) return;
+       checkWidget();
+       setSelectionHighlightGradientColor(null);
+       if (selectionBackground == color) return;
+       if (color == null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND);
+       selectionBackground = color;
+       renderer.createAntialiasColors(); //TODO:  need better caching strategy
+       if (selectedIndex > -1) redraw();
+}
+/**
+ * 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 SWTException <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) {
+       setSelectionBackground(colors, percents, false);
+}
+/**
+ * Specify a gradient of colours to be draw in the background of the selected tab.
+ * For example to draw a vertical 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}, true);
+ * </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.
+ *
+ * @param vertical indicate the direction of the gradient.  True is vertical and false is horizontal.
+ *
+ * @exception SWTException <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>
+ *
+ * @since 3.0
+ */
+public void setSelectionBackground(Color[] colors, int[] percents, boolean vertical) {
+       checkWidget();
+       int colorsLength;
+       Color highlightBeginColor = null;  //null == no highlight
+
+       if (colors != null) {
+               //The colors array can optionally have an extra entry which describes the highlight top color
+               //Thus its either one or two larger than the percents array
+               if (percents == null ||
+                               ! ((percents.length == colors.length - 1) || (percents.length == colors.length - 2))){
+                       SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+               }
+               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);
+                       }
+               }
+               //If the colors is exactly two more than percents then last is highlight
+               //Keep track of *real* colorsLength (minus the highlight)
+               if(percents.length == colors.length - 2) {
+                       highlightBeginColor = colors[colors.length - 1];
+                       colorsLength = colors.length - 1;
+               } else {
+                       colorsLength = colors.length;
+               }
+               if (getDisplay().getDepth() < 15) {
+                       // Don't use gradients on low color displays
+                       colors = new Color[] {colors[colorsLength - 1]};
+                       colorsLength = colors.length;
+                       percents = new int[] {};
+               }
+       } else {
+               colorsLength = 0;
+       }
+
+       // Are these settings the same as before?
+       if (selectionBgImage == null) {
+               if ((selectionGradientColors != null) && (colors != null) &&
+                       (selectionGradientColors.length == colorsLength)) {
+                       boolean same = false;
+                       for (int i = 0; i < selectionGradientColors.length; i++) {
+                               if (selectionGradientColors[i] == null) {
+                                       same = colors[i] == null;
+                               } else {
+                                       same = selectionGradientColors[i].equals(colors[i]);
+                               }
+                               if (!same) break;
+                       }
+                       if (same) {
+                               for (int i = 0; i < selectionGradientPercents.length; i++) {
+                                       same = selectionGradientPercents[i] == percents[i];
+                                       if (!same) break;
+                               }
+                       }
+                       if (same && this.selectionGradientVertical == vertical) return;
+               }
+       } else {
+               selectionBgImage = null;
+       }
+       // Store the new settings
+       if (colors == null) {
+               selectionGradientColors = null;
+               selectionGradientPercents = null;
+               selectionGradientVertical = false;
+               setSelectionBackground((Color)null);
+               setSelectionHighlightGradientColor(null);
+       } else {
+               selectionGradientColors = new Color[colorsLength];
+               for (int i = 0; i < colorsLength; ++i) {
+                       selectionGradientColors[i] = colors[i];
+               }
+               selectionGradientPercents = new int[percents.length];
+               for (int i = 0; i < percents.length; ++i) {
+                       selectionGradientPercents[i] = percents[i];
+               }
+               selectionGradientVertical = vertical;
+               setSelectionBackground(selectionGradientColors[selectionGradientColors.length-1]);
+               setSelectionHighlightGradientColor(highlightBeginColor);
+       }
+
+       // Refresh with the new settings
+       if (selectedIndex > -1) redraw();
+}
+
+/*
+ * Set the color for the highlight start for selected tabs.
+ * Update the cache of highlight gradient colors if required.
+ */
+void setSelectionHighlightGradientColor(Color start) {
+       if (inDispose) return;
+       renderer.setSelectionHighlightGradientColor(start);  //TODO: need better caching strategy
+}
+
+/**
+ * Set the image to be drawn in the background of the selected tab.  Image
+ * is stretched or compressed to cover entire selection tab area.
+ *
+ * @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();
+       setSelectionHighlightGradientColor(null);
+       if (image == selectionBgImage) return;
+       if (image != null) {
+               selectionGradientColors = null;
+               selectionGradientPercents = null;
+               renderer.disposeSelectionHighlightGradientColors(); //TODO: need better caching strategy
+       }
+       selectionBgImage = image;
+       renderer.createAntialiasColors(); //TODO:  need better caching strategy
+       if (selectedIndex > -1) redraw();
+}
+/**
+ * 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 = getDisplay().getSystemColor(SELECTION_FOREGROUND);
+       selectionForeground = color;
+       if (selectedIndex > -1) redraw();
+}
+
+/**
+ * Sets the shape that the CTabFolder will use to render itself.
+ *
+ * @param simple <code>true</code> if the CTabFolder should render itself in a simple, traditional style
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public void setSimple(boolean simple) {
+       checkWidget();
+       if (this.simple != simple) {
+               this.simple = simple;
+               updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
+       }
+}
+/**
+ * Sets the number of tabs that the CTabFolder should display
+ *
+ * @param single <code>true</code> if only the selected tab should be displayed otherwise, multiple tabs will be shown.
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public void setSingle(boolean single) {
+       checkWidget();
+       if (this.single != single) {
+               this.single = single;
+               if (!single) {
+                       for (int i = 0; i < items.length; i++) {
+                               if (i != selectedIndex && items[i].closeImageState == SWT.NONE) {
+                                       items[i].closeImageState = SWT.BACKGROUND;
+                               }
+                       }
+               }
+               updateFolder(REDRAW);
+       }
+}
+
+int getControlY(Point size, Rectangle[] rects, int borderBottom, int borderTop, int i) {
+       int center = fixedTabHeight != SWT.DEFAULT ? 0 : (tabHeight - rects[i].height)/2;
+       return onBottom ? size.y - borderBottom - tabHeight + center : 1 + borderTop + center;
+}
+
+/**
+ * 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 -1 will revert to the default height.
+ *
+ * @param height the point value of the height or -1
+ *
+ * @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 < -1) {
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+       }
+       fixedTabHeight = height;
+       updateFolder(UPDATE_TAB_HEIGHT);
+}
+/**
+ * Specify whether the tabs should appear along the top of the folder
+ * or along the bottom of the folder.
+ *
+ * @param position <code>SWT.TOP</code> for tabs along the top or <code>SWT.BOTTOM</code> for tabs along the bottom
+ *
+ * @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>
+ *
+ * @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;
+               updateFolder(REDRAW);
+       }
+}
+/**
+ * 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.
+ *
+ * @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 disposed, or not a child of this CTabFolder</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public void setTopRight(Control control) {
+       setTopRight(control, SWT.RIGHT);
+}
+/**
+ * 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.
+ * <p>
+ * The alignment parameter sets the layout of the control in the tab area.
+ * <code>SWT.RIGHT</code> will cause the control to be positioned on the far
+ * right of the folder and it will have its default size.  <code>SWT.FILL</code>
+ * will size the control to fill all the available space to the right of the
+ * last tab.  If there is no available space, the control will not be visible.
+ * <code>SWT.RIGHT | SWT.WRAP</code> will allow the control to wrap below the
+ * tabs if there is not enough available space to the right of the last tab.
+ * </p>
+ *
+ * @param control the control to be displayed in the top right corner or null
+ * @param alignment <code>SWT.RIGHT</code> or <code>SWT.FILL</code> or <code>SWT.RIGHT | SWT.WRAP</code>
+ *
+ * @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 disposed, or not a child of this CTabFolder</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setTopRight(Control control, int alignment) {
+       checkWidget();
+       if (alignment != SWT.RIGHT && alignment != SWT.FILL && alignment != (SWT.RIGHT | SWT.WRAP)) {
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+       }
+       if (control != null && (control.isDisposed() || control.getParent() != this)) {
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+       }
+       if (topRight == control && topRightAlignment == alignment) return;
+       if (topRight != null && !topRight.isDisposed()) removeTabControl(topRight, false);
+       topRight = control;
+       topRightAlignment = alignment;
+       alignment &= ~SWT.RIGHT;
+       if (control != null) addTabControl(control, SWT.TRAIL | alignment, -1, false);
+       updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
+}
+
+
+/**
+ * Specify whether the close button appears
+ * when the user hovers over an unselected tabs.
+ *
+ * @param visible <code>true</code> makes the close button appear
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public void setUnselectedCloseVisible(boolean visible) {
+       checkWidget();
+       if (showUnselectedClose == visible) return;
+       // display close button when mouse hovers
+       showUnselectedClose = visible;
+       updateFolder(REDRAW);
+}
+/**
+ * Specify whether the image appears on unselected tabs.
+ *
+ * @param visible <code>true</code> makes the image appear
+ *
+ * @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>
+ *
+ * @since 3.0
+ */
+public void setUnselectedImageVisible(boolean visible) {
+       checkWidget();
+       if (showUnselectedImage == visible) return;
+       // display image on unselected items
+       showUnselectedImage = visible;
+       updateFolder(REDRAW);
+}
+/**
+ * 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 == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+       int idx = -1;
+       for (int i = 0; i < priority.length; i++) {
+               if (priority[i] == index) {
+                       idx = i;
+                       break;
+               }
+       }
+       if (mru) {
+               // move to front of mru order
+               int[] newPriority = new int[priority.length];
+               System.arraycopy(priority, 0, newPriority, 1, idx);
+               System.arraycopy(priority, idx+1, newPriority, idx+1, priority.length - idx - 1);
+               newPriority[0] = index;
+               priority = newPriority;
+       }
+       if (item.showing) return;
+       updateFolder(REDRAW_TABS);
+}
+void showList (Rectangle rect) {
+       if (items.length == 0 || !showChevron) return;
+       if (showMenu == null || showMenu.isDisposed()) {
+               showMenu = new Menu(getShell(), getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT));
+       } else {
+               MenuItem[] items = showMenu.getItems();
+               for (int i = 0; i < items.length; i++) {
+                       items[i].dispose();
+               }
+       }
+       final String id = "CTabFolder_showList_Index"; //$NON-NLS-1$
+       for (int i = 0; i < items.length; i++) {
+               CTabItem tab = items[i];
+               if (tab.showing) continue;
+               MenuItem item = new MenuItem(showMenu, SWT.NONE);
+               // Bug 533124 In the case where you have multi line tab text, we force the drop-down menu to have single line entries to ensure consistent behavior across platforms.
+               item.setText(tab.getText().replace("\n", " "));
+               item.setImage(tab.getImage());
+               item.setData(id, tab);
+               item.addSelectionListener(new SelectionAdapter() {
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               MenuItem menuItem = (MenuItem)e.widget;
+                               int index = indexOf((CTabItem)menuItem.getData(id));
+                               CTabFolder.this.setSelection(index, true);
+                       }
+               });
+       }
+       int x = rect.x;
+       int y = rect.y + rect.height;
+       Point location = getDisplay().map(this, null, x, y);
+       showMenu.setLocation(location.x, location.y);
+       showMenu.setVisible(true);
+}
+/**
+ * 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 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());
+       }
+}
+
+void _setToolTipText (int x, int y) {
+       String oldTip = getToolTipText();
+       String newTip = _getToolTip(x, y);
+       if (newTip == null || !newTip.equals(oldTip)) {
+               setToolTipText(newTip);
+       }
+}
+
+boolean updateItems() {
+       return updateItems(selectedIndex);
+}
+
+boolean updateItems (int showIndex) {
+       GC gc = new GC(this);
+       if (!single && !mru && showIndex != -1) {
+               // make sure selected item will be showing
+               int firstIndex = showIndex;
+               if (priority[0] < showIndex) {
+                       int maxWidth = getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
+                       int width = 0;
+                       int[] widths = new int[items.length];
+                       for (int i = priority[0]; i <= showIndex; i++) {
+                               int state = CTabFolderRenderer.MINIMUM_SIZE;
+                               if (i == selectedIndex) state |= SWT.SELECTED;
+                               widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
+                               width += widths[i];
+                               if (width > maxWidth) break;
+                       }
+                       if (width > maxWidth) {
+                               width = 0;
+                               for (int i = showIndex; i >= 0; i--) {
+                                       int state = CTabFolderRenderer.MINIMUM_SIZE;
+                                       if (i == selectedIndex) state |= SWT.SELECTED;
+                                       if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
+                                       width += widths[i];
+                                       if (width > maxWidth) break;
+                                       firstIndex = i;
+                               }
+                       } else {
+                               firstIndex = priority[0];
+                               for (int i = showIndex + 1; i < items.length; i++) {
+                                       int state = CTabFolderRenderer.MINIMUM_SIZE;
+                                       if (i == selectedIndex) state |= SWT.SELECTED;
+                                       widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
+                                       width += widths[i];
+                                       if (width >= maxWidth) break;
+                               }
+                               if (width < maxWidth) {
+                                       for (int i = priority[0] - 1; i >= 0; i--) {
+                                               int state = CTabFolderRenderer.MINIMUM_SIZE;
+                                               if (i == selectedIndex) state |= SWT.SELECTED;
+                                               if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
+                                               width += widths[i];
+                                               if (width > maxWidth) break;
+                                               firstIndex = i;
+                                       }
+                               }
+                       }
+
+               }
+               if (firstIndex != priority[0]) {
+                       int index = 0;
+                       // enumerate tabs from first visible to the last existing one (sorted ascending)
+                       for (int i = firstIndex; i < items.length; i++) {
+                               priority[index++] = i;
+                       }
+                       // enumerate hidden tabs on the left hand from first visible one
+                       // in the inverse order (sorted descending) so that the originally
+                       // first opened tab is always at the end of the list
+                       for (int i = firstIndex - 1; i >= 0; i--) {
+                               priority[index++] = i;
+                       }
+               }
+       }
+
+       boolean oldShowChevron = showChevron;
+       boolean changed = setItemSize(gc);
+       changed |= setItemLocation(gc);
+       setButtonBounds(gc);
+       changed |= showChevron != oldShowChevron;
+       if (changed && getToolTipText() != null) {
+               Point pt = getDisplay().getCursorLocation();
+               pt = toControl(pt);
+               _setToolTipText(pt.x, pt.y);
+       }
+       gc.dispose();
+       return changed;
+}
+boolean updateTabHeight(boolean force){
+       int oldHeight = tabHeight;
+       GC gc = new GC(this);
+       tabHeight = renderer.computeSize(CTabFolderRenderer.PART_HEADER, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT).y;
+       gc.dispose();
+       if (fixedTabHeight == SWT.DEFAULT && controls != null && controls.length > 0) {
+               for (int i = 0; i < controls.length; i++) {
+                       if ((controlAlignments[i] & SWT.WRAP) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
+                               int topHeight = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
+                               topHeight +=  renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0,0,0,0).height + 1;
+                               tabHeight = Math.max(topHeight, tabHeight);
+                       }
+               }
+       }
+       if (!force && tabHeight == oldHeight) return false;
+       oldSize = null;
+       return true;
+}
+
+void updateFolder (int flags) {
+       updateFlags |= flags;
+       if (updateRun != null) return;
+       updateRun = () -> {
+               updateRun = null;
+               if (isDisposed()) return;
+               runUpdate();
+       };
+       getDisplay().asyncExec(updateRun);
+}
+
+void runUpdate() {
+       if (updateFlags == 0) return;
+       int flags = updateFlags;
+       updateFlags = 0;
+       Rectangle rectBefore = getClientArea();
+       updateTabHeight(false);
+       updateItems(selectedIndex);
+       if ((flags & REDRAW) != 0) {
+               redraw();
+       } else if ((flags & REDRAW_TABS) != 0) {
+               redrawTabs();
+       }
+       Rectangle rectAfter = getClientArea();
+       if (!rectBefore.equals(rectAfter)) {
+               notifyListeners(SWT.Resize, new Event());
+               layout();
+       }
+}
+
+void updateBkImages() {
+       if (controls != null && controls.length > 0) {
+               for (int i = 0; i < controls.length; i++) {
+                       Control control = controls[i];
+                       if (!control.isDisposed()) {
+                               if (hovering) {
+                                       if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
+                                       control.setBackgroundImage(null);
+                                       control.setBackground(getBackground());
+                               } else {
+                                       if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_DEFAULT);
+                                       Rectangle bounds = control.getBounds();
+                                       int tabHeight = getTabHeight();
+                                       int height = this.getSize().y;
+                                       boolean wrapped = onBottom ? bounds.y + bounds.height < height - tabHeight : bounds.y > tabHeight;
+                                       if (wrapped || gradientColors == null) {
+                                               control.setBackgroundImage(null);
+                                               control.setBackground(getBackground());
+                                       } else {
+                                               bounds.width = 10;
+                                               if (!onBottom) {
+                                                       bounds.y = -bounds.y;
+                                                       bounds.height -= 2*bounds.y - 1;
+                                               } else {
+                                                       bounds.height += height - (bounds.y + bounds.height);
+                                                       bounds.y = -1;
+                                               }
+                                               bounds.x = 0;
+                                               if (controlBkImages[i] != null) controlBkImages[i].dispose();
+                                               controlBkImages[i] = new Image(control.getDisplay(), bounds);
+                                               GC gc = new GC(controlBkImages[i]);
+                                               renderer.draw(CTabFolderRenderer.PART_BACKGROUND, 0, bounds, gc);
+                                               gc.dispose();
+                                               control.setBackground(null);
+                                               control.setBackgroundImage(controlBkImages[i]);
+                                       }
+                               }
+                       }
+               }
+
+       }
+}
+String _getToolTip(int x, int y) {
+       CTabItem item = getItem(new Point (x, y));
+       if (item == null) return null;
+       if (!item.showing) return null;
+       if ((showClose || item.showClose) && item.closeRect.contains(x, y)) {
+               return SWT.getMessage("SWT_Close"); //$NON-NLS-1$
+       }
+       return item.getToolTipText();
+}
+/**
+* Set a control that can appear to the left or to the right of the folder tabs.
+* This method can also be used instead of #setTopRight(Control). To remove a tab
+* control, see#removeTabControl(Control);
+* <p>
+* The flags parameter sets the layout of the control in the tab area.
+* <code>SWT.LEAD</code> will cause the control to be positioned on the left
+* of the tabs. <code>SWT.TRAIL</code> will cause the control to be positioned on
+* the far right of the folder and it will have its default size. <code>SWT.TRAIL</code>
+* can be combined with <code>SWT.FILL</code>to fill all the available space to the
+* right of the last tab. <code>SWT.WRAP</code> can also be added to <code>SWT.TRAIL</code>
+* only to cause a control to wrap if there is not enough space to display it in its
+* entirety.
+* </p>
+* @param control the control to be displayed in the top right corner or null
+*
+* @param flags valid combinations are:
+* <ul><li>SWT.LEAD
+* <li> SWT.TRAIL (| SWT.FILL | SWT.WRAP)
+* </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>
+*    <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
+* </ul>
+*/
+/*public*/ void addTabControl(Control control, int flags) {
+       checkWidget();
+       addTabControl(control, flags, -1, true);
+}
+
+void addTabControl(Control control, int flags, int index, boolean update) {
+       switch (flags) {
+               case SWT.TRAIL:
+               case SWT.TRAIL | SWT.WRAP:
+               case SWT.TRAIL | SWT.FILL:
+               case SWT.TRAIL | SWT.FILL | SWT.WRAP:
+               case SWT.LEAD:
+                       break;
+               default:
+                       SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+                       break;
+       }
+       if (control != null && control.getParent() != this) {
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+       }
+       //check for duplicates
+       for (int i = 0; i < controls.length; i++) {
+               if (controls[i] == control) {
+                       SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+               }
+       }
+       int length = controls.length;
+
+       control.addListener(SWT.Resize, listener);
+
+       //Grow all 4 arrays
+       Control[] newControls = new Control [length + 1];
+       System.arraycopy(controls, 0, newControls, 0, length);
+       controls = newControls;
+       int[] newAlignment = new int [length + 1];
+       System.arraycopy(controlAlignments, 0, newAlignment, 0, length);
+       controlAlignments = newAlignment;
+       Rectangle[] newRect = new Rectangle [length + 1];
+       System.arraycopy(controlRects, 0, newRect, 0, length);
+       controlRects = newRect;
+       Image[] newImage = new Image [length + 1];
+       System.arraycopy(controlBkImages, 0, newImage, 0, length);
+       controlBkImages = newImage;
+       if (index == -1) {
+               index = length;
+               if (chevronTb != null && control != chevronTb) index--;
+       }
+       System.arraycopy (controls, index, controls, index + 1, length - index);
+       System.arraycopy (controlAlignments, index, controlAlignments, index + 1, length - index);
+       System.arraycopy (controlRects, index, controlRects, index + 1, length - index);
+       System.arraycopy (controlBkImages, index, controlBkImages, index + 1, length - index);
+       controls[index] = control;
+       controlAlignments[index] = flags;
+       controlRects[index] = new Rectangle(0, 0, 0, 0);
+       if (update) {
+               updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
+       }
+}
+
+/**
+* Removes the control from the list of tab controls.
+*
+* @param control the control to be removed
+*
+* @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 removeTabControl (Control control) {
+       checkWidget();
+       removeTabControl (control, true);
+}
+
+void removeTabControl (Control control, boolean update) {
+       if (control != null && control.getParent() != this) {
+               SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+       }
+       int index = -1;
+       for (int i = 0; i < controls.length; i++) {
+               if (controls[i] == control){
+                       index = i;
+                       break;
+               }
+       }
+       if (index == -1) return;
+
+       if (!control.isDisposed()) {
+               control.removeListener(SWT.Resize, listener);
+               control.setBackground (null);
+               control.setBackgroundImage (null);
+               if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
+       }
+
+       if (controlBkImages[index] != null && !controlBkImages[index].isDisposed()) controlBkImages[index].dispose();
+       if (controls.length == 1) {
+               controls = new Control[0];
+               controlAlignments = new int[0];
+               controlRects = new Rectangle[0];
+               controlBkImages = new Image[0];
+       } else {
+               Control[] newControls = new Control [controls.length - 1];
+               System.arraycopy(controls, 0, newControls, 0, index);
+               System.arraycopy(controls, index + 1, newControls, index, controls.length - index - 1);
+               controls = newControls;
+
+               int[] newAlignments = new int [controls.length];
+               System.arraycopy(controlAlignments, 0, newAlignments, 0, index);
+               System.arraycopy(controlAlignments, index + 1, newAlignments, index, controls.length - index);
+               controlAlignments = newAlignments;
+
+               Rectangle[] newRects = new Rectangle [controls.length];
+               System.arraycopy(controlRects, 0, newRects, 0, index);
+               System.arraycopy(controlRects, index + 1, newRects, index, controls.length - index);
+               controlRects = newRects;
+
+               Image[] newBkImages = new Image [controls.length];
+               System.arraycopy(controlBkImages, 0, newBkImages, 0, index);
+               System.arraycopy(controlBkImages, index + 1, newBkImages, index, controls.length - index);
+               controlBkImages = newBkImages;
+       }
+       if (update) {
+               updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
+       }
+}
+
+int getWrappedHeight (Point size) {
+       boolean[][] positions = new boolean[1][];
+       Rectangle[] rects = computeControlBounds(size, positions);
+       int minY = Integer.MAX_VALUE, maxY = 0, wrapHeight = 0;
+       for (int i = 0; i < rects.length; i++) {
+               if (positions[0][i]) {
+                       minY = Math.min(minY, rects[i].y);
+                       maxY = Math.max(maxY, rects[i].y + rects[i].height);
+                       wrapHeight = maxY - minY;
+               }
+       }
+       return wrapHeight;
+}
+
+/**
+ * Sets whether a chevron is shown when there are more items to be displayed.
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_INVALID_RANGE - if the index is out of range</li>
+ * </ul>
+ * @exception SWTException <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 setChevronVisible(boolean visible) {
+       checkWidget();
+       if (chevronVisible == visible) return;
+       chevronVisible = visible;
+       updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
+}
+
+       boolean shouldHighlight() {
+               return this.highlight && highlightEnabled;
+       }
+
+/**
+ * Sets whether the selected tab is rendered as highlighted.
+ *
+ * @param enabled
+ *            {@code true} if the selected tab should be highlighted,
+ *            {@code 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>
+ * @since 3.106
+ */
+public void setHighlightEnabled(boolean enabled) {
+       checkWidget();
+       if (highlightEnabled == enabled) {
+               return;
+       }
+       highlightEnabled = enabled;
+       updateFolder(REDRAW);
+}
+
+/**
+ * Returns <code>true</code> if the selected tab is rendered as
+ * highlighted.
+ *
+ * @return <code>true</code> if the selected tab is rendered as
+ *         highlighted
+ *
+ * @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>
+ * @since 3.106
+ */
+public boolean getHighlightEnabled() {
+       checkWidget();
+       return highlightEnabled;
+}
+}