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