]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/custom/CTabFolder.java
63d9676f57b6d57fed470366204b11e9675e43a4
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / custom / CTabFolder.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.custom;
15
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.accessibility.*;
18 import org.eclipse.swt.events.*;
19 import org.eclipse.swt.graphics.*;
20 import org.eclipse.swt.internal.*;
21 import org.eclipse.swt.internal.DPIUtil.*;
22 import org.eclipse.swt.widgets.*;
23
24 /**
25  *
26  * Instances of this class implement the notebook user interface
27  * metaphor.  It allows the user to select a notebook page from
28  * set of pages.
29  * <p>
30  * The item children that may be added to instances of this class
31  * must be of type <code>CTabItem</code>.
32  * <code>Control</code> children are created and then set into a
33  * tab item using <code>CTabItem#setControl</code>.
34  * </p><p>
35  * Note that although this class is a subclass of <code>Composite</code>,
36  * it does not make sense to set a layout on it.
37  * </p>
38  * <dl>
39  * <dt><b>Styles:</b></dt>
40  * <dd>CLOSE, TOP, BOTTOM, FLAT, BORDER, SINGLE, MULTI</dd>
41  * <dt><b>Events:</b></dt>
42  * <dd>Selection</dd>
43  * <dd>"CTabFolder2"</dd>
44  * </dl>
45  * Note: Only one of the styles TOP and BOTTOM
46  * may be specified.
47  * <p>
48  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
49  * </p>
50  *
51  * @see <a href="http://www.eclipse.org/swt/snippets/#ctabfolder">CTabFolder, CTabItem snippets</a>
52  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: CustomControlExample</a>
53  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
54  * @noextend This class is not intended to be subclassed by clients.
55  */
56
57 public class CTabFolder extends Composite {
58
59         /**
60          * marginWidth specifies the number of points of horizontal margin
61          * that will be placed along the left and right edges of the form.
62          *
63          * The default value is 0.
64          */
65         public int marginWidth = 0;
66         /**
67          * marginHeight specifies the number of points of vertical margin
68          * that will be placed along the top and bottom edges of the form.
69          *
70          * The default value is 0.
71          */
72         public int marginHeight = 0;
73
74         /**
75          * A multiple of the tab height that specifies the minimum width to which a tab
76          * will be compressed before scrolling arrows are used to navigate the tabs.
77          *
78          * NOTE This field is badly named and can not be fixed for backwards compatibility.
79          * It should not be capitalized.
80          *
81          * @deprecated This field is no longer used.  See setMinimumCharacters(int)
82          */
83         @Deprecated
84         public int MIN_TAB_WIDTH = 4;
85
86         /**
87          * Color of innermost line of drop shadow border.
88          *
89          * NOTE This field is badly named and can not be fixed for backwards compatibility.
90          * It should be capitalized.
91          *
92          * @deprecated drop shadow border is no longer drawn in 3.0
93          */
94         @Deprecated
95         public static RGB borderInsideRGB  = new RGB (132, 130, 132);
96         /**
97          * Color of middle line of drop shadow border.
98          *
99          * NOTE This field is badly named and can not be fixed for backwards compatibility.
100          * It should be capitalized.
101          *
102          * @deprecated drop shadow border is no longer drawn in 3.0
103          */
104         @Deprecated
105         public static RGB borderMiddleRGB  = new RGB (143, 141, 138);
106         /**
107          * Color of outermost line of drop shadow border.
108          *
109          * NOTE This field is badly named and can not be fixed for backwards compatibility.
110          * It should be capitalized.
111          *
112          * @deprecated drop shadow border is no longer drawn in 3.0
113          */
114         @Deprecated
115         public static RGB borderOutsideRGB = new RGB (171, 168, 165);
116
117         /* sizing, positioning */
118         boolean onBottom = false;
119         boolean single = false;
120         boolean simple = true;
121         int fixedTabHeight = SWT.DEFAULT;
122         int tabHeight;
123         int minChars = 20;
124         boolean borderVisible = false;
125
126         /* item management */
127         CTabFolderRenderer renderer;
128         CTabItem items[] = new CTabItem[0];
129         /** index of the left most visible tab. */
130         int firstIndex = -1;
131         int selectedIndex = -1;
132
133         /**
134          * Indices of the elements in the {@link #items} array, used to manage tab
135          * visibility and candidates to be hidden/shown next.
136          * <p>
137          * If there is not enough place for all tabs, tabs starting from the end of
138          * the {@link #priority} array will be hidden first (independently from the
139          * {@link #mru} flag!) =&gt; the right most elements have the highest priority
140          * to be hidden.
141          * <p>
142          * If there is more place to show previously hidden tabs, tabs starting from
143          * the beginning of the {@link #priority} array will be made visible first
144          * (independently from the {@link #mru} flag!) =&gt; the left most elements
145          * have the highest priority to be shown.
146          * <p>
147          * The update strategy of the {@link #priority} array however depends on the
148          * {@link #mru} flag.
149          * <p>
150          * If {@link #mru} flag is set, the first index is always the index of the
151          * currently selected tab, next one is the tab selected before current
152          * etc...
153          * <p>
154          * Example: [4,2,5,1,3,0], just representing the last selection order.
155          * <p>
156          * If {@link #mru} flag is not set, the first index is always the index of
157          * the left most visible tab ({@link #firstIndex} field), next indices are
158          * incremented by one up to <code>priority.length-1</code>, and the rest
159          * filled with indices starting with <code>firstIndex-1</code> and
160          * decremented by one until 0 index is reached.
161          * <p>
162          * The tabs between first index and the index of the currently selected tab
163          * are always visible.
164          * <p>
165          * Example: 6 tabs, 2 and 3 are indices of currently shown tabs:
166          * [2,3,4,5,1,0]. The array consists of two blocks: sorted ascending from
167          * first visible (2) to last available (5), and the rest sorted descending
168          * (1,0). 4 and 5 are the hidden tabs on the right side, 0 and 1 are the
169          * hidden tabs on the left side from the visible tabs 2 and 3.
170          *
171          * @see #updateItems(int)
172          * @see #setItemLocation(GC)
173          */
174         int[] priority = new int[0];
175         boolean mru = false;
176         Listener listener;
177         boolean ignoreTraverse;
178         boolean useDefaultRenderer;
179
180         /* External Listener management */
181         CTabFolder2Listener[] folderListeners = new CTabFolder2Listener[0];
182         // support for deprecated listener mechanism
183         CTabFolderListener[] tabListeners = new CTabFolderListener[0];
184
185         /* Selected item appearance */
186         Image selectionBgImage;
187         Color[] selectionGradientColors;
188         int[] selectionGradientPercents;
189         boolean selectionGradientVertical;
190         Color selectionForeground;
191         Color selectionBackground;
192
193         /* Unselected item appearance */
194         Color[] gradientColors;
195         int[] gradientPercents;
196         boolean gradientVertical;
197         boolean showUnselectedImage = true;
198
199         // close, min/max and chevron buttons
200         boolean showClose = false;
201         boolean showUnselectedClose = true;
202
203         boolean showMin = false;
204         boolean minimized = false;
205         boolean showMax = false;
206         boolean maximized = false;
207         ToolBar minMaxTb;
208         ToolItem maxItem;
209         ToolItem minItem;
210         Image maxImage;
211         Image minImage;
212         boolean hoverTb;
213         Rectangle hoverRect = new Rectangle(0,0,0,0);
214         boolean hovering;
215         boolean hoverTimerRunning;
216         boolean highlight;
217         boolean highlightEnabled = true;
218
219         boolean showChevron = false;
220         Menu showMenu;
221         ToolBar chevronTb;
222         ToolItem chevronItem;
223         int chevronCount;
224         boolean chevronVisible = true;
225
226         Image chevronImage;
227         Control topRight;
228         int topRightAlignment = SWT.RIGHT;
229         boolean ignoreResize;
230         Control[] controls;
231         int[] controlAlignments;
232         Rectangle[] controlRects;
233         Image[] controlBkImages;
234
235         int updateFlags;
236         final static int REDRAW = 1 << 1;
237         final static int REDRAW_TABS = 1 << 2;
238         final static int UPDATE_TAB_HEIGHT = 1 << 3;
239         Runnable updateRun;
240
241         // when disposing CTabFolder, don't try to layout the items or
242         // change the selection as each child is destroyed.
243         boolean inDispose = false;
244
245         // keep track of size changes in order to redraw only affected area
246         // on Resize
247         Point oldSize;
248         Font oldFont;
249
250         // internal constants
251         static final int DEFAULT_WIDTH = 64;
252         static final int DEFAULT_HEIGHT = 64;
253
254         static final int SELECTION_FOREGROUND = SWT.COLOR_LIST_FOREGROUND;
255         static final int SELECTION_BACKGROUND = SWT.COLOR_LIST_BACKGROUND;
256
257         static final int FOREGROUND = SWT.COLOR_WIDGET_FOREGROUND;
258         static final int BACKGROUND = SWT.COLOR_WIDGET_BACKGROUND;
259
260         //TODO: add setter for spacing?
261         static final int SPACING = 3;
262 /**
263  * Constructs a new instance of this class given its parent
264  * and a style value describing its behavior and appearance.
265  * <p>
266  * The style value is either one of the style constants defined in
267  * class <code>SWT</code> which is applicable to instances of this
268  * class, or must be built by <em>bitwise OR</em>'ing together
269  * (that is, using the <code>int</code> "|" operator) two or more
270  * of those <code>SWT</code> style constants. The class description
271  * lists the style constants that are applicable to the class.
272  * Style bits are also inherited from superclasses.
273  * </p>
274  *
275  * @param parent a widget which will be the parent of the new instance (cannot be null)
276  * @param style the style of widget to construct
277  *
278  * @exception IllegalArgumentException <ul>
279  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
280  * </ul>
281  * @exception SWTException <ul>
282  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
283  * </ul>
284  *
285  * @see SWT#TOP
286  * @see SWT#BOTTOM
287  * @see SWT#FLAT
288  * @see SWT#BORDER
289  * @see SWT#SINGLE
290  * @see SWT#MULTI
291  * @see #getStyle()
292  */
293 public CTabFolder(Composite parent, int style) {
294         super(parent, checkStyle (parent, style));
295         init(style);
296 }
297
298 void init(int style) {
299         super.setLayout(new CTabFolderLayout());
300         int style2 = super.getStyle();
301         oldFont = getFont();
302         onBottom = (style2 & SWT.BOTTOM) != 0;
303         showClose = (style2 & SWT.CLOSE) != 0;
304 //      showMin = (style2 & SWT.MIN) != 0; - conflicts with SWT.TOP
305 //      showMax = (style2 & SWT.MAX) != 0; - conflicts with SWT.BOTTOM
306         single = (style2 & SWT.SINGLE) != 0;
307         borderVisible = (style & SWT.BORDER) != 0;
308         //set up default colors
309         Display display = getDisplay();
310         selectionForeground = display.getSystemColor(SELECTION_FOREGROUND);
311         selectionBackground = display.getSystemColor(SELECTION_BACKGROUND);
312         renderer = new CTabFolderRenderer(this);
313         useDefaultRenderer = true;
314         controls = new Control[0];
315         controlAlignments = new int[0];
316         controlRects = new Rectangle[0];
317         controlBkImages = new Image[0];
318         updateTabHeight(false);
319
320         // Add all listeners
321         listener = event -> {
322                 switch (event.type) {
323                         case SWT.Dispose:          onDispose(event); break;
324                         case SWT.DragDetect:       onDragDetect(event); break;
325                         case SWT.FocusIn:          onFocus(event);      break;
326                         case SWT.FocusOut:         onFocus(event);      break;
327                         case SWT.KeyDown:          onKeyDown(event); break;
328                         case SWT.MenuDetect:       onMenuDetect(event); break;
329                         case SWT.MouseDoubleClick: onMouseDoubleClick(event); break;
330                         case SWT.MouseDown:        onMouse(event);      break;
331                         case SWT.MouseEnter:       onMouse(event);      break;
332                         case SWT.MouseExit:        onMouse(event);      break;
333                         case SWT.MouseHover:       onMouse(event); break;
334                         case SWT.MouseMove:        onMouse(event); break;
335                         case SWT.MouseUp:          onMouse(event); break;
336                         case SWT.Paint:            onPaint(event);      break;
337                         case SWT.Resize:           onResize(event);     break;
338                         case SWT.Traverse:         onTraverse(event); break;
339                         case SWT.Selection:        onSelection(event); break;
340                         case SWT.Activate:         onActivate(event); break;
341                         case SWT.Deactivate:       onDeactivate(event); break;
342                 }
343         };
344
345         int[] folderEvents = new int[]{
346                 SWT.Dispose,
347                 SWT.DragDetect,
348                 SWT.FocusIn,
349                 SWT.FocusOut,
350                 SWT.KeyDown,
351                 SWT.MenuDetect,
352                 SWT.MouseDoubleClick,
353                 SWT.MouseDown,
354                 SWT.MouseEnter,
355                 SWT.MouseExit,
356                 SWT.MouseHover,
357                 SWT.MouseMove,
358                 SWT.MouseUp,
359                 SWT.Paint,
360                 SWT.Resize,
361                 SWT.Traverse,
362                 SWT.Activate,
363                 SWT.Deactivate
364         };
365         for (int i = 0; i < folderEvents.length; i++) {
366                 addListener(folderEvents[i], listener);
367         }
368
369         initAccessible();
370 }
371 void onDeactivate(Event event) {
372         if (!highlightEnabled) {
373                 return;
374         }
375         this.highlight = false;
376         redraw();
377 }
378
379 void onActivate(Event event) {
380         if (!highlightEnabled) {
381                 return;
382         }
383         this.highlight = true;
384         redraw();
385 }
386
387 static int checkStyle (Composite parent, int style) {
388         int mask = SWT.CLOSE | SWT.TOP | SWT.BOTTOM | SWT.FLAT | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.SINGLE | SWT.MULTI;
389         style = style & mask;
390         // TOP and BOTTOM are mutually exclusive.
391         // TOP is the default
392         if ((style & SWT.TOP) != 0) style = style & ~SWT.BOTTOM;
393         // SINGLE and MULTI are mutually exclusive.
394         // MULTI is the default
395         if ((style & SWT.MULTI) != 0) style = style & ~SWT.SINGLE;
396         // reduce the flash by not redrawing the entire area on a Resize event
397         style |= SWT.NO_REDRAW_RESIZE;
398
399         //TEMPORARY CODE
400         /*
401          * In Right To Left orientation on Windows, all GC calls that use a brush are drawing
402          * offset by one pixel.  This results in some parts of the CTabFolder not drawing correctly.
403          * To alleviate some of the appearance problems, allow the OS to draw the background.
404          * This does not draw correctly but the result is less obviously wrong.
405          */
406         if ((style & SWT.RIGHT_TO_LEFT) != 0) return style;
407         if ((parent.getStyle() & SWT.MIRRORED) != 0 && (style & SWT.LEFT_TO_RIGHT) == 0) return style;
408
409         return style | SWT.DOUBLE_BUFFERED;
410 }
411
412 /**
413  *
414  * Adds the listener to the collection of listeners who will
415  * be notified when a tab item is closed, minimized, maximized,
416  * restored, or to show the list of items that are not
417  * currently visible.
418  *
419  * @param listener the listener which should be notified
420  *
421  * @exception IllegalArgumentException <ul>
422  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
423  * </ul>
424  *
425  * @exception SWTException <ul>
426  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
427  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
428  * </ul>
429  *
430  * @see CTabFolder2Listener
431  * @see #removeCTabFolder2Listener(CTabFolder2Listener)
432  *
433  * @since 3.0
434  */
435 public void addCTabFolder2Listener(CTabFolder2Listener listener) {
436         checkWidget();
437         if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
438         // add to array
439         CTabFolder2Listener[] newListeners = new CTabFolder2Listener[folderListeners.length + 1];
440         System.arraycopy(folderListeners, 0, newListeners, 0, folderListeners.length);
441         folderListeners = newListeners;
442         folderListeners[folderListeners.length - 1] = listener;
443 }
444 /**
445  * Adds the listener to the collection of listeners who will
446  * be notified when a tab item is closed.
447  *
448  * @param listener the listener which should be notified
449  *
450  * @exception IllegalArgumentException <ul>
451  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
452  * </ul>
453  * @exception SWTException <ul>
454  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
455  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
456  * </ul>
457  *
458  * @see CTabFolderListener
459  * @see #removeCTabFolderListener(CTabFolderListener)
460  *
461  * @deprecated use addCTabFolder2Listener(CTabFolder2Listener)
462  */
463 @Deprecated
464 public void addCTabFolderListener(CTabFolderListener listener) {
465         checkWidget();
466         if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
467         // add to array
468         CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1];
469         System.arraycopy(tabListeners, 0, newTabListeners, 0, tabListeners.length);
470         tabListeners = newTabListeners;
471         tabListeners[tabListeners.length - 1] = listener;
472         // display close button to be backwards compatible
473         if (!showClose) {
474                 showClose = true;
475                 updateFolder(REDRAW);
476         }
477 }
478 /**
479  * Adds the listener to the collection of listeners who will
480  * be notified when the user changes the receiver's selection, by sending
481  * it one of the messages defined in the <code>SelectionListener</code>
482  * interface.
483  * <p>
484  * <code>widgetSelected</code> is called when the user changes the selected tab.
485  * <code>widgetDefaultSelected</code> is not called.
486  * </p>
487  *
488  * @param listener the listener which should be notified when the user changes the receiver's selection
489  *
490  * @exception IllegalArgumentException <ul>
491  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
492  * </ul>
493  * @exception SWTException <ul>
494  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
495  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
496  * </ul>
497  *
498  * @see SelectionListener
499  * @see #removeSelectionListener
500  * @see SelectionEvent
501  */
502 public void addSelectionListener(SelectionListener listener) {
503         checkWidget();
504         if (listener == null) {
505                 SWT.error(SWT.ERROR_NULL_ARGUMENT);
506         }
507         TypedListener typedListener = new TypedListener(listener);
508         addListener(SWT.Selection, typedListener);
509         addListener(SWT.DefaultSelection, typedListener);
510 }
511
512 Rectangle[] computeControlBounds (Point size, boolean[][] position) {
513         if (controls == null || controls.length == 0) return new Rectangle[0];
514         Rectangle[] rects = new Rectangle[controls.length];
515         for (int i = 0; i < rects.length; i++) {
516                 rects[i] = new Rectangle(0, 0, 0, 0);
517         }
518         Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
519         int borderRight = trim.width + trim.x;
520         int borderLeft = -trim.x;
521         int borderBottom = trim.height + trim.y;
522         int borderTop = -trim.y;
523
524         Point[] tabControlSize = new Point[controls.length];
525         boolean[] overflow = new boolean [controls.length];
526         //Left Control
527         int leftWidth = 0;
528         int x = borderLeft + SPACING;
529         int rightWidth = 0;
530         int allWidth = 0;
531         for (int i = 0; i < controls.length; i++) {
532                 Point ctrlSize = tabControlSize[i] = !controls[i].isDisposed() && controls[i].getVisible() ? controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT) : new Point(0,0);
533                 int alignment = controlAlignments[i];
534                 if ((alignment & SWT.LEAD) != 0) {
535                         rects[i].width = ctrlSize.x;
536                         rects[i].height = getControlHeight(ctrlSize);
537                         rects[i].x = x;
538                         rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
539                         x += ctrlSize.x;
540                         leftWidth += ctrlSize.x;
541                 } else {
542                         if ((alignment & (SWT.FILL | SWT.WRAP)) == 0) {
543                                 rightWidth += ctrlSize.x;
544                         }
545                         allWidth += ctrlSize.x;
546                 }
547         }
548         if (leftWidth > 0) leftWidth += SPACING * 2;
549
550         int itemWidth = 0;
551         for (int i = 0; i < items.length; i++) {
552                 if (items[i].showing) itemWidth += items[i].width;
553         }
554
555         int maxWidth = size.x - borderLeft - leftWidth - borderRight;
556         int availableWidth = Math.max(0, maxWidth - itemWidth - rightWidth);
557         if (rightWidth > 0) availableWidth -= SPACING * 2;
558         x =  size.x  - borderRight - SPACING;
559         if (itemWidth + allWidth <= maxWidth) {
560                 //Everything fits
561                 for (int i = 0; i < controls.length; i++) {
562                         int alignment = controlAlignments[i];
563                         if ((alignment & SWT.TRAIL) != 0) {
564                                 Point ctrlSize = tabControlSize[i];
565                                 x -= ctrlSize.x;
566                                 rects[i].width = ctrlSize.x;
567                                 rects[i].height = getControlHeight(ctrlSize);
568                                 rects[i].x = x;
569                                 rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
570                                 if ((alignment & (SWT.FILL | SWT.WRAP)) != 0) availableWidth -= ctrlSize.x;
571                         }
572                         if (tabControlSize[i].y >= tabHeight && fixedTabHeight == SWT.DEFAULT) {
573                                 overflow[i] = true;
574                         }
575                 }
576         } else {
577                 for (int i = 0; i < controls.length; i++) {
578                         int alignment = controlAlignments[i];
579                         Point ctrlSize = tabControlSize[i];
580                         if ((alignment & SWT.TRAIL) != 0) {
581                                 if ((alignment & (SWT.FILL | SWT.WRAP)) == 0) {
582                                         x -= ctrlSize.x;
583                                         rects[i].width = ctrlSize.x;
584                                         rects[i].height = getControlHeight(ctrlSize);
585                                         rects[i].x = x;
586                                         rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
587                                 } else if (((alignment & (SWT.WRAP)) != 0 && ctrlSize.x < availableWidth)) {
588                                         x -= ctrlSize.x;
589                                         rects[i].width = ctrlSize.x;
590                                         rects[i].height = getControlHeight(ctrlSize);
591                                         rects[i].x = x;
592                                         rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
593                                         availableWidth -= ctrlSize.x;
594                                 } else if ((alignment & (SWT.FILL)) != 0 && (alignment & (SWT.WRAP)) == 0) {
595                                         rects[i].width = 0;
596                                         rects[i].height = getControlHeight(ctrlSize);
597                                         rects[i].x = x;
598                                         rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
599                                 } else {
600                                         if ((alignment & (SWT.WRAP)) != 0) {
601                                                 overflow[i] = true;
602                                         }
603                                 }
604                         }
605                 }
606         }
607
608         //Any space, distribute amongst FILL
609         if (availableWidth > 0) {
610                 int fillCount = 0;
611                 for (int i = 0; i < controls.length; i++) {
612                         int alignment = controlAlignments[i];
613                         if ((alignment & SWT.TRAIL) != 0 && (alignment & SWT.FILL) != 0 && !overflow[i]) {
614                                 fillCount++;
615                         }
616                 }
617                 if (fillCount != 0) {
618                         int extraSpace = availableWidth/fillCount;
619                         int addedSpace = 0;
620                         for (int i = 0; i < controls.length; i++) {
621                                 int alignment = controlAlignments[i];
622                                 if ((alignment & SWT.TRAIL) != 0) {
623                                         if ((alignment & SWT.FILL) != 0 && !overflow[i]) {
624                                                 rects[i].width += extraSpace;
625                                                 addedSpace += extraSpace;
626                                         }
627                                         if (!overflow[i]) {
628                                                 rects[i].x -= addedSpace;
629                                         }
630                                 }
631                         }
632                 }
633         }
634
635         //Go through overflow laying out all wrapped controls
636         Rectangle bodyTrim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
637         int bodyRight = bodyTrim.width + bodyTrim.x;
638         int bodyLeft = -bodyTrim.x;
639         int bodyWidth = size.x - bodyLeft - bodyRight;
640         x = size.x - bodyRight;
641         int y = onBottom ? this.getSize().y - getTabHeight() + 2*bodyTrim.y : -bodyTrim.y;
642         availableWidth = bodyWidth;
643         int maxHeight = 0;
644         for (int i = 0; i < controls.length; i++) {
645                 Point ctrlSize = tabControlSize[i];
646                 if (overflow[i]) {
647                         if (availableWidth > ctrlSize.x) {
648                                 x -= ctrlSize.x;
649                                 rects[i].width = ctrlSize.x;
650                                 rects[i].y = onBottom ? y - ctrlSize.y : y;
651                                 rects[i].height = ctrlSize.y;
652                                 rects[i].x = x;
653                                 availableWidth -= ctrlSize.x;
654                                 maxHeight = Math.max(maxHeight, ctrlSize.y);
655                         } else {
656                                 x = size.x - bodyRight;
657                                 y += maxHeight;
658                                 maxHeight = 0;
659                                 availableWidth = bodyWidth;
660                                 if (availableWidth > ctrlSize.x) {
661                                         //Relayout this control in the next line
662                                         i--;
663                                 } else {
664                                         ctrlSize = controls[i].isDisposed() ? new Point(0,0) : controls[i].computeSize(bodyWidth, SWT.DEFAULT);
665                                         rects[i].width = bodyWidth;
666                                         rects[i].y = onBottom ? y - ctrlSize.y : y;
667                                         rects[i].height = ctrlSize.y;
668                                         rects[i].x = size.x - ctrlSize.x - bodyRight;
669                                         y += ctrlSize.y;
670                                 }
671                         }
672                 }
673         }
674
675         if (showChevron) {
676                 int i = 0, lastIndex = -1;
677                 while (i < priority.length && items[priority[i]].showing) {
678                         lastIndex = Math.max(lastIndex, priority[i++]);
679                 }
680                 if (lastIndex == -1) lastIndex = selectedIndex;
681                 if (lastIndex != -1) {
682                         CTabItem lastItem = items[lastIndex];
683                         int w = lastItem.x + lastItem.width + SPACING;
684                         if (!simple && lastIndex == selectedIndex) w -= (renderer.curveIndent - 7);
685                         rects[controls.length - 1].x = w;
686                 }
687         }
688
689         if (position != null) position[0] = overflow;
690         return rects;
691 }
692
693 int getControlHeight(Point ctrlSize) {
694         return fixedTabHeight == SWT.DEFAULT ?  Math.max(tabHeight - 1, ctrlSize.y) : ctrlSize.y;
695 }
696 /*
697 * This class was not intended to be subclassed but this restriction
698 * cannot be enforced without breaking backward compatibility.
699 */
700 //protected void checkSubclass () {
701 //      String name = getClass ().getName ();
702 //      int index = name.lastIndexOf ('.');
703 //      if (!name.substring (0, index + 1).equals ("org.eclipse.swt.custom.")) {
704 //              SWT.error (SWT.ERROR_INVALID_SUBCLASS);
705 //      }
706 //}
707 @Override
708 public Rectangle computeTrim (int x, int y, int width, int height) {
709         checkWidget();
710         Rectangle trim =  renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, x, y, width, height);
711         Point size = new Point(width, height);
712         int wrapHeight = getWrappedHeight(size);
713         if (onBottom) {
714                 trim.height += wrapHeight;
715         } else {
716                 trim.y -= wrapHeight;
717                 trim.height += wrapHeight;
718         }
719         return trim;
720 }
721 Image createButtonImage(Display display, int button) {
722         GC tempGC = new GC (this);
723         Point size = renderer.computeSize(button, SWT.NONE, tempGC, SWT.DEFAULT, SWT.DEFAULT);
724         tempGC.dispose();
725
726         Rectangle trim = renderer.computeTrim(button, SWT.NONE, 0, 0, 0, 0);
727         Image image = new Image (display, size.x - trim.width, size.y - trim.height);
728         GC gc = new GC (image);
729         Color transColor = renderer.parent.getBackground();
730         gc.setBackground(transColor);
731         gc.fillRectangle(image.getBounds());
732         renderer.draw(button, SWT.NONE, new Rectangle(trim.x, trim.y, size.x, size.y), gc);
733         gc.dispose ();
734
735         final ImageData imageData = image.getImageData (DPIUtil.getDeviceZoom ());
736         imageData.transparentPixel = imageData.palette.getPixel(transColor.getRGB());
737         image.dispose();
738         image = new Image(display, new AutoScaleImageDataProvider(display, imageData, DPIUtil.getDeviceZoom()));
739         return image;
740 }
741 void createItem (CTabItem item, int index) {
742         if (0 > index || index > getItemCount ())SWT.error (SWT.ERROR_INVALID_RANGE);
743         item.parent = this;
744         CTabItem[] newItems = new CTabItem [items.length + 1];
745         System.arraycopy(items, 0, newItems, 0, index);
746         newItems[index] = item;
747         System.arraycopy(items, index, newItems, index + 1, items.length - index);
748         items = newItems;
749         if (selectedIndex >= index) selectedIndex ++;
750         int[] newPriority = new int[priority.length + 1];
751         int next = 0,  priorityIndex = priority.length;
752         for (int i = 0; i < priority.length; i++) {
753                 if (!mru && priority[i] == index) {
754                         priorityIndex = next++;
755                 }
756                 newPriority[next++] = priority[i] >= index ? priority[i] + 1 : priority[i];
757         }
758         newPriority[priorityIndex] = index;
759         priority = newPriority;
760
761         if (items.length == 1) {
762                 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
763         } else {
764                 updateFolder(REDRAW_TABS);
765         }
766 }
767 void destroyItem (CTabItem item) {
768         if (inDispose) return;
769         int index = indexOf(item);
770         if (index == -1) return;
771
772         if (items.length == 1) {
773                 items = new CTabItem[0];
774                 priority = new int[0];
775                 firstIndex = -1;
776                 selectedIndex = -1;
777
778                 Control control = item.control;
779                 if (control != null && !control.isDisposed()) {
780                         control.setVisible(false);
781                 }
782                 setToolTipText(null);
783                 GC gc = new GC(this);
784                 setButtonBounds(gc);
785                 gc.dispose();
786                 redraw();
787                 return;
788         }
789
790         CTabItem[] newItems = new CTabItem [items.length - 1];
791         System.arraycopy(items, 0, newItems, 0, index);
792         System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
793         items = newItems;
794
795         int[] newPriority = new int[priority.length - 1];
796         int next = 0;
797         for (int i = 0; i < priority.length; i++) {
798                 if (priority [i] == index) continue;
799                 newPriority[next++] = priority[i] > index ? priority[i] - 1 : priority [i];
800         }
801         priority = newPriority;
802
803         // move the selection if this item is selected
804         if (selectedIndex == index) {
805                 Control control = item.getControl();
806                 selectedIndex = -1;
807                 int nextSelection = mru ? priority[0] : Math.max(0, index - 1);
808                 setSelection(nextSelection, true);
809                 if (control != null && !control.isDisposed()) {
810                         control.setVisible(false);
811                 }
812         } else if (selectedIndex > index) {
813                 selectedIndex --;
814         }
815
816         requestLayout();
817         updateFolder(UPDATE_TAB_HEIGHT | REDRAW_TABS);
818 }
819
820 /**
821  * Returns <code>true</code> if the receiver's border is visible.
822  *
823  * @return the receiver's border visibility state
824  *
825  * @exception SWTException <ul>
826  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
827  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
828  * </ul>
829  *
830  * @since 3.0
831  */
832 public boolean getBorderVisible() {
833         checkWidget();
834         return borderVisible;
835 }
836 ToolBar getChevron() {
837         if (chevronTb == null) {
838                 chevronTb = new ToolBar(this, SWT.FLAT);
839                 initAccessibleChevronTb();
840                 addTabControl(chevronTb, SWT.TRAIL, -1, false);
841         }
842         if (chevronItem == null) {
843                 chevronItem = new ToolItem(chevronTb, SWT.PUSH);
844                 chevronItem.setToolTipText(SWT.getMessage("SWT_ShowList"));
845                 chevronItem.addListener(SWT.Selection, listener);
846         }
847         return chevronTb;
848 }
849 /**
850  * Returns <code>true</code> if the chevron button
851  * is visible when necessary.
852  *
853  * @return the visibility of the chevron button
854  *
855  * @exception SWTException <ul>
856  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
857  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
858  * </ul>
859  *
860  */
861 /*public*/ boolean getChevronVisible() {
862         checkWidget();
863         return chevronVisible;
864 }
865 @Override
866 public Rectangle getClientArea() {
867         checkWidget();
868         //TODO: HACK - find a better way to get padding
869         Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.FILL, 0, 0, 0, 0);
870         Point size = getSize();
871         int wrapHeight = getWrappedHeight(size);
872         if (onBottom) {
873                 trim.height += wrapHeight;
874         } else {
875                 trim.y -= wrapHeight;
876                 trim.height += wrapHeight;
877         }
878         if (minimized) return new Rectangle(-trim.x, -trim.y, 0, 0);
879         int width = size.x - trim.width;
880         int height = size.y - trim.height;
881         return new Rectangle(-trim.x, -trim.y, width, height);
882 }
883
884 /**
885  * Return the tab that is located at the specified index.
886  *
887  * @param index the index of the tab item
888  * @return the item at the specified index
889  *
890  * @exception IllegalArgumentException <ul>
891  *    <li>ERROR_INVALID_RANGE - if the index is out of range</li>
892  * </ul>
893  * @exception SWTException <ul>
894  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
895  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
896  * </ul>
897  */
898 public CTabItem getItem (int index) {
899         /*
900          * This call is intentionally commented out, to allow this getter method to be
901          * called from a thread which is different from one that created the widget.
902          */
903         //checkWidget();
904         if (index  < 0 || index >= items.length)
905                 SWT.error(SWT.ERROR_INVALID_RANGE);
906         return items [index];
907 }
908 /**
909  * Gets the item at a point in the widget.
910  *
911  * @param pt the point in coordinates relative to the CTabFolder
912  * @return the item at a point or null
913  *
914  * @exception SWTException <ul>
915  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
916  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
917  *      </ul>
918  */
919 public CTabItem getItem (Point pt) {
920         /*
921          * This call is intentionally commented out, to allow this getter method to be
922          * called from a thread which is different from one that created the widget.
923          */
924         //checkWidget();
925         if (items.length == 0) return null;
926         runUpdate();
927         Point size = getSize();
928         Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
929         if (size.x <= trim.width) return null;
930         for (int i = 0; i < priority.length; i++) {
931                 CTabItem item = items[priority[i]];
932                 Rectangle rect = item.getBounds();
933                 if (rect.contains(pt)) return item;
934         }
935         return null;
936 }
937 /**
938  * Return the number of tabs in the folder.
939  *
940  * @return the number of tabs in the folder
941  *
942  * @exception SWTException <ul>
943  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
944  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
945  *      </ul>
946  */
947 public int getItemCount(){
948         //checkWidget();
949         return items.length;
950 }
951 /**
952  * Return the tab items.
953  *
954  * @return the tab items
955  *
956  * @exception SWTException <ul>
957  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
958  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
959  *      </ul>
960  */
961 public CTabItem [] getItems() {
962         /*
963          * This call is intentionally commented out, to allow this getter method to be
964          * called from a thread which is different from one that created the widget.
965          */
966         //checkWidget();
967         CTabItem[] tabItems = new CTabItem [items.length];
968         System.arraycopy(items, 0, tabItems, 0, items.length);
969         return tabItems;
970 }
971 int getLeftItemEdge (GC gc, int part){
972         Rectangle trim = renderer.computeTrim(part, SWT.NONE, 0, 0, 0, 0);
973         int x = -trim.x;
974         int width = 0;
975         for (int i = 0; i < controls.length; i++) {
976                 if ((controlAlignments[i] & SWT.LEAD) != 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
977                         width += controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
978                 }
979         }
980         if (width != 0) width += SPACING * 2;
981         x += width;
982         return Math.max(0, x);
983 }
984 /*
985  * Return the lowercase of the first non-'&' character following
986  * an '&' character in the given string. If there are no '&'
987  * characters in the given string, return '\0'.
988  */
989 char _findMnemonic (String string) {
990         if (string == null) return '\0';
991         int index = 0;
992         int length = string.length ();
993         do {
994                 while (index < length && string.charAt (index) != '&') index++;
995                 if (++index >= length) return '\0';
996                 if (string.charAt (index) != '&') return Character.toLowerCase (string.charAt (index));
997                 index++;
998         } while (index < length);
999         return '\0';
1000 }
1001 String stripMnemonic (String string) {
1002         int index = 0;
1003         int length = string.length ();
1004         do {
1005                 while ((index < length) && (string.charAt (index) != '&')) index++;
1006                 if (++index >= length) return string;
1007                 if (string.charAt (index) != '&') {
1008                         return string.substring(0, index-1) + string.substring(index, length);
1009                 }
1010                 index++;
1011         } while (index < length);
1012         return string;
1013 }
1014 /**
1015  * Returns <code>true</code> if the receiver is minimized.
1016  *
1017  * @return the receiver's minimized state
1018  *
1019  * @exception SWTException <ul>
1020  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1021  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1022  * </ul>
1023  *
1024  * @since 3.0
1025  */
1026 public boolean getMinimized() {
1027         checkWidget();
1028         return minimized;
1029 }
1030 /**
1031  * Returns <code>true</code> if the minimize button
1032  * is visible.
1033  *
1034  * @return the visibility of the minimized button
1035  *
1036  * @exception SWTException <ul>
1037  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1038  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1039  * </ul>
1040  *
1041  * @since 3.0
1042  */
1043 public boolean getMinimizeVisible() {
1044         checkWidget();
1045         return showMin;
1046 }
1047 /**
1048  * Returns the number of characters that will
1049  * appear in a fully compressed tab.
1050  *
1051  * @return number of characters that will appear in a fully compressed tab
1052  *
1053  * @since 3.0
1054  */
1055 public int getMinimumCharacters() {
1056         checkWidget();
1057         return minChars;
1058 }
1059
1060 /**
1061  * Returns <code>true</code> if the receiver is maximized.
1062  * <p>
1063  *
1064  * @return the receiver's maximized state
1065  *
1066  * @exception SWTException <ul>
1067  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1068  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1069  * </ul>
1070  *
1071  * @since 3.0
1072  */
1073 public boolean getMaximized() {
1074         checkWidget();
1075         return maximized;
1076 }
1077 /**
1078  * Returns <code>true</code> if the maximize button
1079  * is visible.
1080  *
1081  * @return the visibility of the maximized button
1082  *
1083  * @exception SWTException <ul>
1084  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1085  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1086  * </ul>
1087  *
1088  * @since 3.0
1089  */
1090 public boolean getMaximizeVisible() {
1091         checkWidget();
1092         return showMax;
1093 }
1094 /**
1095  * Returns <code>true</code> if the receiver displays most
1096  * recently used tabs and <code>false</code> otherwise.
1097  * <p>
1098  * When there is not enough horizontal space to show all the tabs,
1099  * by default, tabs are shown sequentially from left to right in
1100  * order of their index.  When the MRU visibility is turned on,
1101  * the tabs that are visible will be the tabs most recently selected.
1102  * Tabs will still maintain their left to right order based on index
1103  * but only the most recently selected tabs are visible.
1104  * <p>
1105  * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
1106  * "Tab 3" and "Tab 4" (in order by index).  The user selects
1107  * "Tab 1" and then "Tab 3".  If the CTabFolder is now
1108  * compressed so that only two tabs are visible, by default,
1109  * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
1110  * selected and "Tab 2" because it is the previous item in index order).
1111  * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
1112  * and "Tab 3" (in that order from left to right).</p>
1113  *
1114  * @return the receiver's header's visibility state
1115  *
1116  * @exception SWTException <ul>
1117  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1118  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1119  * </ul>
1120  *
1121  * @since 3.1
1122  */
1123 public boolean getMRUVisible() {
1124         checkWidget();
1125         return mru;
1126 }
1127 /**
1128  * Returns the receiver's renderer.
1129  *
1130  * @return the receiver's renderer
1131  *
1132  * @exception SWTException <ul>
1133  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1134  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1135  *      </ul>
1136  *
1137  * @see #setRenderer(CTabFolderRenderer)
1138  * @see CTabFolderRenderer
1139  *
1140  * @since 3.6
1141  */
1142 public CTabFolderRenderer getRenderer() {
1143         checkWidget();
1144         return renderer;
1145 }
1146 int getRightItemEdge (GC gc){
1147         Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
1148         int x = getSize().x - (trim.width + trim.x);
1149         int width = 0;
1150         for (int i = 0; i < controls.length; i++) {
1151                 int align = controlAlignments[i];
1152                 if ((align & SWT.WRAP) == 0 && (align & SWT.LEAD) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
1153                         Point rightSize = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT);
1154                         width += rightSize.x;
1155                 }
1156         }
1157         if (width != 0) width += SPACING * 2;
1158         x -= width;
1159         return Math.max(0, x);
1160 }
1161 /**
1162  * Return the selected tab item, or null if there is no selection.
1163  *
1164  * @return the selected tab item, or null if none has been selected
1165  *
1166  * @exception SWTException <ul>
1167  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1168  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1169  *      </ul>
1170  */
1171 public CTabItem getSelection() {
1172         /*
1173          * This call is intentionally commented out, to allow this getter method to be
1174          * called from a thread which is different from one that created the widget.
1175          */
1176         //checkWidget();
1177         if (selectedIndex == -1) return null;
1178         return items[selectedIndex];
1179 }
1180 /**
1181  * Returns the receiver's selection background color.
1182  *
1183  * @return the selection background color of the receiver
1184  *
1185  * @exception SWTException <ul>
1186  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1187  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1188  * </ul>
1189  *
1190  * @since 3.0
1191  */
1192 public Color getSelectionBackground() {
1193         checkWidget();
1194         return selectionBackground;
1195 }
1196 /**
1197  * Returns the receiver's selection foreground color.
1198  *
1199  * @return the selection foreground color of the receiver
1200  *
1201  * @exception SWTException <ul>
1202  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1203  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1204  * </ul>
1205  *
1206  * @since 3.0
1207  */
1208 public Color getSelectionForeground() {
1209         checkWidget();
1210         return selectionForeground;
1211 }
1212 /**
1213  * Return the index of the selected tab item, or -1 if there
1214  * is no selection.
1215  *
1216  * @return the index of the selected tab item or -1
1217  *
1218  * @exception SWTException <ul>
1219  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1220  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1221  *      </ul>
1222  */
1223 public int getSelectionIndex() {
1224         /*
1225          * This call is intentionally commented out, to allow this getter method to be
1226          * called from a thread which is different from one that created the widget.
1227          */
1228         //checkWidget();
1229         return selectedIndex;
1230 }
1231 /**
1232  * Returns <code>true</code> if the CTabFolder is rendered
1233  * with a simple, traditional shape.
1234  *
1235  * @return <code>true</code> if the CTabFolder is rendered with a simple shape
1236  *
1237  * @since 3.0
1238  */
1239 public boolean getSimple() {
1240         checkWidget();
1241         return simple;
1242 }
1243 /**
1244  * Returns <code>true</code> if the CTabFolder only displays the selected tab
1245  * and <code>false</code> if the CTabFolder displays multiple tabs.
1246  *
1247  * @return <code>true</code> if the CTabFolder only displays the selected tab and <code>false</code> if the CTabFolder displays multiple tabs
1248  *
1249  * @since 3.0
1250  */
1251 public boolean getSingle() {
1252         checkWidget();
1253         return single;
1254 }
1255
1256 @Override
1257 public int getStyle() {
1258         int style = super.getStyle();
1259         style &= ~(SWT.TOP | SWT.BOTTOM);
1260         style |= onBottom ? SWT.BOTTOM : SWT.TOP;
1261         style &= ~(SWT.SINGLE | SWT.MULTI);
1262         style |= single ? SWT.SINGLE : SWT.MULTI;
1263         if (borderVisible) style |= SWT.BORDER;
1264         style &= ~SWT.CLOSE;
1265         if (showClose) style |= SWT.CLOSE;
1266         return style;
1267 }
1268 /**
1269  * Returns the height of the tab
1270  *
1271  * @return the height of the tab
1272  *
1273  * @exception SWTException <ul>
1274  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1275  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1276  *      </ul>
1277  */
1278 public int getTabHeight(){
1279         checkWidget();
1280         if (fixedTabHeight != SWT.DEFAULT) return fixedTabHeight;
1281         return tabHeight - 1; // -1 for line drawn across top of tab //TODO: replace w/ computeTrim of tab area?
1282 }
1283 /**
1284  * Returns the position of the tab.  Possible values are SWT.TOP or SWT.BOTTOM.
1285  *
1286  * @return the position of the tab
1287  *
1288  * @exception SWTException <ul>
1289  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1290  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1291  *      </ul>
1292  */
1293 public int getTabPosition(){
1294         checkWidget();
1295         return onBottom ? SWT.BOTTOM : SWT.TOP;
1296 }
1297 /**
1298  * Returns the control in the top right corner of the tab folder.
1299  * Typically this is a close button or a composite with a menu and close button.
1300  *
1301  * @return the control in the top right corner of the tab folder or null
1302  *
1303  * @exception  SWTException <ul>
1304  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1305  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1306  *      </ul>
1307  *
1308  * @since 2.1
1309  */
1310 public Control getTopRight() {
1311         checkWidget();
1312         return topRight;
1313 }
1314 /**
1315  * Returns the alignment of the top right control.
1316  *
1317  * @return the alignment of the top right control which is either
1318  * <code>SWT.RIGHT</code> or <code>SWT.FILL</code>
1319  *
1320  * @exception  SWTException <ul>
1321  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1322  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1323  *      </ul>
1324  *
1325  * @since 3.6
1326  */
1327 public int getTopRightAlignment() {
1328         checkWidget();
1329         return topRightAlignment;
1330 }
1331 /**
1332  * Returns <code>true</code> if the close button appears
1333  * when the user hovers over an unselected tabs.
1334  *
1335  * @return <code>true</code> if the close button appears on unselected tabs
1336  *
1337  * @since 3.0
1338  */
1339 public boolean getUnselectedCloseVisible() {
1340         checkWidget();
1341         return showUnselectedClose;
1342 }
1343 /**
1344  * Returns <code>true</code> if an image appears
1345  * in unselected tabs.
1346  *
1347  * @return <code>true</code> if an image appears in unselected tabs
1348  *
1349  * @since 3.0
1350  */
1351 public boolean getUnselectedImageVisible() {
1352         checkWidget();
1353         return showUnselectedImage;
1354 }
1355 /**
1356  * Return the index of the specified tab or -1 if the tab is not
1357  * in the receiver.
1358  *
1359  * @param item the tab item for which the index is required
1360  *
1361  * @return the index of the specified tab item or -1
1362  *
1363  * @exception IllegalArgumentException <ul>
1364  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1365  * </ul>
1366  *
1367  * @exception SWTException <ul>
1368  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1369  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1370  * </ul>
1371  */
1372 public int indexOf(CTabItem item) {
1373         checkWidget();
1374         if (item == null) {
1375                 SWT.error(SWT.ERROR_NULL_ARGUMENT);
1376         }
1377         for (int i = 0; i < items.length; i++) {
1378                 if (items[i] == item) return i;
1379         }
1380         return -1;
1381 }
1382 void initAccessible() {
1383         final Accessible accessible = getAccessible();
1384         accessible.addAccessibleListener(new AccessibleAdapter() {
1385                 @Override
1386                 public void getName(AccessibleEvent e) {
1387                         CTabItem item = null;
1388                         int childID = e.childID;
1389                         if (childID == ACC.CHILDID_SELF) {
1390                                 if (selectedIndex != -1) {
1391                                         item = items[selectedIndex];
1392                                 }
1393                         } else if (childID >= 0 && childID < items.length) {
1394                                 item = items[childID];
1395                         }
1396                         e.result = item == null ? null : stripMnemonic(item.getText());
1397                 }
1398
1399                 @Override
1400                 public void getHelp(AccessibleEvent e) {
1401                         String help = null;
1402                         int childID = e.childID;
1403                         if (childID == ACC.CHILDID_SELF) {
1404                                 help = getToolTipText();
1405                         } else if (childID >= 0 && childID < items.length) {
1406                                 help = items[childID].getToolTipText();
1407                         }
1408                         e.result = help;
1409                 }
1410
1411                 @Override
1412                 public void getKeyboardShortcut(AccessibleEvent e) {
1413                         String shortcut = null;
1414                         int childID = e.childID;
1415                         if (childID >= 0 && childID < items.length) {
1416                                 String text = items[childID].getText();
1417                                 if (text != null) {
1418                                         char mnemonic = _findMnemonic(text);
1419                                         if (mnemonic != '\0') {
1420                                                 shortcut = SWT.getMessage ("SWT_Page_Mnemonic", new Object[] {Character.valueOf(mnemonic)}); //$NON-NLS-1$
1421                                         }
1422                                 }
1423                         }
1424                         if (childID == ACC.CHILDID_SELF) {
1425                                 shortcut = SWT.getMessage ("SWT_SwitchPage_Shortcut"); //$NON-NLS-1$
1426                         }
1427                         e.result = shortcut;
1428                 }
1429         });
1430
1431         accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
1432                 @Override
1433                 public void getChildAtPoint(AccessibleControlEvent e) {
1434                         Point testPoint = toControl(e.x, e.y);
1435                         int childID = ACC.CHILDID_NONE;
1436                         for (int i = 0; i < items.length; i++) {
1437                                 if (items[i].getBounds().contains(testPoint)) {
1438                                         childID = i;
1439                                         break;
1440                                 }
1441                         }
1442                         if (childID == ACC.CHILDID_NONE) {
1443                                 Rectangle location = getBounds();
1444                                 location.x = location.y = 0;
1445                                 location.height = location.height - getClientArea().height;
1446                                 if (location.contains(testPoint)) {
1447                                         childID = ACC.CHILDID_SELF;
1448                                 }
1449                         }
1450                         e.childID = childID;
1451                 }
1452
1453                 @Override
1454                 public void getLocation(AccessibleControlEvent e) {
1455                         Rectangle location = null;
1456                         Point pt = null;
1457                         int childID = e.childID;
1458                         if (childID == ACC.CHILDID_SELF) {
1459                                 location = getBounds();
1460                                 pt = getParent().toDisplay(location.x, location.y);
1461                         } else {
1462                                 if (childID >= 0 && childID < items.length && items[childID].showing) {
1463                                         location = items[childID].getBounds();
1464                                 }
1465                                 if (location != null) {
1466                                         pt = toDisplay(location.x, location.y);
1467                                 }
1468                         }
1469                         if (location != null && pt != null) {
1470                                 e.x = pt.x;
1471                                 e.y = pt.y;
1472                                 e.width = location.width;
1473                                 e.height = location.height;
1474                         }
1475                 }
1476
1477                 @Override
1478                 public void getChildCount(AccessibleControlEvent e) {
1479                         e.detail = items.length;
1480                 }
1481
1482                 @Override
1483                 public void getDefaultAction(AccessibleControlEvent e) {
1484                         String action = null;
1485                         int childID = e.childID;
1486                         if (childID >= 0 && childID < items.length) {
1487                                 action = SWT.getMessage ("SWT_Switch"); //$NON-NLS-1$
1488                         }
1489                         e.result = action;
1490                 }
1491
1492                 @Override
1493                 public void getFocus(AccessibleControlEvent e) {
1494                         int childID = ACC.CHILDID_NONE;
1495                         if (isFocusControl()) {
1496                                 if (selectedIndex == -1) {
1497                                         childID = ACC.CHILDID_SELF;
1498                                 } else {
1499                                         childID = selectedIndex;
1500                                 }
1501                         }
1502                         e.childID = childID;
1503                 }
1504
1505                 @Override
1506                 public void getRole(AccessibleControlEvent e) {
1507                         int role = 0;
1508                         int childID = e.childID;
1509                         if (childID == ACC.CHILDID_SELF) {
1510                                 role = ACC.ROLE_TABFOLDER;
1511                         } else if (childID >= 0 && childID < items.length) {
1512                                 role = ACC.ROLE_TABITEM;
1513                         }
1514                         e.detail = role;
1515                 }
1516
1517                 @Override
1518                 public void getSelection(AccessibleControlEvent e) {
1519                         e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex;
1520                 }
1521
1522                 @Override
1523                 public void getState(AccessibleControlEvent e) {
1524                         int state = 0;
1525                         int childID = e.childID;
1526                         if (childID == ACC.CHILDID_SELF) {
1527                                 state = ACC.STATE_NORMAL;
1528                         } else if (childID >= 0 && childID < items.length) {
1529                                 state = ACC.STATE_SELECTABLE;
1530                                 if (isFocusControl()) {
1531                                         state |= ACC.STATE_FOCUSABLE;
1532                                 }
1533                                 if (selectedIndex == childID) {
1534                                         state |= ACC.STATE_SELECTED;
1535                                         if (isFocusControl()) {
1536                                                 state |= ACC.STATE_FOCUSED;
1537                                         }
1538                                 }
1539                         }
1540                         e.detail = state;
1541                 }
1542
1543                 @Override
1544                 public void getChildren(AccessibleControlEvent e) {
1545                         int childIdCount = items.length;
1546                         Object[] children = new Object[childIdCount];
1547                         for (int i = 0; i < childIdCount; i++) {
1548                                 children[i] = Integer.valueOf(i);
1549                         }
1550                         e.children = children;
1551                 }
1552         });
1553
1554         addListener(SWT.Selection, event -> {
1555                 if (isFocusControl()) {
1556                         if (selectedIndex == -1) {
1557                                 accessible.setFocus(ACC.CHILDID_SELF);
1558                         } else {
1559                                 accessible.setFocus(selectedIndex);
1560                         }
1561                 }
1562         });
1563
1564         addListener(SWT.FocusIn, event -> {
1565                 if (selectedIndex == -1) {
1566                         accessible.setFocus(ACC.CHILDID_SELF);
1567                 } else {
1568                         accessible.setFocus(selectedIndex);
1569                 }
1570         });
1571 }
1572 void initAccessibleMinMaxTb() {
1573         minMaxTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
1574                 @Override
1575                 public void getName(AccessibleEvent e) {
1576                         if (e.childID != ACC.CHILDID_SELF) {
1577                                 if (minItem != null && e.childID == minMaxTb.indexOf(minItem)) {
1578                                         e.result = minItem.getToolTipText();
1579                                 } else if (maxItem != null && e.childID == minMaxTb.indexOf(maxItem)) {
1580                                         e.result = maxItem.getToolTipText();
1581                                 }
1582                         }
1583                 }
1584         });
1585 }
1586 void initAccessibleChevronTb() {
1587         chevronTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
1588                 @Override
1589                 public void getName(AccessibleEvent e) {
1590                         if (e.childID != ACC.CHILDID_SELF) {
1591                                 if (chevronItem != null && e.childID == chevronTb.indexOf(chevronItem)) {
1592                                         e.result = chevronItem.getToolTipText();
1593                                 }
1594                         }
1595                 }
1596         });
1597 }
1598 void onKeyDown (Event event) {
1599         runUpdate();
1600         switch (event.keyCode) {
1601                 case SWT.ARROW_LEFT:
1602                 case SWT.ARROW_RIGHT:
1603                         int count = items.length;
1604                         if (count == 0) return;
1605                         if (selectedIndex  == -1) return;
1606                         int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
1607                         int offset =  event.keyCode == leadKey ? -1 : 1;
1608                         int index;
1609                         if (!mru) {
1610                                 index = selectedIndex + offset;
1611                         } else {
1612                                 int[] visible = new int[items.length];
1613                                 int idx = 0;
1614                                 int current = -1;
1615                                 for (int i = 0; i < items.length; i++) {
1616                                         if (items[i].showing) {
1617                                                 if (i == selectedIndex) current = idx;
1618                                                 visible [idx++] = i;
1619                                         }
1620                                 }
1621                                 if (current + offset >= 0 && current + offset < idx){
1622                                         index = visible [current + offset];
1623                                 } else {
1624                                         if (showChevron) {
1625                                                 Rectangle chevronRect = chevronItem.getBounds();
1626                                                 chevronRect = event.display.map(chevronTb, this, chevronRect);
1627                                                 CTabFolderEvent e = new CTabFolderEvent(this);
1628                                                 e.widget = this;
1629                                                 e.time = event.time;
1630                                                 e.x = chevronRect.x;
1631                                                 e.y = chevronRect.y;
1632                                                 e.width = chevronRect.width;
1633                                                 e.height = chevronRect.height;
1634                                                 e.doit = true;
1635                                                 for (int i = 0; i < folderListeners.length; i++) {
1636                                                         folderListeners[i].showList(e);
1637                                                 }
1638                                                 if (e.doit && !isDisposed()) {
1639                                                         showList(chevronRect);
1640                                                 }
1641                                         }
1642                                         return;
1643                                 }
1644                         }
1645                         if (index < 0 || index >= count) return;
1646                         setSelection (index, true);
1647                         forceFocus();
1648         }
1649 }
1650 void onDispose(Event event) {
1651         removeListener(SWT.Dispose, listener);
1652         notifyListeners(SWT.Dispose, event);
1653         event.type = SWT.None;
1654         /*
1655          * Usually when an item is disposed, destroyItem will change the size of the items array,
1656          * reset the bounds of all the tabs and manage the widget associated with the tab.
1657          * Since the whole folder is being disposed, this is not necessary.  For speed
1658          * the inDispose flag is used to skip over this part of the item dispose.
1659          */
1660         inDispose = true;
1661
1662         if (showMenu != null && !showMenu.isDisposed()) {
1663                 showMenu.dispose();
1664                 showMenu = null;
1665         }
1666         int length = items.length;
1667         for (int i = 0; i < length; i++) {
1668                 if (items[i] != null) {
1669                         items[i].dispose();
1670                 }
1671         }
1672
1673         gradientColors = null;
1674
1675         selectionGradientColors = null;
1676         selectionGradientPercents = null;
1677         selectionBgImage = null;
1678
1679         selectionBackground = null;
1680         selectionForeground = null;
1681
1682         if (controlBkImages != null) {
1683                 for (int i = 0; i < controlBkImages.length; i++) {
1684                         if (controlBkImages[i] != null) {
1685                                 controlBkImages[i].dispose();
1686                                 controlBkImages[i] = null;
1687                         }
1688                 }
1689                 controlBkImages = null;
1690         }
1691         controls = null;
1692         controlAlignments = null;
1693         controlRects = null;
1694
1695         if (maxImage != null) maxImage.dispose();
1696         maxImage = null;
1697
1698         if (minImage != null) minImage.dispose();
1699         minImage = null;
1700
1701         if (chevronImage != null) chevronImage.dispose();
1702         chevronImage = null;
1703
1704         if (renderer != null) renderer.dispose();
1705         renderer = null;
1706
1707         minItem = null;
1708         maxItem = null;
1709         minMaxTb = null;
1710
1711         chevronItem = null;
1712         chevronTb = null;
1713
1714         if (folderListeners.length != 0) folderListeners = new CTabFolder2Listener[0];
1715         if (tabListeners.length != 0) tabListeners = new CTabFolderListener[0];
1716 }
1717 void onDragDetect(Event event) {
1718         boolean consume = false;
1719         for (int i = 0; i < items.length; i++) {
1720                 if (items[i].closeRect.contains(event.x, event.y)) {
1721                                 consume = true;
1722                                 break;
1723                 }
1724         }
1725         if (consume) {
1726                 event.type = SWT.None;
1727         }
1728 }
1729 void onFocus(Event event) {
1730         checkWidget();
1731         if (selectedIndex >= 0) {
1732                 redraw();
1733         } else {
1734                 setSelection(0, true);
1735         }
1736 }
1737 boolean onMnemonic (Event event, boolean doit) {
1738         char key = event.character;
1739         for (int i = 0; i < items.length; i++) {
1740                 if (items[i] != null) {
1741                         char mnemonic = _findMnemonic (items[i].getText ());
1742                         if (mnemonic != '\0') {
1743                                 if (Character.toLowerCase (key) == mnemonic) {
1744                                         if (doit) {
1745                                                 setSelection(i, true);
1746                                                 forceFocus();
1747                                         }
1748                                         return true;
1749                                 }
1750                         }
1751                 }
1752         }
1753         return false;
1754 }
1755 void onMenuDetect(Event event) {
1756         if (event.detail == SWT.MENU_KEYBOARD) {
1757                 if (selectedIndex != -1) {
1758                         CTabItem item = items[selectedIndex];
1759                         Rectangle rect = getDisplay().map(this, null, item.getBounds());
1760                         if (!rect.contains(event.x, event.y)) {
1761                                 /* If the mouse is not in the currently-selected tab,
1762                                  * then pop up the menu near the top-right corner of the current tab.
1763                                  */
1764                                 Rectangle itemTrim = renderer.computeTrim(selectedIndex, SWT.NONE, 0, 0, 0, 0);
1765                                 Rectangle closeTrim = renderer.computeTrim(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, 0, 0, 0, 0);
1766                                 event.x = rect.x + rect.width - item.closeRect.width + itemTrim.x - closeTrim.width;
1767                                 event.y = rect.y - itemTrim.y - closeTrim.y;
1768                         }
1769                 }
1770         }
1771 }
1772 void onMouseDoubleClick(Event event) {
1773         if (event.button != 1 ||
1774                 (event.stateMask & SWT.BUTTON2) != 0 ||
1775                 (event.stateMask & SWT.BUTTON3) != 0) return;
1776         Event e = new Event();
1777         e.item = getItem(new Point(event.x, event.y));
1778         if (e.item != null) {
1779                 notifyListeners(SWT.DefaultSelection, e);
1780         }
1781 }
1782 void onMouse(Event event) {
1783         if( isDisposed() ) {
1784                 return;
1785         }
1786         int x = event.x, y = event.y;
1787         switch (event.type) {
1788                 case SWT.MouseEnter: {
1789                         setToolTipText(null);
1790                         break;
1791                 }
1792                 case SWT.MouseExit: {
1793                         for (int i=0; i<items.length; i++) {
1794                                 CTabItem item = items[i];
1795                                 if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND) {
1796                                         item.closeImageState = SWT.BACKGROUND;
1797                                         redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1798                                 }
1799                                 if ((item.state & SWT.HOT) != 0) {
1800                                         item.state &= ~SWT.HOT;
1801                                         redraw(item.x, item.y, item.width, item.height, false);
1802                                 }
1803                                 if (i == selectedIndex && item.closeImageState != SWT.NONE) {
1804                                         item.closeImageState = SWT.NONE;
1805                                         redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1806                                 }
1807                         }
1808                         break;
1809                 }
1810                 case SWT.MouseHover:
1811                 case SWT.MouseDown: {
1812                         if (hoverTb && hoverRect.contains(x, y) && !hovering) {
1813                                 hovering = true;
1814                                 updateItems();
1815                                 hoverTimerRunning = true;
1816                                 event.display.timerExec(2000, new Runnable() {
1817                                         @Override
1818                                         public void run() {
1819                                                 if (isDisposed()) return;
1820                                                 if (hovering) {
1821                                                         Display display = getDisplay();
1822                                                         Control c = display.getCursorControl();
1823                                                         boolean reschedule = false;
1824                                                         if (c != null) {
1825                                                                 for (int i = 0; i < controls.length; i++) {
1826                                                                         Control temp = c;
1827                                                                         do {
1828                                                                                 if (temp.equals(controls[i])) {
1829                                                                                         reschedule = true;
1830                                                                                 } else {
1831                                                                                         temp = temp.getParent();
1832                                                                                         if (temp == null || temp.equals(CTabFolder.this)) break;
1833                                                                                 }
1834                                                                         } while (!reschedule);
1835                                                                         if (reschedule) break;
1836                                                                 }
1837                                                         }
1838                                                         if (reschedule && hoverTimerRunning) {
1839                                                                 display.timerExec(2000, this);
1840                                                         } else {
1841                                                                 hovering = false;
1842                                                                 updateItems();
1843                                                         }
1844                                                 }
1845                                         }
1846                                 });
1847                                 return;
1848                         }
1849                         if (event.button != 1) return;
1850                         CTabItem item = null;
1851                         if (single) {
1852                                 if (selectedIndex != -1) {
1853                                         Rectangle bounds = items[selectedIndex].getBounds();
1854                                         if (bounds.contains(x, y)){
1855                                                 item = items[selectedIndex];
1856                                         }
1857                                 }
1858                         } else {
1859                                 for (int i=0; i<items.length; i++) {
1860                                         Rectangle bounds = items[i].getBounds();
1861                                         if (bounds.contains(x, y)){
1862                                                 item = items[i];
1863                                         }
1864                                 }
1865                         }
1866                         if (item != null) {
1867                                 if (item.closeRect.contains(x,y)){
1868                                         item.closeImageState = SWT.SELECTED;
1869                                         redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1870                                         update();
1871                                         return;
1872                                 }
1873                                 int index = indexOf(item);
1874                                 if (item.showing){
1875                                         int oldSelectedIndex = selectedIndex;
1876                                         setSelection(index, true);
1877                                         if (oldSelectedIndex == selectedIndex) {
1878                                                 /* If the click is on the selected tabitem, then set focus to the tabfolder */
1879                                                 forceFocus();
1880                                         }
1881                                 }
1882                                 return;
1883                         }
1884                         break;
1885                 }
1886                 case SWT.MouseMove: {
1887                         _setToolTipText(event.x, event.y);
1888                         boolean close = false;
1889                         for (int i=0; i<items.length; i++) {
1890                                 CTabItem item = items[i];
1891                                 close = false;
1892                                 if (item.getBounds().contains(x, y)) {
1893                                         close = true;
1894                                         if (item.closeRect.contains(x, y)) {
1895                                                 if (item.closeImageState != SWT.SELECTED && item.closeImageState != SWT.HOT) {
1896                                                         item.closeImageState = SWT.HOT;
1897                                                         redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1898                                                 }
1899                                         } else {
1900                                                 if (item.closeImageState != SWT.NONE) {
1901                                                         item.closeImageState = SWT.NONE;
1902                                                         redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1903                                                 }
1904                                         }
1905                                         if ((item.state & SWT.HOT) == 0) {
1906                                                 item.state |= SWT.HOT;
1907                                                 redraw(item.x, item.y, item.width, item.height, false);
1908                                         }
1909                                 }
1910                                 if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND && !close) {
1911                                         item.closeImageState = SWT.BACKGROUND;
1912                                         redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1913                                 }
1914                                 if ((item.state & SWT.HOT) != 0 && !close) {
1915                                         item.state &= ~SWT.HOT;
1916                                         redraw(item.x, item.y, item.width, item.height, false);
1917                                 }
1918                                 if (i == selectedIndex && item.closeImageState != SWT.NONE && !close) {
1919                                         item.closeImageState = SWT.NONE;
1920                                         redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1921                                 }
1922                         }
1923                         break;
1924                 }
1925                 case SWT.MouseUp: {
1926                         if (event.button != 1) return;
1927                         CTabItem item = null;
1928                         if (single) {
1929                                 if (selectedIndex != -1) {
1930                                         Rectangle bounds = items[selectedIndex].getBounds();
1931                                         if (bounds.contains(x, y)){
1932                                                 item = items[selectedIndex];
1933                                         }
1934                                 }
1935                         } else {
1936                                 for (int i=0; i<items.length; i++) {
1937                                         Rectangle bounds = items[i].getBounds();
1938                                         if (bounds.contains(x, y)){
1939                                                 item = items[i];
1940                                         }
1941                                 }
1942                         }
1943                         if (item != null) {
1944                                 if (item.closeRect.contains(x,y)) {
1945                                         boolean selected = item.closeImageState == SWT.SELECTED;
1946                                         item.closeImageState = SWT.HOT;
1947                                         redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1948                                         if (!selected) return;
1949                                         CTabFolderEvent e = new CTabFolderEvent(this);
1950                                         e.widget = this;
1951                                         e.time = event.time;
1952                                         e.item = item;
1953                                         e.doit = true;
1954                                         for (int j = 0; j < folderListeners.length; j++) {
1955                                                 CTabFolder2Listener listener = folderListeners[j];
1956                                                 listener.close(e);
1957                                         }
1958                                         for (int j = 0; j < tabListeners.length; j++) {
1959                                                 CTabFolderListener listener = tabListeners[j];
1960                                                 listener.itemClosed(e);
1961                                         }
1962                                         if (e.doit) item.dispose();
1963                                         if (!isDisposed() && item.isDisposed()) {
1964                                                 Display display = getDisplay();
1965                                                 Point pt = display.getCursorLocation();
1966                                                 pt = display.map(null, this, pt.x, pt.y);
1967                                                 CTabItem nextItem = getItem(pt);
1968                                                 if (nextItem != null) {
1969                                                         if (nextItem.closeRect.contains(pt)) {
1970                                                                 if (nextItem.closeImageState != SWT.SELECTED && nextItem.closeImageState != SWT.HOT) {
1971                                                                         nextItem.closeImageState = SWT.HOT;
1972                                                                         redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
1973                                                                 }
1974                                                         } else {
1975                                                                 if (nextItem.closeImageState != SWT.NONE) {
1976                                                                         nextItem.closeImageState = SWT.NONE;
1977                                                                         redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
1978                                                                 }
1979                                                         }
1980                                                 }
1981                                         }
1982                                         return;
1983                                 }
1984                         }
1985                 }
1986         }
1987 }
1988 void onPageTraversal(Event event) {
1989         int count = items.length;
1990         if (count == 0) return;
1991         int index = selectedIndex;
1992         if (index  == -1) {
1993                 index = 0;
1994         } else {
1995                 int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
1996                 if (!mru) {
1997                         index = (selectedIndex + offset + count) % count;
1998                 } else {
1999                         int[] visible = new int[items.length];
2000                         int idx = 0;
2001                         int current = -1;
2002                         for (int i = 0; i < items.length; i++) {
2003                                 if (items[i].showing) {
2004                                         if (i == selectedIndex) current = idx;
2005                                         visible [idx++] = i;
2006                                 }
2007                         }
2008                         if (current + offset >= 0 && current + offset < idx){
2009                                 index = visible [current + offset];
2010                         } else {
2011                                 if (showChevron) {
2012                                         Rectangle chevronRect = chevronItem.getBounds();
2013                                         chevronRect = event.display.map(chevronTb, this, chevronRect);
2014                                         CTabFolderEvent e = new CTabFolderEvent(this);
2015                                         e.widget = this;
2016                                         e.time = event.time;
2017                                         e.x = chevronRect.x;
2018                                         e.y = chevronRect.y;
2019                                         e.width = chevronRect.width;
2020                                         e.height = chevronRect.height;
2021                                         e.doit = true;
2022                                         for (int i = 0; i < folderListeners.length; i++) {
2023                                                 folderListeners[i].showList(e);
2024                                         }
2025                                         if (e.doit && !isDisposed()) {
2026                                                 showList(chevronRect);
2027                                         }
2028                                 }
2029                         }
2030                 }
2031         }
2032         setSelection (index, true);
2033 }
2034 void onPaint(Event event) {
2035         if (inDispose) return;
2036         Font font = getFont();
2037         if (oldFont == null || !oldFont.equals(font)) {
2038                 // handle case where  default font changes
2039                 oldFont = font;
2040                 if (!updateTabHeight(false)) {
2041                         updateItems();
2042                         redraw();
2043                         return;
2044                 }
2045         }
2046
2047         GC gc = event.gc;
2048         Font gcFont = gc.getFont();
2049         Color gcBackground = gc.getBackground();
2050         Color gcForeground = gc.getForeground();
2051
2052 // Useful for debugging paint problems
2053 //{
2054 //Point size = getSize();
2055 //gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN));
2056 //gc.fillRectangle(-10, -10, size.x + 20, size.y+20);
2057 //}
2058
2059         Point size = getSize();
2060         Rectangle bodyRect = new Rectangle(0, 0, size.x, size.y);
2061         renderer.draw(CTabFolderRenderer.PART_BODY, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
2062
2063         gc.setFont(gcFont);
2064         gc.setForeground(gcForeground);
2065         gc.setBackground(gcBackground);
2066
2067         renderer.draw(CTabFolderRenderer.PART_HEADER, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
2068
2069         gc.setFont(gcFont);
2070         gc.setForeground(gcForeground);
2071         gc.setBackground(gcBackground);
2072
2073         if (!single) {
2074                 for (int i=0; i < items.length; i++) {
2075                         Rectangle itemBounds = items[i].getBounds();
2076                         if (i != selectedIndex && event.getBounds().intersects(itemBounds)) {
2077                                 renderer.draw(i, SWT.BACKGROUND | SWT.FOREGROUND | items[i].state , itemBounds, gc);
2078                         }
2079                 }
2080         }
2081
2082         gc.setFont(gcFont);
2083         gc.setForeground(gcForeground);
2084         gc.setBackground(gcBackground);
2085
2086         if (selectedIndex != -1) {
2087                 renderer.draw(selectedIndex, items[selectedIndex].state | SWT.BACKGROUND | SWT.FOREGROUND, items[selectedIndex].getBounds(), gc);
2088         }
2089
2090         gc.setFont(gcFont);
2091         gc.setForeground(gcForeground);
2092         gc.setBackground(gcBackground);
2093
2094         if (hoverTb) {
2095                 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
2096                 int x = getSize().x - (trim.width + trim.x);
2097                 hoverRect = new Rectangle(x - 16 - SPACING, 2, 16, getTabHeight() - 2);
2098                 gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
2099                 x = hoverRect.x;
2100                 int y = hoverRect.y;
2101                 gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
2102                 gc.fillRectangle(x + hoverRect.width - 6, y, 5, 5);
2103                 gc.drawRectangle(x + hoverRect.width - 6, y, 5, 5);
2104                 gc.drawLine(x + hoverRect.width - 6, y+2, x + hoverRect.width - 6 + 5, y + 2);
2105                 gc.fillRectangle(x, y, 5 , 2);
2106                 gc.drawRectangle(x, y, 5 , 2);
2107         }
2108         gc.setFont(gcFont);
2109         gc.setForeground(gcForeground);
2110         gc.setBackground(gcBackground);
2111 }
2112
2113 void onResize(Event event) {
2114         if (inDispose) return;
2115         if (ignoreResize) return;
2116         if (updateItems()) {
2117                 redrawTabs();
2118         }
2119         Point size = getSize();
2120         if (oldSize == null) {
2121                 redraw();
2122         } else {
2123                 if (onBottom && size.y != oldSize.y) {
2124                         redraw();
2125                 } else {
2126                         int x1 = Math.min(size.x, oldSize.x);
2127                         Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
2128                         if (size.x != oldSize.x) x1 -= trim.width + trim.x - marginWidth + 2;
2129                         if (!simple) x1 -= 5; // rounded top right corner
2130                         int y1 = Math.min(size.y, oldSize.y);
2131                         if (size.y != oldSize.y) y1 -= trim.height + trim.y - marginHeight;
2132                         int x2 = Math.max(size.x, oldSize.x);
2133                         int y2 = Math.max(size.y, oldSize.y);
2134                         redraw(0, y1, x2, y2 - y1, false);
2135                         redraw(x1, 0, x2 - x1, y2, false);
2136                         if (hoverTb) {
2137                                 redraw(hoverRect.x, hoverRect.y, hoverRect.width, hoverRect.height, false);
2138                         }
2139                 }
2140         }
2141         oldSize = size;
2142 }
2143 void onSelection(Event event) {
2144         if (hovering) {
2145                 hovering = false;
2146                 updateItems();
2147         }
2148         if (event.widget == maxItem) {
2149                 CTabFolderEvent e = new CTabFolderEvent(this);
2150                 e.widget = CTabFolder.this;
2151                 e.time = event.time;
2152                 for (int i = 0; i < folderListeners.length; i++) {
2153                         if (maximized) {
2154                                 folderListeners[i].restore(e);
2155                         } else {
2156                                 folderListeners[i].maximize(e);
2157                         }
2158                 }
2159         } else if (event.widget == minItem) {
2160                 CTabFolderEvent e = new CTabFolderEvent(this);
2161                 e.widget = CTabFolder.this;
2162                 e.time = event.time;
2163                 for (int i = 0; i < folderListeners.length; i++) {
2164                         if (minimized) {
2165                                 folderListeners[i].restore(e);
2166                         } else {
2167                                 folderListeners[i].minimize(e);
2168                         }
2169                 }
2170         } else if (event.widget == chevronItem) {
2171                 Rectangle chevronRect = chevronItem.getBounds();
2172                 chevronRect = event.display.map(chevronTb, this, chevronRect);
2173                 CTabFolderEvent e = new CTabFolderEvent(this);
2174                 e.widget = this;
2175                 e.time = event.time;
2176                 e.x = chevronRect.x;
2177                 e.y = chevronRect.y;
2178                 e.width = chevronRect.width;
2179                 e.height = chevronRect.height;
2180                 e.doit = true;
2181                 for (int i = 0; i < folderListeners.length; i++) {
2182                         folderListeners[i].showList(e);
2183                 }
2184                 if (e.doit && !isDisposed()) {
2185                         showList(chevronRect);
2186                 }
2187         }
2188 }
2189 void onTraverse (Event event) {
2190         if (ignoreTraverse) return;
2191         runUpdate();
2192         switch (event.detail) {
2193                 case SWT.TRAVERSE_ESCAPE:
2194                 case SWT.TRAVERSE_RETURN:
2195                 case SWT.TRAVERSE_TAB_NEXT:
2196                 case SWT.TRAVERSE_TAB_PREVIOUS:
2197                         Control focusControl = getDisplay().getFocusControl();
2198                         if (focusControl == this) event.doit = true;
2199                         break;
2200                 case SWT.TRAVERSE_MNEMONIC:
2201                         event.doit = onMnemonic(event, false);
2202                         break;
2203                 case SWT.TRAVERSE_PAGE_NEXT:
2204                 case SWT.TRAVERSE_PAGE_PREVIOUS:
2205                         event.doit = items.length > 0;
2206                         break;
2207         }
2208         ignoreTraverse = true;
2209         notifyListeners(SWT.Traverse, event);
2210         ignoreTraverse = false;
2211         event.type = SWT.None;
2212         if (isDisposed()) return;
2213         if (!event.doit) return;
2214         switch (event.detail) {
2215                 case SWT.TRAVERSE_MNEMONIC:
2216                         onMnemonic(event, true);
2217                         event.detail = SWT.TRAVERSE_NONE;
2218                         break;
2219                 case SWT.TRAVERSE_PAGE_NEXT:
2220                 case SWT.TRAVERSE_PAGE_PREVIOUS:
2221                         onPageTraversal(event);
2222                         event.detail = SWT.TRAVERSE_NONE;
2223                         break;
2224         }
2225 }
2226 void redrawTabs() {
2227         Point size = getSize();
2228         Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
2229         if (onBottom) {
2230                 int h = trim.height + trim.y - marginHeight;
2231                 redraw(0, size.y - h - 1, size.x, h + 1, false);
2232         } else {
2233                 redraw(0, 0, size.x, -trim.y - marginHeight + 1, false);
2234         }
2235 }
2236 /**
2237  * Removes the listener.
2238  *
2239  * @param listener the listener which should no longer be notified
2240  *
2241  * @exception IllegalArgumentException <ul>
2242  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2243  * </ul>
2244  *
2245  * @exception SWTException <ul>
2246  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2247  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2248  * </ul>
2249  *
2250  * @see #addCTabFolder2Listener(CTabFolder2Listener)
2251  *
2252  * @since 3.0
2253  */
2254 public void removeCTabFolder2Listener(CTabFolder2Listener listener) {
2255         checkWidget();
2256         if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2257         if (folderListeners.length == 0) return;
2258         int index = -1;
2259         for (int i = 0; i < folderListeners.length; i++) {
2260                 if (listener == folderListeners[i]){
2261                         index = i;
2262                         break;
2263                 }
2264         }
2265         if (index == -1) return;
2266         if (folderListeners.length == 1) {
2267                 folderListeners = new CTabFolder2Listener[0];
2268                 return;
2269         }
2270         CTabFolder2Listener[] newTabListeners = new CTabFolder2Listener[folderListeners.length - 1];
2271         System.arraycopy(folderListeners, 0, newTabListeners, 0, index);
2272         System.arraycopy(folderListeners, index + 1, newTabListeners, index, folderListeners.length - index - 1);
2273         folderListeners = newTabListeners;
2274 }
2275 /**
2276  * Removes the listener.
2277  *
2278  * @param listener the listener which should no longer be notified
2279  *
2280  * @exception IllegalArgumentException <ul>
2281  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2282  * </ul>
2283  *
2284  * @exception SWTException <ul>
2285  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2286  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2287  * </ul>
2288  *
2289  * @deprecated see removeCTabFolderCloseListener(CTabFolderListener)
2290  */
2291 @Deprecated
2292 public void removeCTabFolderListener(CTabFolderListener listener) {
2293         checkWidget();
2294         if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2295         if (tabListeners.length == 0) return;
2296         int index = -1;
2297         for (int i = 0; i < tabListeners.length; i++) {
2298                 if (listener == tabListeners[i]){
2299                         index = i;
2300                         break;
2301                 }
2302         }
2303         if (index == -1) return;
2304         if (tabListeners.length == 1) {
2305                 tabListeners = new CTabFolderListener[0];
2306                 return;
2307         }
2308         CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
2309         System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
2310         System.arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1);
2311         tabListeners = newTabListeners;
2312 }
2313 /**
2314  * Removes the listener from the collection of listeners who will
2315  * be notified when the user changes the receiver's selection.
2316  *
2317  * @param listener the listener which should no longer be notified
2318  *
2319  * @exception IllegalArgumentException <ul>
2320  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2321  * </ul>
2322  * @exception SWTException <ul>
2323  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2324  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2325  * </ul>
2326  *
2327  * @see SelectionListener
2328  * @see #addSelectionListener
2329  */
2330 public void removeSelectionListener(SelectionListener listener) {
2331         checkWidget();
2332         if (listener == null) {
2333                 SWT.error(SWT.ERROR_NULL_ARGUMENT);
2334         }
2335         removeListener(SWT.Selection, listener);
2336         removeListener(SWT.DefaultSelection, listener);
2337 }
2338
2339 @Override
2340 public void reskin(int flags) {
2341         super.reskin(flags);
2342         for (int i = 0; i < items.length; i++) {
2343                 items[i].reskin(flags);
2344         }
2345 }
2346
2347 @Override
2348 public void setBackground (Color color) {
2349         super.setBackground(color);
2350         renderer.createAntialiasColors(); //TODO: need better caching strategy
2351         updateBkImages();
2352         redraw();
2353 }
2354 /**
2355  * Specify a gradient of colors to be drawn in the background of the unselected tabs.
2356  * For example to draw a gradient that varies from dark blue to blue and then to
2357  * white, use the following call to setBackground:
2358  * <pre>
2359  *      cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2360  *                                         display.getSystemColor(SWT.COLOR_BLUE),
2361  *                                         display.getSystemColor(SWT.COLOR_WHITE),
2362  *                                         display.getSystemColor(SWT.COLOR_WHITE)},
2363  *                             new int[] {25, 50, 100});
2364  * </pre>
2365  *
2366  * @param colors an array of Color that specifies the colors to appear in the gradient
2367  *               in order of appearance left to right.  The value <code>null</code> clears the
2368  *               background gradient. The value <code>null</code> can be used inside the array of
2369  *               Color to specify the background color.
2370  * @param percents an array of integers between 0 and 100 specifying the percent of the width
2371  *                 of the widget at which the color should change.  The size of the <code>percents</code>
2372  *                 array must be one less than the size of the <code>colors</code> array.
2373  *
2374  * @exception SWTException <ul>
2375  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2376  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2377  *      </ul>
2378  *
2379  * @since 3.6
2380  */
2381 public void setBackground(Color[] colors, int[] percents) {
2382         setBackground(colors, percents, false);
2383 }
2384 /**
2385  * Specify a gradient of colors to be drawn in the background of the unselected tab.
2386  * For example to draw a vertical gradient that varies from dark blue to blue and then to
2387  * white, use the following call to setBackground:
2388  * <pre>
2389  *      cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2390  *                                         display.getSystemColor(SWT.COLOR_BLUE),
2391  *                                         display.getSystemColor(SWT.COLOR_WHITE),
2392  *                                         display.getSystemColor(SWT.COLOR_WHITE)},
2393  *                                new int[] {25, 50, 100}, true);
2394  * </pre>
2395  *
2396  * @param colors an array of Color that specifies the colors to appear in the gradient
2397  *               in order of appearance left to right.  The value <code>null</code> clears the
2398  *               background gradient. The value <code>null</code> can be used inside the array of
2399  *               Color to specify the background color.
2400  * @param percents an array of integers between 0 and 100 specifying the percent of the width
2401  *                 of the widget at which the color should change.  The size of the <code>percents</code>
2402  *                 array must be one less than the size of the <code>colors</code> array.
2403  *
2404  * @param vertical indicate the direction of the gradient. <code>True</code> is vertical and <code>false</code> is horizontal.
2405  *
2406  * @exception SWTException <ul>
2407  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2408  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2409  *      </ul>
2410  *
2411  * @since 3.6
2412  */
2413 public void setBackground(Color[] colors, int[] percents, boolean vertical) {
2414         checkWidget();
2415         if (colors != null) {
2416                 if (percents == null || percents.length != colors.length - 1) {
2417                         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2418                 }
2419                 for (int i = 0; i < percents.length; i++) {
2420                         if (percents[i] < 0 || percents[i] > 100) {
2421                                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2422                         }
2423                         if (i > 0 && percents[i] < percents[i-1]) {
2424                                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2425                         }
2426                 }
2427                 if (getDisplay().getDepth() < 15) {
2428                         // Don't use gradients on low color displays
2429                         colors = new Color[] {colors[colors.length - 1]};
2430                         percents = new int[] {};
2431                 }
2432         }
2433
2434         // Are these settings the same as before?
2435         if ((gradientColors != null) && (colors != null) &&
2436                 (gradientColors.length == colors.length)) {
2437                 boolean same = false;
2438                 for (int i = 0; i < gradientColors.length; i++) {
2439                         if (gradientColors[i] == null) {
2440                         same = colors[i] == null;
2441                         } else {
2442                         same = gradientColors[i].equals(colors[i]);
2443                         }
2444                         if (!same) break;
2445                 }
2446                 if (same) {
2447                         for (int i = 0; i < gradientPercents.length; i++) {
2448                         same = gradientPercents[i] == percents[i];
2449                         if (!same) break;
2450                         }
2451                 }
2452                 if (same && this.gradientVertical == vertical) return;
2453         }
2454         // Store the new settings
2455         if (colors == null) {
2456                 gradientColors = null;
2457                 gradientPercents = null;
2458                 gradientVertical = false;
2459                 setBackground((Color)null);
2460         } else {
2461                 gradientColors = new Color[colors.length];
2462                 for (int i = 0; i < colors.length; ++i) {
2463                         gradientColors[i] = colors[i];
2464                 }
2465                 gradientPercents = new int[percents.length];
2466                 for (int i = 0; i < percents.length; ++i) {
2467                         gradientPercents[i] = percents[i];
2468                 }
2469                 gradientVertical = vertical;
2470                 setBackground(gradientColors[gradientColors.length-1]);
2471         }
2472
2473         // Refresh with the new settings
2474         redraw();
2475 }
2476 @Override
2477 public void setBackgroundImage(Image image) {
2478                 super.setBackgroundImage(image);
2479                 renderer.createAntialiasColors(); //TODO: need better caching strategy
2480                 redraw();
2481 }
2482 /**
2483  * Toggle the visibility of the border
2484  *
2485  * @param show true if the border should be displayed
2486  *
2487  * @exception SWTException <ul>
2488  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2489  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2490  * </ul>
2491  */
2492 public void setBorderVisible(boolean show) {
2493         checkWidget();
2494         if (borderVisible == show) return;
2495         this.borderVisible = show;
2496         updateFolder(REDRAW);
2497 }
2498 void setButtonBounds(GC gc) {
2499         Point size = getSize();
2500         // max button
2501         Display display = getDisplay();
2502         if (showMax) {
2503                 if (minMaxTb == null) {
2504                         minMaxTb = new ToolBar(this, SWT.FLAT);
2505                         initAccessibleMinMaxTb();
2506                         addTabControl(minMaxTb, SWT.TRAIL, 0, false);
2507                 }
2508                 if (maxItem == null) {
2509                         maxItem = new ToolItem(minMaxTb, SWT.PUSH);
2510                         if (maxImage == null) {
2511                                 maxImage = createButtonImage(display, CTabFolderRenderer.PART_MAX_BUTTON);
2512                         }
2513                         maxItem.setImage(maxImage);
2514                         maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
2515                         maxItem.addListener(SWT.Selection, listener);
2516                 }
2517         } else {
2518                 //might need to remove it if already there
2519                 if (maxItem != null) {
2520                         maxItem.dispose();
2521                         maxItem = null;
2522                 }
2523         }
2524         // min button
2525         if (showMin) {
2526                 if (minMaxTb == null) {
2527                         minMaxTb = new ToolBar(this, SWT.FLAT);
2528                         initAccessibleMinMaxTb();
2529                         addTabControl(minMaxTb, SWT.TRAIL, 0, false);
2530                 }
2531                 if (minItem == null) {
2532                         minItem = new ToolItem(minMaxTb, SWT.PUSH, 0);
2533                         if (minImage == null) {
2534                                 minImage = createButtonImage(display, CTabFolderRenderer.PART_MIN_BUTTON);
2535                         }
2536                         minItem.setImage(minImage);
2537                         minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
2538                         minItem.addListener(SWT.Selection, listener);
2539                 }
2540         } else {
2541                 //might need to remove it if already there
2542                 if (minItem != null) {
2543                         minItem.dispose();
2544                         minItem = null;
2545                 }
2546         }
2547         if (minMaxTb != null && minMaxTb.getItemCount() == 0) {
2548                 removeTabControl(minMaxTb, false);
2549                 minMaxTb.dispose();
2550                 minMaxTb = null;
2551         }
2552         if (showChevron) {
2553                 int itemCount = items.length;
2554                 int count;
2555                 if (single) {
2556                         count = selectedIndex == -1 ? itemCount : itemCount - 1;
2557                 } else {
2558                         int showCount = 0;
2559                         while (showCount < priority.length && items[priority[showCount]].showing) {
2560                                 showCount++;
2561                         }
2562                         count = itemCount - showCount;
2563                 }
2564                 if (count != chevronCount) {
2565                         chevronCount = count;
2566                         if (chevronImage != null) chevronImage.dispose();
2567                         chevronImage = createButtonImage(display, CTabFolderRenderer.PART_CHEVRON_BUTTON);
2568                         chevronItem.setImage(chevronImage);
2569                 }
2570         }
2571
2572         boolean[][] overflow = new boolean[1][0];
2573         Rectangle[] rects = computeControlBounds(size, overflow);
2574         if (fixedTabHeight != SWT.DEFAULT) {
2575                 int height = fixedTabHeight;
2576                 if (!hovering) {
2577                         hoverTb = false;
2578                         Rectangle tabBounds = this.getBounds();
2579                         for (int i = 0; i < rects.length; i++) {
2580                                 if (!(overflow[0][i])) {
2581                                         if (rects[i].height > height) {
2582                                                 hoverTb = true;
2583                                                 break;
2584                                         }
2585                                 }
2586                         }
2587                         if (hoverTb) {
2588                                 for (int i = 0; i < rects.length; i++) {
2589                                         if (!(overflow[0][i])) {
2590                                                 if (rects[i].height > height) {
2591                                                         rects[i].x = tabBounds.width + 20;
2592                                                 }
2593                                         }
2594                                 }
2595                         }
2596                 }
2597         }
2598         int headerHeight = 0;
2599         for (int i = 0; i < rects.length; i++) {
2600                 if (!overflow[0][i]) headerHeight = Math.max(rects[i].height, headerHeight);
2601         }
2602         boolean changed = false;
2603         ignoreResize = true;
2604         for (int i = 0; i < controls.length; i++) {
2605                 if (!controls[i].isDisposed()) {
2606                         if (overflow[0][i]) {
2607                                 controls[i].setBounds(rects[i]);
2608                         } else {
2609                                 controls[i].moveAbove(null);
2610                                 controls[i].setBounds(rects[i].x, rects[i].y, rects[i].width, headerHeight);
2611                         }
2612                 }
2613                 if (!changed && !rects[i].equals(controlRects[i])) changed = true;
2614         }
2615         ignoreResize = false;
2616         controlRects = rects;
2617         if (changed || hovering) updateBkImages();
2618 }
2619 @Override
2620 public boolean setFocus () {
2621         checkWidget ();
2622
2623         /*
2624         * Feature in SWT.  When a new tab item is selected
2625         * and the previous tab item had focus, removing focus
2626         * from the previous tab item causes fixFocus() to give
2627         * focus to the first child, which is usually one of the
2628         * toolbars. This is unexpected.
2629         * The fix is to try to set focus on the first tab item
2630         * if fixFocus() is called.
2631         */
2632         Control focusControl = getDisplay().getFocusControl ();
2633         boolean fixFocus = isAncestor (focusControl);
2634         if (fixFocus) {
2635                 CTabItem item = getSelection();
2636                 if (item != null) {
2637                         if (item.setFocus ()) return true;
2638                 }
2639         }
2640         return super.setFocus ();
2641 }
2642 /* Copy of isFocusAncestor from Control. */
2643 boolean isAncestor (Control control) {
2644         while (control != null && control != this && !(control instanceof Shell)) {
2645                 control = control.getParent();
2646         }
2647         return control == this;
2648 }
2649 @Override
2650 public void setFont(Font font) {
2651         checkWidget();
2652         if (font != null && font.equals(getFont())) return;
2653         super.setFont(font);
2654         oldFont = getFont();
2655         updateFolder(REDRAW);
2656 }
2657 @Override
2658 public void setForeground (Color color) {
2659         super.setForeground(color);
2660         redraw();
2661 }
2662 /**
2663  * Display an insert marker before or after the specified tab item.
2664  *
2665  * A value of null will clear the mark.
2666  *
2667  * @param item the item with which the mark is associated or null
2668  *
2669  * @param after true if the mark should be displayed after the specified item
2670  *
2671  * @exception SWTException <ul>
2672  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2673  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2674  * </ul>
2675  */
2676 public void setInsertMark(CTabItem item, boolean after) {
2677         checkWidget();
2678 }
2679 /**
2680  * Display an insert marker before or after the specified tab item.
2681  *
2682  * A value of -1 will clear the mark.
2683  *
2684  * @param index the index of the item with which the mark is associated or -1
2685  *
2686  * @param after true if the mark should be displayed after the specified item
2687  *
2688  * @exception IllegalArgumentException <ul>
2689  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
2690  * </ul>
2691  *
2692  * @exception SWTException <ul>
2693  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2694  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2695  * </ul>
2696  */
2697 public void setInsertMark(int index, boolean after) {
2698         checkWidget();
2699         if (index < -1 || index >= getItemCount()) {
2700                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2701         }
2702 }
2703 boolean setItemLocation(GC gc) {
2704         boolean changed = false;
2705         if (items.length == 0) return false;
2706         Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
2707         int borderBottom = trim.height + trim.y;
2708         int borderTop = -trim.y;
2709         Point size = getSize();
2710         int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop;
2711         Point closeButtonSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, 0, gc, SWT.DEFAULT, SWT.DEFAULT);
2712         int leftItemEdge = getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
2713         if (single) {
2714                 int defaultX = getDisplay().getBounds().width + 10; // off screen
2715                 for (int i = 0; i < items.length; i++) {
2716                         CTabItem item = items[i];
2717                         if (i == selectedIndex) {
2718                                 firstIndex = selectedIndex;
2719                                 int oldX = item.x, oldY = item.y;
2720                                 item.x = leftItemEdge;
2721                                 item.y = y;
2722                                 item.showing = true;
2723                                 if (showClose || item.showClose) {
2724                                         item.closeRect.x = leftItemEdge - renderer.computeTrim(i, SWT.NONE, 0, 0, 0, 0).x;
2725                                         item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
2726                                 }
2727                                 if (item.x != oldX || item.y != oldY) changed = true;
2728                         } else {
2729                                 item.x = defaultX;
2730                                 item.showing = false;
2731                         }
2732                 }
2733         } else {
2734                 int rightItemEdge = getRightItemEdge(gc);
2735                 int maxWidth = rightItemEdge - leftItemEdge;
2736                 int width = 0;
2737                 for (int i = 0; i < priority.length; i++) {
2738                         CTabItem item = items[priority[i]];
2739                         width += item.width;
2740                         item.showing = i == 0 ? true : item.width > 0 && width <= maxWidth;
2741                 }
2742                 int x = getLeftItemEdge(gc, CTabFolderRenderer.PART_HEADER);
2743                 int defaultX = getDisplay().getBounds().width + 10; // off screen
2744                 firstIndex = items.length - 1;
2745                 for (int i = 0; i < items.length; i++) {
2746                         CTabItem item = items[i];
2747                         if (!item.showing) {
2748                                 if (item.x != defaultX) changed = true;
2749                                 item.x = defaultX;
2750                         } else {
2751                                 firstIndex = Math.min(firstIndex, i);
2752                                 if (item.x != x || item.y != y) changed = true;
2753                                 item.x = x;
2754                                 item.y = y;
2755                                 int state = SWT.NONE;
2756                                 if (i == selectedIndex) state |= SWT.SELECTED;
2757                                 Rectangle edgeTrim = renderer.computeTrim(i, state, 0, 0, 0, 0);
2758                                 item.closeRect.x = item.x + item.width  - (edgeTrim.width + edgeTrim.x) - closeButtonSize.x;
2759                                 item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
2760                                 x = x + item.width;
2761                                 if (!simple && i == selectedIndex) x -= renderer.curveIndent; //TODO: fix next item position
2762                         }
2763                 }
2764         }
2765         return changed;
2766 }
2767 /**
2768  * Reorder the items of the receiver.
2769  * @param indices an array containing the new indices for all items
2770  *
2771  * @exception IllegalArgumentException <ul>
2772  *    <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
2773  *    <li>ERROR_INVALID_ARGUMENT - if the indices array is not the same length as the number of items,
2774  *    if there are duplicate indices or an index is out of range.</li>
2775  * </ul>
2776  *
2777  * @exception SWTException <ul>
2778  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2779  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2780  * </ul>
2781  */
2782 /*public*/ void setItemOrder (int[] indices) {
2783         checkWidget();
2784         if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2785         if (indices.length != items.length) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2786         int newSelectedIndex = -1;
2787         boolean[] seen = new boolean[items.length];
2788         CTabItem[] temp = new CTabItem[items.length];
2789         for (int i=0; i<indices.length; i++) {
2790                 int index = indices[i];
2791                 if (!(0 <= index && index < items.length)) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2792                 if (seen[index]) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2793                 seen[index] = true;
2794                 if (index == selectedIndex) newSelectedIndex = i;
2795                 temp[i] = items[index];
2796         }
2797         items = temp;
2798         selectedIndex = newSelectedIndex;
2799         updateFolder(REDRAW);
2800 }
2801 boolean setItemSize(GC gc) {
2802         boolean changed = false;
2803         if (isDisposed()) return changed;
2804         Point size = getSize();
2805         if (size.x <= 0 || size.y <= 0) return changed;
2806         ToolBar chevron = getChevron();
2807         if (chevron != null) chevron.setVisible(false);
2808         showChevron = false;
2809         if (single) {
2810                 showChevron = chevronVisible && items.length > 1;
2811                 if (showChevron) {
2812                         chevron.setVisible(true);
2813                 }
2814                 if (selectedIndex != -1) {
2815                         CTabItem tab = items[selectedIndex];
2816                         int width = renderer.computeSize(selectedIndex, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2817                         width = Math.min(width, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
2818                         if (tab.height != tabHeight || tab.width != width) {
2819                                 changed = true;
2820                                 tab.shortenedText = null;
2821                                 tab.shortenedTextWidth = 0;
2822                                 tab.height = tabHeight;
2823                                 tab.width = width;
2824                                 tab.closeRect.width = tab.closeRect.height = 0;
2825                                 if (showClose || tab.showClose) {
2826                                         Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT);
2827                                         tab.closeRect.width = closeSize.x;
2828                                         tab.closeRect.height = closeSize.y;
2829                                 }
2830                         }
2831                 }
2832                 return changed;
2833         }
2834
2835         if (items.length == 0) return changed;
2836         int[] widths;
2837         int tabAreaWidth = Math.max(0, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
2838         // First, try the minimum tab size at full compression.
2839         int minWidth = 0;
2840         int[] minWidths = new int[items.length];
2841         for (int i = 0; i < priority.length; i++) {
2842                 int index = priority[i];
2843                 int state = CTabFolderRenderer.MINIMUM_SIZE;
2844                 if (index == selectedIndex) state |= SWT.SELECTED;
2845                 minWidths[index] = renderer.computeSize(index, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2846                 minWidth += minWidths[index];
2847                 if (minWidth > tabAreaWidth) break;
2848         }
2849         if (minWidth > tabAreaWidth) {
2850                 // full compression required and a chevron
2851                 showChevron = chevronVisible && items.length > 1;
2852                 if (showChevron) {
2853                         tabAreaWidth -= chevron.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
2854                         chevron.setVisible(true);
2855                 }
2856                 widths = minWidths;
2857                 int index = selectedIndex != -1 ? selectedIndex : 0;
2858                 if (tabAreaWidth < widths[index]) {
2859                         widths[index] = Math.max(0, tabAreaWidth);
2860                 }
2861         } else {
2862                 int maxWidth = 0;
2863                 int[] maxWidths = new int[items.length];
2864                 for (int i = 0; i < items.length; i++) {
2865                         int state = 0;
2866                         if (i == selectedIndex) state |= SWT.SELECTED;
2867                         maxWidths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2868                         maxWidth += maxWidths[i];
2869                 }
2870                 if (maxWidth <= tabAreaWidth) {
2871                         // no compression required
2872                         widths = maxWidths;
2873                 } else {
2874                         // determine compression for each item
2875                         int extra = (tabAreaWidth - minWidth) / items.length;
2876                         while (true) {
2877                                 int large = 0, totalWidth = 0;
2878                                 for (int i = 0 ; i < items.length; i++) {
2879                                         if (maxWidths[i] > minWidths[i] + extra) {
2880                                                 totalWidth += minWidths[i] + extra;
2881                                                 large++;
2882                                         } else {
2883                                                 totalWidth += maxWidths[i];
2884                                         }
2885                                 }
2886                                 if (totalWidth >= tabAreaWidth) {
2887                                         extra--;
2888                                         break;
2889                                 }
2890                                 if (large == 0 || tabAreaWidth - totalWidth < large) break;
2891                                 extra++;
2892                         }
2893                         widths = new int[items.length];
2894                         for (int i = 0; i < items.length; i++) {
2895                                 widths[i] = Math.min(maxWidths[i], minWidths[i] + extra);
2896                         }
2897                 }
2898         }
2899
2900         for (int i = 0; i < items.length; i++) {
2901                 CTabItem tab = items[i];
2902                 int width = widths[i];
2903                 if (tab.height != tabHeight || tab.width != width) {
2904                         changed = true;
2905                         tab.shortenedText = null;
2906                         tab.shortenedTextWidth = 0;
2907                         tab.height = tabHeight;
2908                         tab.width = width;
2909                         tab.closeRect.width = tab.closeRect.height = 0;
2910                         if (showClose || tab.showClose) {
2911                                 if (i == selectedIndex || showUnselectedClose) {
2912                                         Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT);
2913                                         tab.closeRect.width = closeSize.x;
2914                                         tab.closeRect.height = closeSize.y;
2915                                 }
2916                         }
2917                 }
2918         }
2919         return changed;
2920 }
2921 /**
2922  * Marks the receiver's maximize button as visible if the argument is <code>true</code>,
2923  * and marks it invisible otherwise.
2924  *
2925  * @param visible the new visibility state
2926  *
2927  * @exception SWTException <ul>
2928  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2929  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2930  * </ul>
2931  *
2932  * @since 3.0
2933  */
2934 public void setMaximizeVisible(boolean visible) {
2935         checkWidget();
2936         if (showMax == visible) return;
2937         // display maximize button
2938         showMax = visible;
2939         updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
2940 }
2941 /**
2942  * Sets the layout which is associated with the receiver to be
2943  * the argument which may be null.
2944  * <p>
2945  * Note: No Layout can be set on this Control because it already
2946  * manages the size and position of its children.
2947  * </p>
2948  *
2949  * @param layout the receiver's new layout or null
2950  *
2951  * @exception SWTException <ul>
2952  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2953  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2954  * </ul>
2955  */
2956 @Override
2957 public void setLayout (Layout layout) {
2958         checkWidget();
2959         return;
2960 }
2961 /**
2962  * Sets the maximized state of the receiver.
2963  *
2964  * @param maximize the new maximized state
2965  *
2966  * @exception SWTException <ul>
2967  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2968  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2969  * </ul>
2970  *
2971  * @since 3.0
2972  */
2973 public void setMaximized(boolean maximize) {
2974         checkWidget ();
2975         if (this.maximized == maximize) return;
2976         if (maximize && this.minimized) setMinimized(false);
2977         this.maximized = maximize;
2978         if (minMaxTb != null && maxItem != null) {
2979                 if (maxImage != null) maxImage.dispose();
2980                 maxImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MAX_BUTTON);
2981                 maxItem.setImage(maxImage);
2982                 maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
2983         }
2984 }
2985 /**
2986  * Marks the receiver's minimize button as visible if the argument is <code>true</code>,
2987  * and marks it invisible otherwise.
2988  *
2989  * @param visible the new visibility state
2990  *
2991  * @exception SWTException <ul>
2992  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2993  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2994  * </ul>
2995  *
2996  * @since 3.0
2997  */
2998 public void setMinimizeVisible(boolean visible) {
2999         checkWidget();
3000         if (showMin == visible) return;
3001         // display minimize button
3002         showMin = visible;
3003         updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3004 }
3005 /**
3006  * Sets the minimized state of the receiver.
3007  *
3008  * @param minimize the new minimized state
3009  *
3010  * @exception SWTException <ul>
3011  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3012  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3013  * </ul>
3014  *
3015  * @since 3.0
3016  */
3017 public void setMinimized(boolean minimize) {
3018         checkWidget ();
3019         if (this.minimized == minimize) return;
3020         if (minimize && this.maximized) setMaximized(false);
3021         this.minimized = minimize;
3022         if (minMaxTb != null && minItem != null) {
3023                 if (minImage != null) minImage.dispose();
3024                 minImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MIN_BUTTON);
3025                 minItem.setImage(minImage);
3026                 minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
3027         }
3028 }
3029
3030 /**
3031  * Sets the minimum number of characters that will
3032  * be displayed in a fully compressed tab.
3033  *
3034  * @param count the minimum number of characters that will be displayed in a fully compressed tab
3035  *
3036  * @exception SWTException <ul>
3037  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3038  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3039  *    <li>ERROR_INVALID_RANGE - if the count is less than zero</li>
3040  * </ul>
3041  *
3042  * @since 3.0
3043  */
3044 public void setMinimumCharacters(int count) {
3045         checkWidget ();
3046         if (count < 0) SWT.error(SWT.ERROR_INVALID_RANGE);
3047         if (minChars == count) return;
3048         minChars = count;
3049         updateFolder(REDRAW_TABS);
3050 }
3051
3052 /**
3053  * When there is not enough horizontal space to show all the tabs,
3054  * by default, tabs are shown sequentially from left to right in
3055  * order of their index.  When the MRU visibility is turned on,
3056  * the tabs that are visible will be the tabs most recently selected.
3057  * Tabs will still maintain their left to right order based on index
3058  * but only the most recently selected tabs are visible.
3059  * <p>
3060  * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
3061  * "Tab 3" and "Tab 4" (in order by index).  The user selects
3062  * "Tab 1" and then "Tab 3".  If the CTabFolder is now
3063  * compressed so that only two tabs are visible, by default,
3064  * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
3065  * selected and "Tab 2" because it is the previous item in index order).
3066  * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
3067  * and "Tab 3" (in that order from left to right).</p>
3068  *
3069  * @param show the new visibility state
3070  *
3071  * @exception SWTException <ul>
3072  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3073  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3074  * </ul>
3075  *
3076  * @since 3.1
3077  */
3078 public void setMRUVisible(boolean show) {
3079         checkWidget();
3080         if (mru == show) return;
3081         mru = show;
3082         if (!mru) {
3083                 if (firstIndex == -1) return;
3084                 int idx = firstIndex;
3085                 int next = 0;
3086                 for (int i = firstIndex; i < items.length; i++) {
3087                         priority[next++] = i;
3088                 }
3089                 for (int i = 0; i < idx; i++) {
3090                         priority[next++] = i;
3091                 }
3092                 updateFolder(REDRAW_TABS);
3093         }
3094 }
3095 /**
3096  * Sets the renderer which is associated with the receiver to be
3097  * the argument which may be null. In the case of null, the default
3098  * renderer is used.
3099  *
3100  * @param renderer a new renderer
3101  *
3102  * @exception SWTException <ul>
3103  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3104  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3105  *      </ul>
3106  *
3107  * @see CTabFolderRenderer
3108  *
3109  * @since 3.6
3110  */
3111 public void setRenderer(CTabFolderRenderer renderer) {
3112         checkWidget();
3113         if (this.renderer == renderer || (useDefaultRenderer && renderer == null)) return;
3114         if (this.renderer != null) this.renderer.dispose();
3115         useDefaultRenderer = renderer == null;
3116         if (useDefaultRenderer) renderer = new CTabFolderRenderer(this);
3117         this.renderer = renderer;
3118         updateFolder(REDRAW);
3119 }
3120 /**
3121  * Set the selection to the tab at the specified item.
3122  *
3123  * @param item the tab item to be selected
3124  *
3125  * @exception IllegalArgumentException <ul>
3126  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3127  * </ul>
3128  *
3129  * @exception SWTException <ul>
3130  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3131  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3132  * </ul>
3133  */
3134 public void setSelection(CTabItem item) {
3135         checkWidget();
3136         if (item == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3137         int index = indexOf(item);
3138         setSelection(index);
3139 }
3140 /**
3141  * Set the selection to the tab at the specified index.
3142  *
3143  * @param index the index of the tab item to be selected
3144  *
3145  * @exception SWTException <ul>
3146  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3147  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3148  * </ul>
3149  */
3150 public void setSelection(int index) {
3151         checkWidget();
3152         if (index < 0 || index >= items.length) return;
3153         CTabItem selection = items[index];
3154         if (selectedIndex == index) {
3155                 showItem(selection);
3156                 return;
3157         }
3158
3159         int oldIndex = selectedIndex;
3160         selectedIndex = index;
3161         if (oldIndex != -1) {
3162                 items[oldIndex].closeImageState = SWT.BACKGROUND;
3163                 items[oldIndex].state &= ~SWT.SELECTED;
3164         }
3165         selection.closeImageState = SWT.NONE;
3166         selection.showing = false;
3167         selection.state |= SWT.SELECTED;
3168
3169         Control newControl = selection.control;
3170         Control oldControl = null;
3171         if (oldIndex != -1) {
3172                 oldControl = items[oldIndex].control;
3173         }
3174
3175         if (newControl != oldControl) {
3176                 if (newControl != null && !newControl.isDisposed()) {
3177                         newControl.setBounds(getClientArea());
3178                         newControl.setVisible(true);
3179                 }
3180                 if (oldControl != null && !oldControl.isDisposed()) {
3181                         oldControl.setVisible(false);
3182                 }
3183         }
3184         showItem(selection);
3185         redraw();
3186 }
3187 void setSelection(int index, boolean notify) {
3188         int oldSelectedIndex = selectedIndex;
3189         setSelection(index);
3190         if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
3191                 Event event = new Event();
3192                 event.item = getItem(selectedIndex);
3193                 notifyListeners(SWT.Selection, event);
3194         }
3195 }
3196 /**
3197  * Sets the receiver's selection background color to the color specified
3198  * by the argument, or to the default system color for the control
3199  * if the argument is null.
3200  *
3201  * @param color the new color (or null)
3202  *
3203  * @exception IllegalArgumentException <ul>
3204  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
3205  * </ul>
3206  * @exception SWTException <ul>
3207  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3208  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3209  * </ul>
3210  *
3211  * @since 3.0
3212  */
3213 public void setSelectionBackground (Color color) {
3214         if (inDispose) return;
3215         checkWidget();
3216         setSelectionHighlightGradientColor(null);
3217         if (selectionBackground == color) return;
3218         if (color == null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND);
3219         selectionBackground = color;
3220         renderer.createAntialiasColors(); //TODO:  need better caching strategy
3221         if (selectedIndex > -1) redraw();
3222 }
3223 /**
3224  * Specify a gradient of colours to be draw in the background of the selected tab.
3225  * For example to draw a gradient that varies from dark blue to blue and then to
3226  * white, use the following call to setBackground:
3227  * <pre>
3228  *      cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3229  *                                         display.getSystemColor(SWT.COLOR_BLUE),
3230  *                                         display.getSystemColor(SWT.COLOR_WHITE),
3231  *                                         display.getSystemColor(SWT.COLOR_WHITE)},
3232  *                             new int[] {25, 50, 100});
3233  * </pre>
3234  *
3235  * @param colors an array of Color that specifies the colors to appear in the gradient
3236  *               in order of appearance left to right.  The value <code>null</code> clears the
3237  *               background gradient. The value <code>null</code> can be used inside the array of
3238  *               Color to specify the background color.
3239  * @param percents an array of integers between 0 and 100 specifying the percent of the width
3240  *                 of the widget at which the color should change.  The size of the percents array must be one
3241  *                 less than the size of the colors array.
3242  *
3243  * @exception SWTException <ul>
3244  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3245  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3246  *      </ul>
3247  */
3248 public void setSelectionBackground(Color[] colors, int[] percents) {
3249         setSelectionBackground(colors, percents, false);
3250 }
3251 /**
3252  * Specify a gradient of colours to be draw in the background of the selected tab.
3253  * For example to draw a vertical gradient that varies from dark blue to blue and then to
3254  * white, use the following call to setBackground:
3255  * <pre>
3256  *      cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3257  *                                         display.getSystemColor(SWT.COLOR_BLUE),
3258  *                                         display.getSystemColor(SWT.COLOR_WHITE),
3259  *                                         display.getSystemColor(SWT.COLOR_WHITE)},
3260  *                                new int[] {25, 50, 100}, true);
3261  * </pre>
3262  *
3263  * @param colors an array of Color that specifies the colors to appear in the gradient
3264  *               in order of appearance left to right.  The value <code>null</code> clears the
3265  *               background gradient. The value <code>null</code> can be used inside the array of
3266  *               Color to specify the background color.
3267  * @param percents an array of integers between 0 and 100 specifying the percent of the width
3268  *                 of the widget at which the color should change.  The size of the percents array must be one
3269  *                 less than the size of the colors array.
3270  *
3271  * @param vertical indicate the direction of the gradient.  True is vertical and false is horizontal.
3272  *
3273  * @exception SWTException <ul>
3274  *              <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3275  *              <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3276  *      </ul>
3277  *
3278  * @since 3.0
3279  */
3280 public void setSelectionBackground(Color[] colors, int[] percents, boolean vertical) {
3281         checkWidget();
3282         int colorsLength;
3283         Color highlightBeginColor = null;  //null == no highlight
3284
3285         if (colors != null) {
3286                 //The colors array can optionally have an extra entry which describes the highlight top color
3287                 //Thus its either one or two larger than the percents array
3288                 if (percents == null ||
3289                                 ! ((percents.length == colors.length - 1) || (percents.length == colors.length - 2))){
3290                         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3291                 }
3292                 for (int i = 0; i < percents.length; i++) {
3293                         if (percents[i] < 0 || percents[i] > 100) {
3294                                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3295                         }
3296                         if (i > 0 && percents[i] < percents[i-1]) {
3297                                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3298                         }
3299                 }
3300                 //If the colors is exactly two more than percents then last is highlight
3301                 //Keep track of *real* colorsLength (minus the highlight)
3302                 if(percents.length == colors.length - 2) {
3303                         highlightBeginColor = colors[colors.length - 1];
3304                         colorsLength = colors.length - 1;
3305                 } else {
3306                         colorsLength = colors.length;
3307                 }
3308                 if (getDisplay().getDepth() < 15) {
3309                         // Don't use gradients on low color displays
3310                         colors = new Color[] {colors[colorsLength - 1]};
3311                         colorsLength = colors.length;
3312                         percents = new int[] {};
3313                 }
3314         } else {
3315                 colorsLength = 0;
3316         }
3317
3318         // Are these settings the same as before?
3319         if (selectionBgImage == null) {
3320                 if ((selectionGradientColors != null) && (colors != null) &&
3321                         (selectionGradientColors.length == colorsLength)) {
3322                         boolean same = false;
3323                         for (int i = 0; i < selectionGradientColors.length; i++) {
3324                                 if (selectionGradientColors[i] == null) {
3325                                         same = colors[i] == null;
3326                                 } else {
3327                                         same = selectionGradientColors[i].equals(colors[i]);
3328                                 }
3329                                 if (!same) break;
3330                         }
3331                         if (same) {
3332                                 for (int i = 0; i < selectionGradientPercents.length; i++) {
3333                                         same = selectionGradientPercents[i] == percents[i];
3334                                         if (!same) break;
3335                                 }
3336                         }
3337                         if (same && this.selectionGradientVertical == vertical) return;
3338                 }
3339         } else {
3340                 selectionBgImage = null;
3341         }
3342         // Store the new settings
3343         if (colors == null) {
3344                 selectionGradientColors = null;
3345                 selectionGradientPercents = null;
3346                 selectionGradientVertical = false;
3347                 setSelectionBackground((Color)null);
3348                 setSelectionHighlightGradientColor(null);
3349         } else {
3350                 selectionGradientColors = new Color[colorsLength];
3351                 for (int i = 0; i < colorsLength; ++i) {
3352                         selectionGradientColors[i] = colors[i];
3353                 }
3354                 selectionGradientPercents = new int[percents.length];
3355                 for (int i = 0; i < percents.length; ++i) {
3356                         selectionGradientPercents[i] = percents[i];
3357                 }
3358                 selectionGradientVertical = vertical;
3359                 setSelectionBackground(selectionGradientColors[selectionGradientColors.length-1]);
3360                 setSelectionHighlightGradientColor(highlightBeginColor);
3361         }
3362
3363         // Refresh with the new settings
3364         if (selectedIndex > -1) redraw();
3365 }
3366
3367 /*
3368  * Set the color for the highlight start for selected tabs.
3369  * Update the cache of highlight gradient colors if required.
3370  */
3371 void setSelectionHighlightGradientColor(Color start) {
3372         if (inDispose) return;
3373         renderer.setSelectionHighlightGradientColor(start);  //TODO: need better caching strategy
3374 }
3375
3376 /**
3377  * Set the image to be drawn in the background of the selected tab.  Image
3378  * is stretched or compressed to cover entire selection tab area.
3379  *
3380  * @param image the image to be drawn in the background
3381  *
3382  * @exception SWTException <ul>
3383  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3384  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3385  * </ul>
3386  */
3387 public void setSelectionBackground(Image image) {
3388         checkWidget();
3389         setSelectionHighlightGradientColor(null);
3390         if (image == selectionBgImage) return;
3391         if (image != null) {
3392                 selectionGradientColors = null;
3393                 selectionGradientPercents = null;
3394                 renderer.disposeSelectionHighlightGradientColors(); //TODO: need better caching strategy
3395         }
3396         selectionBgImage = image;
3397         renderer.createAntialiasColors(); //TODO:  need better caching strategy
3398         if (selectedIndex > -1) redraw();
3399 }
3400 /**
3401  * Set the foreground color of the selected tab.
3402  *
3403  * @param color the color of the text displayed in the selected tab
3404  *
3405  * @exception SWTException <ul>
3406  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3407  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3408  * </ul>
3409  */
3410 public void setSelectionForeground (Color color) {
3411         checkWidget();
3412         if (selectionForeground == color) return;
3413         if (color == null) color = getDisplay().getSystemColor(SELECTION_FOREGROUND);
3414         selectionForeground = color;
3415         if (selectedIndex > -1) redraw();
3416 }
3417
3418 /**
3419  * Sets the shape that the CTabFolder will use to render itself.
3420  *
3421  * @param simple <code>true</code> if the CTabFolder should render itself in a simple, traditional style
3422  *
3423  * @exception SWTException <ul>
3424  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3425  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3426  * </ul>
3427  *
3428  * @since 3.0
3429  */
3430 public void setSimple(boolean simple) {
3431         checkWidget();
3432         if (this.simple != simple) {
3433                 this.simple = simple;
3434                 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3435         }
3436 }
3437 /**
3438  * Sets the number of tabs that the CTabFolder should display
3439  *
3440  * @param single <code>true</code> if only the selected tab should be displayed otherwise, multiple tabs will be shown.
3441  *
3442  * @exception SWTException <ul>
3443  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3444  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3445  * </ul>
3446  *
3447  * @since 3.0
3448  */
3449 public void setSingle(boolean single) {
3450         checkWidget();
3451         if (this.single != single) {
3452                 this.single = single;
3453                 if (!single) {
3454                         for (int i = 0; i < items.length; i++) {
3455                                 if (i != selectedIndex && items[i].closeImageState == SWT.NONE) {
3456                                         items[i].closeImageState = SWT.BACKGROUND;
3457                                 }
3458                         }
3459                 }
3460                 updateFolder(REDRAW);
3461         }
3462 }
3463
3464 int getControlY(Point size, Rectangle[] rects, int borderBottom, int borderTop, int i) {
3465         int center = fixedTabHeight != SWT.DEFAULT ? 0 : (tabHeight - rects[i].height)/2;
3466         return onBottom ? size.y - borderBottom - tabHeight + center : 1 + borderTop + center;
3467 }
3468
3469 /**
3470  * Specify a fixed height for the tab items.  If no height is specified,
3471  * the default height is the height of the text or the image, whichever
3472  * is greater. Specifying a height of -1 will revert to the default height.
3473  *
3474  * @param height the point value of the height or -1
3475  *
3476  * @exception SWTException <ul>
3477  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3478  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3479  *    <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
3480  * </ul>
3481  */
3482 public void setTabHeight(int height) {
3483         checkWidget();
3484         if (height < -1) {
3485                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3486         }
3487         fixedTabHeight = height;
3488         updateFolder(UPDATE_TAB_HEIGHT);
3489 }
3490 /**
3491  * Specify whether the tabs should appear along the top of the folder
3492  * or along the bottom of the folder.
3493  *
3494  * @param position <code>SWT.TOP</code> for tabs along the top or <code>SWT.BOTTOM</code> for tabs along the bottom
3495  *
3496  * @exception SWTException <ul>
3497  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3498  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3499  *    <li>ERROR_INVALID_ARGUMENT - if the position value is not either SWT.TOP or SWT.BOTTOM</li>
3500  * </ul>
3501  *
3502  * @since 3.0
3503  */
3504 public void setTabPosition(int position) {
3505         checkWidget();
3506         if (position != SWT.TOP && position != SWT.BOTTOM) {
3507                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3508         }
3509         if (onBottom != (position == SWT.BOTTOM)) {
3510                 onBottom = position == SWT.BOTTOM;
3511                 updateFolder(REDRAW);
3512         }
3513 }
3514 /**
3515  * Set the control that appears in the top right corner of the tab folder.
3516  * Typically this is a close button or a composite with a Menu and close button.
3517  * The topRight control is optional.  Setting the top right control to null will
3518  * remove it from the tab folder.
3519  *
3520  * @param control the control to be displayed in the top right corner or null
3521  *
3522  * @exception SWTException <ul>
3523  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3524  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3525  *    <li>ERROR_INVALID_ARGUMENT - if the control is disposed, or not a child of this CTabFolder</li>
3526  * </ul>
3527  *
3528  * @since 2.1
3529  */
3530 public void setTopRight(Control control) {
3531         setTopRight(control, SWT.RIGHT);
3532 }
3533 /**
3534  * Set the control that appears in the top right corner of the tab folder.
3535  * Typically this is a close button or a composite with a Menu and close button.
3536  * The topRight control is optional.  Setting the top right control to null
3537  * will remove it from the tab folder.
3538  * <p>
3539  * The alignment parameter sets the layout of the control in the tab area.
3540  * <code>SWT.RIGHT</code> will cause the control to be positioned on the far
3541  * right of the folder and it will have its default size.  <code>SWT.FILL</code>
3542  * will size the control to fill all the available space to the right of the
3543  * last tab.  If there is no available space, the control will not be visible.
3544  * <code>SWT.RIGHT | SWT.WRAP</code> will allow the control to wrap below the
3545  * tabs if there is not enough available space to the right of the last tab.
3546  * </p>
3547  *
3548  * @param control the control to be displayed in the top right corner or null
3549  * @param alignment <code>SWT.RIGHT</code> or <code>SWT.FILL</code> or <code>SWT.RIGHT | SWT.WRAP</code>
3550  *
3551  * @exception SWTException <ul>
3552  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3553  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3554  *    <li>ERROR_INVALID_ARGUMENT - if the control is disposed, or not a child of this CTabFolder</li>
3555  * </ul>
3556  *
3557  * @since 3.0
3558  */
3559 public void setTopRight(Control control, int alignment) {
3560         checkWidget();
3561         if (alignment != SWT.RIGHT && alignment != SWT.FILL && alignment != (SWT.RIGHT | SWT.WRAP)) {
3562                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3563         }
3564         if (control != null && (control.isDisposed() || control.getParent() != this)) {
3565                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3566         }
3567         if (topRight == control && topRightAlignment == alignment) return;
3568         if (topRight != null && !topRight.isDisposed()) removeTabControl(topRight, false);
3569         topRight = control;
3570         topRightAlignment = alignment;
3571         alignment &= ~SWT.RIGHT;
3572         if (control != null) addTabControl(control, SWT.TRAIL | alignment, -1, false);
3573         updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3574 }
3575
3576
3577 /**
3578  * Specify whether the close button appears
3579  * when the user hovers over an unselected tabs.
3580  *
3581  * @param visible <code>true</code> makes the close button appear
3582  *
3583  * @exception SWTException <ul>
3584  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3585  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3586  * </ul>
3587  *
3588  * @since 3.0
3589  */
3590 public void setUnselectedCloseVisible(boolean visible) {
3591         checkWidget();
3592         if (showUnselectedClose == visible) return;
3593         // display close button when mouse hovers
3594         showUnselectedClose = visible;
3595         updateFolder(REDRAW);
3596 }
3597 /**
3598  * Specify whether the image appears on unselected tabs.
3599  *
3600  * @param visible <code>true</code> makes the image appear
3601  *
3602  * @exception SWTException <ul>
3603  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3604  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3605  * </ul>
3606  *
3607  * @since 3.0
3608  */
3609 public void setUnselectedImageVisible(boolean visible) {
3610         checkWidget();
3611         if (showUnselectedImage == visible) return;
3612         // display image on unselected items
3613         showUnselectedImage = visible;
3614         updateFolder(REDRAW);
3615 }
3616 /**
3617  * Shows the item.  If the item is already showing in the receiver,
3618  * this method simply returns.  Otherwise, the items are scrolled until
3619  * the item is visible.
3620  *
3621  * @param item the item to be shown
3622  *
3623  * @exception IllegalArgumentException <ul>
3624  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3625  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
3626  * </ul>
3627  * @exception SWTException <ul>
3628  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3629  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3630  * </ul>
3631  *
3632  * @see CTabFolder#showSelection()
3633  *
3634  * @since 2.0
3635  */
3636 public void showItem (CTabItem item) {
3637         checkWidget();
3638         if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
3639         if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3640         int index = indexOf(item);
3641         if (index == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3642         int idx = -1;
3643         for (int i = 0; i < priority.length; i++) {
3644                 if (priority[i] == index) {
3645                         idx = i;
3646                         break;
3647                 }
3648         }
3649         if (mru) {
3650                 // move to front of mru order
3651                 int[] newPriority = new int[priority.length];
3652                 System.arraycopy(priority, 0, newPriority, 1, idx);
3653                 System.arraycopy(priority, idx+1, newPriority, idx+1, priority.length - idx - 1);
3654                 newPriority[0] = index;
3655                 priority = newPriority;
3656         }
3657         if (item.showing) return;
3658         updateFolder(REDRAW_TABS);
3659 }
3660 void showList (Rectangle rect) {
3661         if (items.length == 0 || !showChevron) return;
3662         if (showMenu == null || showMenu.isDisposed()) {
3663                 showMenu = new Menu(getShell(), getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT));
3664         } else {
3665                 MenuItem[] items = showMenu.getItems();
3666                 for (int i = 0; i < items.length; i++) {
3667                         items[i].dispose();
3668                 }
3669         }
3670         final String id = "CTabFolder_showList_Index"; //$NON-NLS-1$
3671         for (int i = 0; i < items.length; i++) {
3672                 CTabItem tab = items[i];
3673                 if (tab.showing) continue;
3674                 MenuItem item = new MenuItem(showMenu, SWT.NONE);
3675                 // 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.
3676                 item.setText(tab.getText().replace("\n", " "));
3677                 item.setImage(tab.getImage());
3678                 item.setData(id, tab);
3679                 item.addSelectionListener(new SelectionAdapter() {
3680                         @Override
3681                         public void widgetSelected(SelectionEvent e) {
3682                                 MenuItem menuItem = (MenuItem)e.widget;
3683                                 int index = indexOf((CTabItem)menuItem.getData(id));
3684                                 CTabFolder.this.setSelection(index, true);
3685                         }
3686                 });
3687         }
3688         int x = rect.x;
3689         int y = rect.y + rect.height;
3690         Point location = getDisplay().map(this, null, x, y);
3691         showMenu.setLocation(location.x, location.y);
3692         showMenu.setVisible(true);
3693 }
3694 /**
3695  * Shows the selection.  If the selection is already showing in the receiver,
3696  * this method simply returns.  Otherwise, the items are scrolled until
3697  * the selection is visible.
3698  *
3699  * @exception SWTException <ul>
3700  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3701  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3702  * </ul>
3703  *
3704  * @see CTabFolder#showItem(CTabItem)
3705  *
3706  * @since 2.0
3707  */
3708 public void showSelection () {
3709         checkWidget ();
3710         if (selectedIndex != -1) {
3711                 showItem(getSelection());
3712         }
3713 }
3714
3715 void _setToolTipText (int x, int y) {
3716         String oldTip = getToolTipText();
3717         String newTip = _getToolTip(x, y);
3718         if (newTip == null || !newTip.equals(oldTip)) {
3719                 setToolTipText(newTip);
3720         }
3721 }
3722
3723 boolean updateItems() {
3724         return updateItems(selectedIndex);
3725 }
3726
3727 boolean updateItems (int showIndex) {
3728         GC gc = new GC(this);
3729         if (!single && !mru && showIndex != -1) {
3730                 // make sure selected item will be showing
3731                 int firstIndex = showIndex;
3732                 if (priority[0] < showIndex) {
3733                         int maxWidth = getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
3734                         int width = 0;
3735                         int[] widths = new int[items.length];
3736                         for (int i = priority[0]; i <= showIndex; i++) {
3737                                 int state = CTabFolderRenderer.MINIMUM_SIZE;
3738                                 if (i == selectedIndex) state |= SWT.SELECTED;
3739                                 widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3740                                 width += widths[i];
3741                                 if (width > maxWidth) break;
3742                         }
3743                         if (width > maxWidth) {
3744                                 width = 0;
3745                                 for (int i = showIndex; i >= 0; i--) {
3746                                         int state = CTabFolderRenderer.MINIMUM_SIZE;
3747                                         if (i == selectedIndex) state |= SWT.SELECTED;
3748                                         if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3749                                         width += widths[i];
3750                                         if (width > maxWidth) break;
3751                                         firstIndex = i;
3752                                 }
3753                         } else {
3754                                 firstIndex = priority[0];
3755                                 for (int i = showIndex + 1; i < items.length; i++) {
3756                                         int state = CTabFolderRenderer.MINIMUM_SIZE;
3757                                         if (i == selectedIndex) state |= SWT.SELECTED;
3758                                         widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3759                                         width += widths[i];
3760                                         if (width >= maxWidth) break;
3761                                 }
3762                                 if (width < maxWidth) {
3763                                         for (int i = priority[0] - 1; i >= 0; i--) {
3764                                                 int state = CTabFolderRenderer.MINIMUM_SIZE;
3765                                                 if (i == selectedIndex) state |= SWT.SELECTED;
3766                                                 if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3767                                                 width += widths[i];
3768                                                 if (width > maxWidth) break;
3769                                                 firstIndex = i;
3770                                         }
3771                                 }
3772                         }
3773
3774                 }
3775                 if (firstIndex != priority[0]) {
3776                         int index = 0;
3777                         // enumerate tabs from first visible to the last existing one (sorted ascending)
3778                         for (int i = firstIndex; i < items.length; i++) {
3779                                 priority[index++] = i;
3780                         }
3781                         // enumerate hidden tabs on the left hand from first visible one
3782                         // in the inverse order (sorted descending) so that the originally
3783                         // first opened tab is always at the end of the list
3784                         for (int i = firstIndex - 1; i >= 0; i--) {
3785                                 priority[index++] = i;
3786                         }
3787                 }
3788         }
3789
3790         boolean oldShowChevron = showChevron;
3791         boolean changed = setItemSize(gc);
3792         changed |= setItemLocation(gc);
3793         setButtonBounds(gc);
3794         changed |= showChevron != oldShowChevron;
3795         if (changed && getToolTipText() != null) {
3796                 Point pt = getDisplay().getCursorLocation();
3797                 pt = toControl(pt);
3798                 _setToolTipText(pt.x, pt.y);
3799         }
3800         gc.dispose();
3801         return changed;
3802 }
3803 boolean updateTabHeight(boolean force){
3804         int oldHeight = tabHeight;
3805         GC gc = new GC(this);
3806         tabHeight = renderer.computeSize(CTabFolderRenderer.PART_HEADER, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT).y;
3807         gc.dispose();
3808         if (fixedTabHeight == SWT.DEFAULT && controls != null && controls.length > 0) {
3809                 for (int i = 0; i < controls.length; i++) {
3810                         if ((controlAlignments[i] & SWT.WRAP) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
3811                                 int topHeight = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
3812                                 topHeight +=  renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0,0,0,0).height + 1;
3813                                 tabHeight = Math.max(topHeight, tabHeight);
3814                         }
3815                 }
3816         }
3817         if (!force && tabHeight == oldHeight) return false;
3818         oldSize = null;
3819         return true;
3820 }
3821
3822 void updateFolder (int flags) {
3823         updateFlags |= flags;
3824         if (updateRun != null) return;
3825         updateRun = () -> {
3826                 updateRun = null;
3827                 if (isDisposed()) return;
3828                 runUpdate();
3829         };
3830         getDisplay().asyncExec(updateRun);
3831 }
3832
3833 void runUpdate() {
3834         if (updateFlags == 0) return;
3835         int flags = updateFlags;
3836         updateFlags = 0;
3837         Rectangle rectBefore = getClientArea();
3838         updateTabHeight(false);
3839         updateItems(selectedIndex);
3840         if ((flags & REDRAW) != 0) {
3841                 redraw();
3842         } else if ((flags & REDRAW_TABS) != 0) {
3843                 redrawTabs();
3844         }
3845         Rectangle rectAfter = getClientArea();
3846         if (!rectBefore.equals(rectAfter)) {
3847                 notifyListeners(SWT.Resize, new Event());
3848                 layout();
3849         }
3850 }
3851
3852 void updateBkImages() {
3853         if (controls != null && controls.length > 0) {
3854                 for (int i = 0; i < controls.length; i++) {
3855                         Control control = controls[i];
3856                         if (!control.isDisposed()) {
3857                                 if (hovering) {
3858                                         if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
3859                                         control.setBackgroundImage(null);
3860                                         control.setBackground(getBackground());
3861                                 } else {
3862                                         if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_DEFAULT);
3863                                         Rectangle bounds = control.getBounds();
3864                                         int tabHeight = getTabHeight();
3865                                         int height = this.getSize().y;
3866                                         boolean wrapped = onBottom ? bounds.y + bounds.height < height - tabHeight : bounds.y > tabHeight;
3867                                         if (wrapped || gradientColors == null) {
3868                                                 control.setBackgroundImage(null);
3869                                                 control.setBackground(getBackground());
3870                                         } else {
3871                                                 bounds.width = 10;
3872                                                 if (!onBottom) {
3873                                                         bounds.y = -bounds.y;
3874                                                         bounds.height -= 2*bounds.y - 1;
3875                                                 } else {
3876                                                         bounds.height += height - (bounds.y + bounds.height);
3877                                                         bounds.y = -1;
3878                                                 }
3879                                                 bounds.x = 0;
3880                                                 if (controlBkImages[i] != null) controlBkImages[i].dispose();
3881                                                 controlBkImages[i] = new Image(control.getDisplay(), bounds);
3882                                                 GC gc = new GC(controlBkImages[i]);
3883                                                 renderer.draw(CTabFolderRenderer.PART_BACKGROUND, 0, bounds, gc);
3884                                                 gc.dispose();
3885                                                 control.setBackground(null);
3886                                                 control.setBackgroundImage(controlBkImages[i]);
3887                                         }
3888                                 }
3889                         }
3890                 }
3891
3892         }
3893 }
3894 String _getToolTip(int x, int y) {
3895         CTabItem item = getItem(new Point (x, y));
3896         if (item == null) return null;
3897         if (!item.showing) return null;
3898         if ((showClose || item.showClose) && item.closeRect.contains(x, y)) {
3899                 return SWT.getMessage("SWT_Close"); //$NON-NLS-1$
3900         }
3901         return item.getToolTipText();
3902 }
3903 /**
3904 * Set a control that can appear to the left or to the right of the folder tabs.
3905 * This method can also be used instead of #setTopRight(Control). To remove a tab
3906 * control, see#removeTabControl(Control);
3907 * <p>
3908 * The flags parameter sets the layout of the control in the tab area.
3909 * <code>SWT.LEAD</code> will cause the control to be positioned on the left
3910 * of the tabs. <code>SWT.TRAIL</code> will cause the control to be positioned on
3911 * the far right of the folder and it will have its default size. <code>SWT.TRAIL</code>
3912 * can be combined with <code>SWT.FILL</code>to fill all the available space to the
3913 * right of the last tab. <code>SWT.WRAP</code> can also be added to <code>SWT.TRAIL</code>
3914 * only to cause a control to wrap if there is not enough space to display it in its
3915 * entirety.
3916 * </p>
3917 * @param control the control to be displayed in the top right corner or null
3918 *
3919 * @param flags valid combinations are:
3920 * <ul><li>SWT.LEAD
3921 * <li> SWT.TRAIL (| SWT.FILL | SWT.WRAP)
3922 * </ul>
3923 * @exception SWTException <ul>
3924 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3925 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3926 *    <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
3927 * </ul>
3928 */
3929 /*public*/ void addTabControl(Control control, int flags) {
3930         checkWidget();
3931         addTabControl(control, flags, -1, true);
3932 }
3933
3934 void addTabControl(Control control, int flags, int index, boolean update) {
3935         switch (flags) {
3936                 case SWT.TRAIL:
3937                 case SWT.TRAIL | SWT.WRAP:
3938                 case SWT.TRAIL | SWT.FILL:
3939                 case SWT.TRAIL | SWT.FILL | SWT.WRAP:
3940                 case SWT.LEAD:
3941                         break;
3942                 default:
3943                         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3944                         break;
3945         }
3946         if (control != null && control.getParent() != this) {
3947                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3948         }
3949         //check for duplicates
3950         for (int i = 0; i < controls.length; i++) {
3951                 if (controls[i] == control) {
3952                         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3953                 }
3954         }
3955         int length = controls.length;
3956
3957         control.addListener(SWT.Resize, listener);
3958
3959         //Grow all 4 arrays
3960         Control[] newControls = new Control [length + 1];
3961         System.arraycopy(controls, 0, newControls, 0, length);
3962         controls = newControls;
3963         int[] newAlignment = new int [length + 1];
3964         System.arraycopy(controlAlignments, 0, newAlignment, 0, length);
3965         controlAlignments = newAlignment;
3966         Rectangle[] newRect = new Rectangle [length + 1];
3967         System.arraycopy(controlRects, 0, newRect, 0, length);
3968         controlRects = newRect;
3969         Image[] newImage = new Image [length + 1];
3970         System.arraycopy(controlBkImages, 0, newImage, 0, length);
3971         controlBkImages = newImage;
3972         if (index == -1) {
3973                 index = length;
3974                 if (chevronTb != null && control != chevronTb) index--;
3975         }
3976         System.arraycopy (controls, index, controls, index + 1, length - index);
3977         System.arraycopy (controlAlignments, index, controlAlignments, index + 1, length - index);
3978         System.arraycopy (controlRects, index, controlRects, index + 1, length - index);
3979         System.arraycopy (controlBkImages, index, controlBkImages, index + 1, length - index);
3980         controls[index] = control;
3981         controlAlignments[index] = flags;
3982         controlRects[index] = new Rectangle(0, 0, 0, 0);
3983         if (update) {
3984                 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3985         }
3986 }
3987
3988 /**
3989 * Removes the control from the list of tab controls.
3990 *
3991 * @param control the control to be removed
3992 *
3993 * @exception SWTException <ul>
3994 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3995 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3996 *    <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
3997 * </ul>
3998 */
3999 /*public*/ void removeTabControl (Control control) {
4000         checkWidget();
4001         removeTabControl (control, true);
4002 }
4003
4004 void removeTabControl (Control control, boolean update) {
4005         if (control != null && control.getParent() != this) {
4006                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4007         }
4008         int index = -1;
4009         for (int i = 0; i < controls.length; i++) {
4010                 if (controls[i] == control){
4011                         index = i;
4012                         break;
4013                 }
4014         }
4015         if (index == -1) return;
4016
4017         if (!control.isDisposed()) {
4018                 control.removeListener(SWT.Resize, listener);
4019                 control.setBackground (null);
4020                 control.setBackgroundImage (null);
4021                 if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
4022         }
4023
4024         if (controlBkImages[index] != null && !controlBkImages[index].isDisposed()) controlBkImages[index].dispose();
4025         if (controls.length == 1) {
4026                 controls = new Control[0];
4027                 controlAlignments = new int[0];
4028                 controlRects = new Rectangle[0];
4029                 controlBkImages = new Image[0];
4030         } else {
4031                 Control[] newControls = new Control [controls.length - 1];
4032                 System.arraycopy(controls, 0, newControls, 0, index);
4033                 System.arraycopy(controls, index + 1, newControls, index, controls.length - index - 1);
4034                 controls = newControls;
4035
4036                 int[] newAlignments = new int [controls.length];
4037                 System.arraycopy(controlAlignments, 0, newAlignments, 0, index);
4038                 System.arraycopy(controlAlignments, index + 1, newAlignments, index, controls.length - index);
4039                 controlAlignments = newAlignments;
4040
4041                 Rectangle[] newRects = new Rectangle [controls.length];
4042                 System.arraycopy(controlRects, 0, newRects, 0, index);
4043                 System.arraycopy(controlRects, index + 1, newRects, index, controls.length - index);
4044                 controlRects = newRects;
4045
4046                 Image[] newBkImages = new Image [controls.length];
4047                 System.arraycopy(controlBkImages, 0, newBkImages, 0, index);
4048                 System.arraycopy(controlBkImages, index + 1, newBkImages, index, controls.length - index);
4049                 controlBkImages = newBkImages;
4050         }
4051         if (update) {
4052                 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4053         }
4054 }
4055
4056 int getWrappedHeight (Point size) {
4057         boolean[][] positions = new boolean[1][];
4058         Rectangle[] rects = computeControlBounds(size, positions);
4059         int minY = Integer.MAX_VALUE, maxY = 0, wrapHeight = 0;
4060         for (int i = 0; i < rects.length; i++) {
4061                 if (positions[0][i]) {
4062                         minY = Math.min(minY, rects[i].y);
4063                         maxY = Math.max(maxY, rects[i].y + rects[i].height);
4064                         wrapHeight = maxY - minY;
4065                 }
4066         }
4067         return wrapHeight;
4068 }
4069
4070 /**
4071  * Sets whether a chevron is shown when there are more items to be displayed.
4072  *
4073  * @exception IllegalArgumentException <ul>
4074  *    <li>ERROR_INVALID_RANGE - if the index is out of range</li>
4075  * </ul>
4076  * @exception SWTException <ul>
4077  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
4078  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
4079  * </ul>
4080  *
4081  */
4082 /*public*/ void setChevronVisible(boolean visible) {
4083         checkWidget();
4084         if (chevronVisible == visible) return;
4085         chevronVisible = visible;
4086         updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4087 }
4088
4089         boolean shouldHighlight() {
4090                 return this.highlight && highlightEnabled;
4091         }
4092
4093 /**
4094  * Sets whether the selected tab is rendered as highlighted.
4095  *
4096  * @param enabled
4097  *            {@code true} if the selected tab should be highlighted,
4098  *            {@code false} otherwise.
4099  * @exception SWTException
4100  *                <ul>
4101  *                <li>ERROR_WIDGET_DISPOSED - if the receiver has been
4102  *                disposed</li>
4103  *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
4104  *                thread that created the receiver</li>
4105  *                </ul>
4106  * @since 3.106
4107  */
4108 public void setHighlightEnabled(boolean enabled) {
4109         checkWidget();
4110         if (highlightEnabled == enabled) {
4111                 return;
4112         }
4113         highlightEnabled = enabled;
4114         updateFolder(REDRAW);
4115 }
4116
4117 /**
4118  * Returns <code>true</code> if the selected tab is rendered as
4119  * highlighted.
4120  *
4121  * @return <code>true</code> if the selected tab is rendered as
4122  *         highlighted
4123  *
4124  * @exception SWTException
4125  *                <ul>
4126  *                <li>ERROR_WIDGET_DISPOSED - if the receiver has been
4127  *                disposed</li>
4128  *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
4129  *                thread that created the receiver</li>
4130  *                </ul>
4131  * @since 3.106
4132  */
4133 public boolean getHighlightEnabled() {
4134         checkWidget();
4135         return highlightEnabled;
4136 }
4137 }