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 					location = items[childID].getBounds();
1467 				}
1468 				if (location != null) {
1469 					pt = toDisplay(location.x, location.y);
1470 				}
1471 			}
1472 			if (location != null && pt != null) {
1473 				e.x = pt.x;
1474 				e.y = pt.y;
1475 				e.width = location.width;
1476 				e.height = location.height;
1477 			}
1478 		}
1479 
1480 		@Override
1481 		public void getChildCount(AccessibleControlEvent e) {
1482 			e.detail = items.length;
1483 		}
1484 
1485 		@Override
1486 		public void getDefaultAction(AccessibleControlEvent e) {
1487 			String action = null;
1488 			int childID = e.childID;
1489 			if (childID >= 0 && childID < items.length) {
1490 				action = SWT.getMessage ("SWT_Switch"); //$NON-NLS-1$
1491 			}
1492 			e.result = action;
1493 		}
1494 
1495 		@Override
1496 		public void getFocus(AccessibleControlEvent e) {
1497 			int childID = ACC.CHILDID_NONE;
1498 			if (isFocusControl()) {
1499 				if (selectedIndex == -1) {
1500 					childID = ACC.CHILDID_SELF;
1501 				} else {
1502 					childID = selectedIndex;
1503 				}
1504 			}
1505 			e.childID = childID;
1506 		}
1507 
1508 		@Override
1509 		public void getRole(AccessibleControlEvent e) {
1510 			int role = 0;
1511 			int childID = e.childID;
1512 			if (childID == ACC.CHILDID_SELF) {
1513 				role = ACC.ROLE_TABFOLDER;
1514 			} else if (childID >= 0 && childID < items.length) {
1515 				role = ACC.ROLE_TABITEM;
1516 			}
1517 			e.detail = role;
1518 		}
1519 
1520 		@Override
1521 		public void getSelection(AccessibleControlEvent e) {
1522 			e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex;
1523 		}
1524 
1525 		@Override
1526 		public void getState(AccessibleControlEvent e) {
1527 			int state = 0;
1528 			int childID = e.childID;
1529 			if (childID == ACC.CHILDID_SELF) {
1530 				state = ACC.STATE_NORMAL;
1531 			} else if (childID >= 0 && childID < items.length) {
1532 				state = ACC.STATE_SELECTABLE;
1533 				if (isFocusControl()) {
1534 					state |= ACC.STATE_FOCUSABLE;
1535 				}
1536 				if (selectedIndex == childID) {
1537 					state |= ACC.STATE_SELECTED;
1538 					if (isFocusControl()) {
1539 						state |= ACC.STATE_FOCUSED;
1540 					}
1541 				}
1542 			}
1543 			e.detail = state;
1544 		}
1545 
1546 		@Override
1547 		public void getChildren(AccessibleControlEvent e) {
1548 			int childIdCount = items.length;
1549 			Object[] children = new Object[childIdCount];
1550 			for (int i = 0; i < childIdCount; i++) {
1551 				children[i] = Integer.valueOf(i);
1552 			}
1553 			e.children = children;
1554 		}
1555 	});
1556 
1557 	addListener(SWT.Selection, event -> {
1558 		if (isFocusControl()) {
1559 			if (selectedIndex == -1) {
1560 				accessible.setFocus(ACC.CHILDID_SELF);
1561 			} else {
1562 				accessible.setFocus(selectedIndex);
1563 			}
1564 		}
1565 	});
1566 
1567 	addListener(SWT.FocusIn, event -> {
1568 		if (selectedIndex == -1) {
1569 			accessible.setFocus(ACC.CHILDID_SELF);
1570 		} else {
1571 			accessible.setFocus(selectedIndex);
1572 		}
1573 	});
1574 }
initAccessibleMinMaxTb()1575 void initAccessibleMinMaxTb() {
1576 	minMaxTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
1577 		@Override
1578 		public void getName(AccessibleEvent e) {
1579 			if (e.childID != ACC.CHILDID_SELF) {
1580 				if (minItem != null && e.childID == minMaxTb.indexOf(minItem)) {
1581 					e.result = minItem.getToolTipText();
1582 				} else if (maxItem != null && e.childID == minMaxTb.indexOf(maxItem)) {
1583 					e.result = maxItem.getToolTipText();
1584 				}
1585 			}
1586 		}
1587 	});
1588 }
initAccessibleChevronTb()1589 void initAccessibleChevronTb() {
1590 	chevronTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
1591 		@Override
1592 		public void getName(AccessibleEvent e) {
1593 			if (e.childID != ACC.CHILDID_SELF) {
1594 				if (chevronItem != null && e.childID == chevronTb.indexOf(chevronItem)) {
1595 					e.result = chevronItem.getToolTipText();
1596 				}
1597 			}
1598 		}
1599 	});
1600 }
onKeyDown(Event event)1601 void onKeyDown (Event event) {
1602 	runUpdate();
1603 	switch (event.keyCode) {
1604 		case SWT.ARROW_LEFT:
1605 		case SWT.ARROW_RIGHT:
1606 			int count = items.length;
1607 			if (count == 0) return;
1608 			if (selectedIndex  == -1) return;
1609 			int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
1610 			int offset =  event.keyCode == leadKey ? -1 : 1;
1611 			int index;
1612 			if (!mru) {
1613 				index = selectedIndex + offset;
1614 			} else {
1615 				int[] visible = new int[items.length];
1616 				int idx = 0;
1617 				int current = -1;
1618 				for (int i = 0; i < items.length; i++) {
1619 					if (items[i].showing) {
1620 						if (i == selectedIndex) current = idx;
1621 						visible [idx++] = i;
1622 					}
1623 				}
1624 				if (current + offset >= 0 && current + offset < idx){
1625 					index = visible [current + offset];
1626 				} else {
1627 					if (showChevron) {
1628 						Rectangle chevronRect = chevronItem.getBounds();
1629 						chevronRect = event.display.map(chevronTb, this, chevronRect);
1630 						CTabFolderEvent e = new CTabFolderEvent(this);
1631 						e.widget = this;
1632 						e.time = event.time;
1633 						e.x = chevronRect.x;
1634 						e.y = chevronRect.y;
1635 						e.width = chevronRect.width;
1636 						e.height = chevronRect.height;
1637 						e.doit = true;
1638 						for (CTabFolder2Listener folderListener : folderListeners) {
1639 							folderListener.showList(e);
1640 						}
1641 						if (e.doit && !isDisposed()) {
1642 							showList(chevronRect);
1643 						}
1644 					}
1645 					return;
1646 				}
1647 			}
1648 			if (index < 0 || index >= count) return;
1649 			setSelection (index, true);
1650 			forceFocus();
1651 	}
1652 }
onDispose(Event event)1653 void onDispose(Event event) {
1654 	removeListener(SWT.Dispose, listener);
1655 	notifyListeners(SWT.Dispose, event);
1656 	event.type = SWT.None;
1657 	/*
1658 	 * Usually when an item is disposed, destroyItem will change the size of the items array,
1659 	 * reset the bounds of all the tabs and manage the widget associated with the tab.
1660 	 * Since the whole folder is being disposed, this is not necessary.  For speed
1661 	 * the inDispose flag is used to skip over this part of the item dispose.
1662 	 */
1663 	inDispose = true;
1664 
1665 	if (showMenu != null && !showMenu.isDisposed()) {
1666 		showMenu.dispose();
1667 		showMenu = null;
1668 	}
1669 	int length = items.length;
1670 	for (int i = 0; i < length; i++) {
1671 		if (items[i] != null) {
1672 			items[i].dispose();
1673 		}
1674 	}
1675 
1676 	gradientColors = null;
1677 
1678 	selectionGradientColors = null;
1679 	selectionGradientPercents = null;
1680 	selectionBgImage = null;
1681 
1682 	selectionBackground = null;
1683 	selectionForeground = null;
1684 
1685 	if (controlBkImages != null) {
1686 		for (int i = 0; i < controlBkImages.length; i++) {
1687 			if (controlBkImages[i] != null) {
1688 				controlBkImages[i].dispose();
1689 				controlBkImages[i] = null;
1690 			}
1691 		}
1692 		controlBkImages = null;
1693 	}
1694 	controls = null;
1695 	controlAlignments = null;
1696 	controlRects = null;
1697 
1698 	if (maxImage != null) maxImage.dispose();
1699 	maxImage = null;
1700 
1701 	if (minImage != null) minImage.dispose();
1702 	minImage = null;
1703 
1704 	if (chevronImage != null) chevronImage.dispose();
1705 	chevronImage = null;
1706 
1707 	if (renderer != null) renderer.dispose();
1708 	renderer = null;
1709 
1710 	minItem = null;
1711 	maxItem = null;
1712 	minMaxTb = null;
1713 
1714 	chevronItem = null;
1715 	chevronTb = null;
1716 
1717 	if (folderListeners.length != 0) folderListeners = new CTabFolder2Listener[0];
1718 	if (tabListeners.length != 0) tabListeners = new CTabFolderListener[0];
1719 }
onDragDetect(Event event)1720 void onDragDetect(Event event) {
1721 	boolean consume = false;
1722 	for (CTabItem item : items) {
1723 		if (item.closeRect.contains(event.x, event.y)) {
1724 				consume = true;
1725 				break;
1726 		}
1727 	}
1728 	if (consume) {
1729 		event.type = SWT.None;
1730 	}
1731 }
onFocus(Event event)1732 void onFocus(Event event) {
1733 	checkWidget();
1734 	if (selectedIndex >= 0) {
1735 		redraw();
1736 	} else {
1737 		setSelection(0, true);
1738 	}
1739 }
onMnemonic(Event event, boolean doit)1740 boolean onMnemonic (Event event, boolean doit) {
1741 	char key = event.character;
1742 	for (int i = 0; i < items.length; i++) {
1743 		if (items[i] != null) {
1744 			char mnemonic = _findMnemonic (items[i].getText ());
1745 			if (mnemonic != '\0') {
1746 				if (Character.toLowerCase (key) == mnemonic) {
1747 					if (doit) {
1748 						setSelection(i, true);
1749 						forceFocus();
1750 					}
1751 					return true;
1752 				}
1753 			}
1754 		}
1755 	}
1756 	return false;
1757 }
onMenuDetect(Event event)1758 void onMenuDetect(Event event) {
1759 	if (event.detail == SWT.MENU_KEYBOARD) {
1760 		if (selectedIndex != -1) {
1761 			CTabItem item = items[selectedIndex];
1762 			Rectangle rect = getDisplay().map(this, null, item.getBounds());
1763 			if (!rect.contains(event.x, event.y)) {
1764 				/* If the mouse is not in the currently-selected tab,
1765 				 * then pop up the menu near the top-right corner of the current tab.
1766 				 */
1767 				Rectangle itemTrim = renderer.computeTrim(selectedIndex, SWT.NONE, 0, 0, 0, 0);
1768 				Rectangle closeTrim = renderer.computeTrim(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, 0, 0, 0, 0);
1769 				event.x = rect.x + rect.width - item.closeRect.width + itemTrim.x - closeTrim.width;
1770 				event.y = rect.y - itemTrim.y - closeTrim.y;
1771 			}
1772 		}
1773 	}
1774 }
onMouseDoubleClick(Event event)1775 void onMouseDoubleClick(Event event) {
1776 	if (event.button != 1 ||
1777 		(event.stateMask & SWT.BUTTON2) != 0 ||
1778 		(event.stateMask & SWT.BUTTON3) != 0) return;
1779 	Event e = new Event();
1780 	e.item = getItem(new Point(event.x, event.y));
1781 	if (e.item != null) {
1782 		notifyListeners(SWT.DefaultSelection, e);
1783 	}
1784 }
onMouse(Event event)1785 void onMouse(Event event) {
1786 	if( isDisposed() ) {
1787 		return;
1788 	}
1789 	int x = event.x, y = event.y;
1790 	switch (event.type) {
1791 		case SWT.MouseEnter: {
1792 			setToolTipText(null);
1793 			break;
1794 		}
1795 		case SWT.MouseExit: {
1796 			for (int i=0; i<items.length; i++) {
1797 				CTabItem item = items[i];
1798 				if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND) {
1799 					item.closeImageState = SWT.BACKGROUND;
1800 					redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1801 				}
1802 				if ((item.state & SWT.HOT) != 0) {
1803 					item.state &= ~SWT.HOT;
1804 					redraw(item.x, item.y, item.width, item.height, false);
1805 				}
1806 				if (i == selectedIndex && item.closeImageState != SWT.NONE) {
1807 					item.closeImageState = SWT.NONE;
1808 					redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1809 				}
1810 			}
1811 			break;
1812 		}
1813 		case SWT.MouseHover:
1814 		case SWT.MouseDown: {
1815 			if (hoverTb && hoverRect.contains(x, y) && !hovering) {
1816 				hovering = true;
1817 				updateItems();
1818 				hoverTimerRunning = true;
1819 				event.display.timerExec(2000, new Runnable() {
1820 					@Override
1821 					public void run() {
1822 						if (isDisposed()) return;
1823 						if (hovering) {
1824 							Display display = getDisplay();
1825 							Control c = display.getCursorControl();
1826 							boolean reschedule = false;
1827 							if (c != null) {
1828 								for (Control control : controls) {
1829 									Control temp = c;
1830 									do {
1831 										if (temp.equals(control)) {
1832 											reschedule = true;
1833 										} else {
1834 											temp = temp.getParent();
1835 											if (temp == null || temp.equals(CTabFolder.this)) break;
1836 										}
1837 									} while (!reschedule);
1838 									if (reschedule) break;
1839 								}
1840 							}
1841 							if (reschedule && hoverTimerRunning) {
1842 								display.timerExec(2000, this);
1843 							} else {
1844 								hovering = false;
1845 								updateItems();
1846 							}
1847 						}
1848 					}
1849 				});
1850 				return;
1851 			}
1852 			if (event.button != 1) return;
1853 			CTabItem item = null;
1854 			if (single) {
1855 				if (selectedIndex != -1) {
1856 					Rectangle bounds = items[selectedIndex].getBounds();
1857 					if (bounds.contains(x, y)){
1858 						item = items[selectedIndex];
1859 					}
1860 				}
1861 			} else {
1862 				for (CTabItem tabItem : items) {
1863 					Rectangle bounds = tabItem.getBounds();
1864 					if (bounds.contains(x, y)){
1865 						item = tabItem;
1866 					}
1867 				}
1868 			}
1869 			if (item != null) {
1870 				if (item.closeRect.contains(x,y)){
1871 					item.closeImageState = SWT.SELECTED;
1872 					redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1873 					update();
1874 					return;
1875 				}
1876 				int index = indexOf(item);
1877 				if (item.showing){
1878 					int oldSelectedIndex = selectedIndex;
1879 					setSelection(index, true);
1880 					if (oldSelectedIndex == selectedIndex) {
1881 						/* If the click is on the selected tabitem, then set focus to the tabfolder */
1882 						forceFocus();
1883 					}
1884 				}
1885 				return;
1886 			}
1887 			break;
1888 		}
1889 		case SWT.MouseMove: {
1890 			_setToolTipText(event.x, event.y);
1891 			boolean close = false;
1892 			for (int i=0; i<items.length; i++) {
1893 				CTabItem item = items[i];
1894 				close = false;
1895 				if (item.getBounds().contains(x, y)) {
1896 					close = true;
1897 					if (item.closeRect.contains(x, y)) {
1898 						if (item.closeImageState != SWT.SELECTED && item.closeImageState != SWT.HOT) {
1899 							item.closeImageState = SWT.HOT;
1900 							redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1901 						}
1902 					} else {
1903 						if (item.closeImageState != SWT.NONE) {
1904 							item.closeImageState = SWT.NONE;
1905 							redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1906 						}
1907 					}
1908 					if ((item.state & SWT.HOT) == 0) {
1909 						item.state |= SWT.HOT;
1910 						redraw(item.x, item.y, item.width, item.height, false);
1911 					}
1912 				}
1913 				if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND && !close) {
1914 					item.closeImageState = SWT.BACKGROUND;
1915 					redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1916 				}
1917 				if ((item.state & SWT.HOT) != 0 && !close) {
1918 					item.state &= ~SWT.HOT;
1919 					redraw(item.x, item.y, item.width, item.height, false);
1920 				}
1921 				if (i == selectedIndex && item.closeImageState != SWT.NONE && !close) {
1922 					item.closeImageState = SWT.NONE;
1923 					redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1924 				}
1925 			}
1926 			break;
1927 		}
1928 		case SWT.MouseUp: {
1929 			if (event.button != 1) return;
1930 			CTabItem item = null;
1931 			if (single) {
1932 				if (selectedIndex != -1) {
1933 					Rectangle bounds = items[selectedIndex].getBounds();
1934 					if (bounds.contains(x, y)){
1935 						item = items[selectedIndex];
1936 					}
1937 				}
1938 			} else {
1939 				for (CTabItem tabItem : items) {
1940 					Rectangle bounds = tabItem.getBounds();
1941 					if (bounds.contains(x, y)){
1942 						item = tabItem;
1943 					}
1944 				}
1945 			}
1946 			if (item != null) {
1947 				if (item.closeRect.contains(x,y)) {
1948 					boolean selected = item.closeImageState == SWT.SELECTED;
1949 					item.closeImageState = SWT.HOT;
1950 					redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1951 					if (!selected) return;
1952 					CTabFolderEvent e = new CTabFolderEvent(this);
1953 					e.widget = this;
1954 					e.time = event.time;
1955 					e.item = item;
1956 					e.doit = true;
1957 					for (CTabFolder2Listener listener : folderListeners) {
1958 						listener.close(e);
1959 					}
1960 					for (CTabFolderListener listener : tabListeners) {
1961 						listener.itemClosed(e);
1962 					}
1963 					if (e.doit) item.dispose();
1964 					if (!isDisposed() && item.isDisposed()) {
1965 						Display display = getDisplay();
1966 						Point pt = display.getCursorLocation();
1967 						pt = display.map(null, this, pt.x, pt.y);
1968 						CTabItem nextItem = getItem(pt);
1969 						if (nextItem != null) {
1970 							if (nextItem.closeRect.contains(pt)) {
1971 								if (nextItem.closeImageState != SWT.SELECTED && nextItem.closeImageState != SWT.HOT) {
1972 									nextItem.closeImageState = SWT.HOT;
1973 									redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
1974 								}
1975 							} else {
1976 								if (nextItem.closeImageState != SWT.NONE) {
1977 									nextItem.closeImageState = SWT.NONE;
1978 									redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
1979 								}
1980 							}
1981 						}
1982 					}
1983 					return;
1984 				}
1985 			}
1986 		}
1987 	}
1988 }
onPageTraversal(Event event)1989 void onPageTraversal(Event event) {
1990 	int count = items.length;
1991 	if (count == 0) return;
1992 	int index = selectedIndex;
1993 	if (index  == -1) {
1994 		index = 0;
1995 	} else {
1996 		int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
1997 		if (!mru) {
1998 			index = (selectedIndex + offset + count) % count;
1999 		} else {
2000 			int[] visible = new int[items.length];
2001 			int idx = 0;
2002 			int current = -1;
2003 			for (int i = 0; i < items.length; i++) {
2004 				if (items[i].showing) {
2005 					if (i == selectedIndex) current = idx;
2006 					visible [idx++] = i;
2007 				}
2008 			}
2009 			if (current + offset >= 0 && current + offset < idx){
2010 				index = visible [current + offset];
2011 			} else {
2012 				if (showChevron) {
2013 					Rectangle chevronRect = chevronItem.getBounds();
2014 					chevronRect = event.display.map(chevronTb, this, chevronRect);
2015 					CTabFolderEvent e = new CTabFolderEvent(this);
2016 					e.widget = this;
2017 					e.time = event.time;
2018 					e.x = chevronRect.x;
2019 					e.y = chevronRect.y;
2020 					e.width = chevronRect.width;
2021 					e.height = chevronRect.height;
2022 					e.doit = true;
2023 					for (CTabFolder2Listener folderListener : folderListeners) {
2024 						folderListener.showList(e);
2025 					}
2026 					if (e.doit && !isDisposed()) {
2027 						showList(chevronRect);
2028 					}
2029 				}
2030 			}
2031 		}
2032 	}
2033 	setSelection (index, true);
2034 }
onPaint(Event event)2035 void onPaint(Event event) {
2036 	if (inDispose) return;
2037 	Font font = getFont();
2038 	if (oldFont == null || !oldFont.equals(font)) {
2039 		// handle case where  default font changes
2040 		oldFont = font;
2041 		if (!updateTabHeight(false)) {
2042 			updateItems();
2043 			redraw();
2044 			return;
2045 		}
2046 	}
2047 
2048 	GC gc = event.gc;
2049 	Font gcFont = gc.getFont();
2050 	Color gcBackground = gc.getBackground();
2051 	Color gcForeground = gc.getForeground();
2052 
2053 // Useful for debugging paint problems
2054 //{
2055 //Point size = getSize();
2056 //gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN));
2057 //gc.fillRectangle(-10, -10, size.x + 20, size.y+20);
2058 //}
2059 
2060 	Point size = getSize();
2061 	Rectangle bodyRect = new Rectangle(0, 0, size.x, size.y);
2062 	renderer.draw(CTabFolderRenderer.PART_BODY, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
2063 
2064 	gc.setFont(gcFont);
2065 	gc.setForeground(gcForeground);
2066 	gc.setBackground(gcBackground);
2067 
2068 	renderer.draw(CTabFolderRenderer.PART_HEADER, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
2069 
2070 	gc.setFont(gcFont);
2071 	gc.setForeground(gcForeground);
2072 	gc.setBackground(gcBackground);
2073 
2074 	if (!single) {
2075 		for (int i=0; i < items.length; i++) {
2076 			Rectangle itemBounds = items[i].getBounds();
2077 			if (i != selectedIndex && event.getBounds().intersects(itemBounds)) {
2078 				renderer.draw(i, SWT.BACKGROUND | SWT.FOREGROUND | items[i].state , itemBounds, gc);
2079 			}
2080 		}
2081 	}
2082 
2083 	gc.setFont(gcFont);
2084 	gc.setForeground(gcForeground);
2085 	gc.setBackground(gcBackground);
2086 
2087 	if (selectedIndex != -1) {
2088 		renderer.draw(selectedIndex, items[selectedIndex].state | SWT.BACKGROUND | SWT.FOREGROUND, items[selectedIndex].getBounds(), gc);
2089 	}
2090 
2091 	gc.setFont(gcFont);
2092 	gc.setForeground(gcForeground);
2093 	gc.setBackground(gcBackground);
2094 
2095 	if (hoverTb) {
2096 		Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
2097 		int x = getSize().x - (trim.width + trim.x);
2098 		hoverRect = new Rectangle(x - 16 - SPACING, 2, 16, getTabHeight() - 2);
2099 		gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
2100 		x = hoverRect.x;
2101 		int y = hoverRect.y;
2102 		gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
2103 		gc.fillRectangle(x + hoverRect.width - 6, y, 5, 5);
2104 		gc.drawRectangle(x + hoverRect.width - 6, y, 5, 5);
2105 		gc.drawLine(x + hoverRect.width - 6, y+2, x + hoverRect.width - 6 + 5, y + 2);
2106 		gc.fillRectangle(x, y, 5 , 2);
2107 		gc.drawRectangle(x, y, 5 , 2);
2108 	}
2109 	gc.setFont(gcFont);
2110 	gc.setForeground(gcForeground);
2111 	gc.setBackground(gcBackground);
2112 }
2113 
onResize(Event event)2114 void onResize(Event event) {
2115 	if (inDispose) return;
2116 	if (ignoreResize) return;
2117 	if (updateItems()) {
2118 		redrawTabs();
2119 	}
2120 	Point size = getSize();
2121 	if (oldSize == null) {
2122 		redraw();
2123 	} else {
2124 		if (onBottom && size.y != oldSize.y) {
2125 			redraw();
2126 		} else {
2127 			int x1 = Math.min(size.x, oldSize.x);
2128 			Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
2129 			if (size.x != oldSize.x) x1 -= trim.width + trim.x - marginWidth + 2;
2130 			if (!simple) x1 -= 5; // rounded top right corner
2131 			int y1 = Math.min(size.y, oldSize.y);
2132 			if (size.y != oldSize.y) y1 -= trim.height + trim.y - marginHeight;
2133 			int x2 = Math.max(size.x, oldSize.x);
2134 			int y2 = Math.max(size.y, oldSize.y);
2135 			redraw(0, y1, x2, y2 - y1, false);
2136 			redraw(x1, 0, x2 - x1, y2, false);
2137 			if (hoverTb) {
2138 				redraw(hoverRect.x, hoverRect.y, hoverRect.width, hoverRect.height, false);
2139 			}
2140 		}
2141 	}
2142 	oldSize = size;
2143 }
onSelection(Event event)2144 void onSelection(Event event) {
2145 	if (hovering) {
2146 		hovering = false;
2147 		updateItems();
2148 	}
2149 	if (event.widget == maxItem) {
2150 		CTabFolderEvent e = new CTabFolderEvent(this);
2151 		e.widget = CTabFolder.this;
2152 		e.time = event.time;
2153 		for (CTabFolder2Listener folderListener : folderListeners) {
2154 			if (maximized) {
2155 				folderListener.restore(e);
2156 			} else {
2157 				folderListener.maximize(e);
2158 			}
2159 		}
2160 	} else if (event.widget == minItem) {
2161 		CTabFolderEvent e = new CTabFolderEvent(this);
2162 		e.widget = CTabFolder.this;
2163 		e.time = event.time;
2164 		for (CTabFolder2Listener folderListener : folderListeners) {
2165 			if (minimized) {
2166 				folderListener.restore(e);
2167 			} else {
2168 				folderListener.minimize(e);
2169 			}
2170 		}
2171 	} else if (event.widget == chevronItem) {
2172 		Rectangle chevronRect = chevronItem.getBounds();
2173 		chevronRect = event.display.map(chevronTb, this, chevronRect);
2174 		CTabFolderEvent e = new CTabFolderEvent(this);
2175 		e.widget = this;
2176 		e.time = event.time;
2177 		e.x = chevronRect.x;
2178 		e.y = chevronRect.y;
2179 		e.width = chevronRect.width;
2180 		e.height = chevronRect.height;
2181 		e.doit = true;
2182 		for (CTabFolder2Listener folderListener : folderListeners) {
2183 			folderListener.showList(e);
2184 		}
2185 		if (e.doit && !isDisposed()) {
2186 			showList(chevronRect);
2187 		}
2188 	}
2189 }
onTraverse(Event event)2190 void onTraverse (Event event) {
2191 	if (ignoreTraverse) return;
2192 	runUpdate();
2193 	switch (event.detail) {
2194 		case SWT.TRAVERSE_ESCAPE:
2195 		case SWT.TRAVERSE_RETURN:
2196 		case SWT.TRAVERSE_TAB_NEXT:
2197 		case SWT.TRAVERSE_TAB_PREVIOUS:
2198 			Control focusControl = getDisplay().getFocusControl();
2199 			if (focusControl == this) event.doit = true;
2200 			break;
2201 		case SWT.TRAVERSE_MNEMONIC:
2202 			event.doit = onMnemonic(event, false);
2203 			break;
2204 		case SWT.TRAVERSE_PAGE_NEXT:
2205 		case SWT.TRAVERSE_PAGE_PREVIOUS:
2206 			event.doit = items.length > 0;
2207 			break;
2208 	}
2209 	ignoreTraverse = true;
2210 	notifyListeners(SWT.Traverse, event);
2211 	ignoreTraverse = false;
2212 	event.type = SWT.None;
2213 	if (isDisposed()) return;
2214 	if (!event.doit) return;
2215 	switch (event.detail) {
2216 		case SWT.TRAVERSE_MNEMONIC:
2217 			onMnemonic(event, true);
2218 			event.detail = SWT.TRAVERSE_NONE;
2219 			break;
2220 		case SWT.TRAVERSE_PAGE_NEXT:
2221 		case SWT.TRAVERSE_PAGE_PREVIOUS:
2222 			onPageTraversal(event);
2223 			event.detail = SWT.TRAVERSE_NONE;
2224 			break;
2225 	}
2226 }
redrawTabs()2227 void redrawTabs() {
2228 	Point size = getSize();
2229 	Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
2230 	if (onBottom) {
2231 		int h = trim.height + trim.y - marginHeight;
2232 		redraw(0, size.y - h - 1, size.x, h + 1, false);
2233 	} else {
2234 		redraw(0, 0, size.x, -trim.y - marginHeight + 1, false);
2235 	}
2236 }
2237 /**
2238  * Removes the listener.
2239  *
2240  * @param listener the listener which should no longer be notified
2241  *
2242  * @exception IllegalArgumentException <ul>
2243  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2244  * </ul>
2245  *
2246  * @exception SWTException <ul>
2247  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2248  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2249  * </ul>
2250  *
2251  * @see #addCTabFolder2Listener(CTabFolder2Listener)
2252  *
2253  * @since 3.0
2254  */
removeCTabFolder2Listener(CTabFolder2Listener listener)2255 public void removeCTabFolder2Listener(CTabFolder2Listener listener) {
2256 	checkWidget();
2257 	if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2258 	if (folderListeners.length == 0) return;
2259 	int index = -1;
2260 	for (int i = 0; i < folderListeners.length; i++) {
2261 		if (listener == folderListeners[i]){
2262 			index = i;
2263 			break;
2264 		}
2265 	}
2266 	if (index == -1) return;
2267 	if (folderListeners.length == 1) {
2268 		folderListeners = new CTabFolder2Listener[0];
2269 		return;
2270 	}
2271 	CTabFolder2Listener[] newTabListeners = new CTabFolder2Listener[folderListeners.length - 1];
2272 	System.arraycopy(folderListeners, 0, newTabListeners, 0, index);
2273 	System.arraycopy(folderListeners, index + 1, newTabListeners, index, folderListeners.length - index - 1);
2274 	folderListeners = newTabListeners;
2275 }
2276 /**
2277  * Removes the listener.
2278  *
2279  * @param listener the listener which should no longer be notified
2280  *
2281  * @exception IllegalArgumentException <ul>
2282  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2283  * </ul>
2284  *
2285  * @exception SWTException <ul>
2286  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2287  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2288  * </ul>
2289  *
2290  * @deprecated see removeCTabFolderCloseListener(CTabFolderListener)
2291  */
2292 @Deprecated
removeCTabFolderListener(CTabFolderListener listener)2293 public void removeCTabFolderListener(CTabFolderListener listener) {
2294 	checkWidget();
2295 	if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2296 	if (tabListeners.length == 0) return;
2297 	int index = -1;
2298 	for (int i = 0; i < tabListeners.length; i++) {
2299 		if (listener == tabListeners[i]){
2300 			index = i;
2301 			break;
2302 		}
2303 	}
2304 	if (index == -1) return;
2305 	if (tabListeners.length == 1) {
2306 		tabListeners = new CTabFolderListener[0];
2307 		return;
2308 	}
2309 	CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
2310 	System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
2311 	System.arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1);
2312 	tabListeners = newTabListeners;
2313 }
2314 /**
2315  * Removes the listener from the collection of listeners who will
2316  * be notified when the user changes the receiver's selection.
2317  *
2318  * @param listener the listener which should no longer be notified
2319  *
2320  * @exception IllegalArgumentException <ul>
2321  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2322  * </ul>
2323  * @exception SWTException <ul>
2324  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2325  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2326  * </ul>
2327  *
2328  * @see SelectionListener
2329  * @see #addSelectionListener
2330  */
removeSelectionListener(SelectionListener listener)2331 public void removeSelectionListener(SelectionListener listener) {
2332 	checkWidget();
2333 	if (listener == null) {
2334 		SWT.error(SWT.ERROR_NULL_ARGUMENT);
2335 	}
2336 	removeListener(SWT.Selection, listener);
2337 	removeListener(SWT.DefaultSelection, listener);
2338 }
2339 
2340 @Override
reskin(int flags)2341 public void reskin(int flags) {
2342 	super.reskin(flags);
2343 	for (CTabItem item : items) {
2344 		item.reskin(flags);
2345 	}
2346 }
2347 
2348 @Override
setBackground(Color color)2349 public void setBackground (Color color) {
2350 	super.setBackground(color);
2351 	renderer.createAntialiasColors(); //TODO: need better caching strategy
2352 	updateBkImages();
2353 	redraw();
2354 }
2355 /**
2356  * Specify a gradient of colors to be drawn in the background of the unselected tabs.
2357  * For example to draw a gradient that varies from dark blue to blue and then to
2358  * white, use the following call to setBackground:
2359  * <pre>
2360  *	cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2361  *		                           display.getSystemColor(SWT.COLOR_BLUE),
2362  *		                           display.getSystemColor(SWT.COLOR_WHITE),
2363  *		                           display.getSystemColor(SWT.COLOR_WHITE)},
2364  *		               new int[] {25, 50, 100});
2365  * </pre>
2366  *
2367  * @param colors an array of Color that specifies the colors to appear in the gradient
2368  *               in order of appearance left to right.  The value <code>null</code> clears the
2369  *               background gradient. The value <code>null</code> can be used inside the array of
2370  *               Color to specify the background color.
2371  * @param percents an array of integers between 0 and 100 specifying the percent of the width
2372  *                 of the widget at which the color should change.  The size of the <code>percents</code>
2373  *                 array must be one less than the size of the <code>colors</code> array.
2374  *
2375  * @exception SWTException <ul>
2376  *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2377  *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2378  *	</ul>
2379  *
2380  * @since 3.6
2381  */
setBackground(Color[] colors, int[] percents)2382 public void setBackground(Color[] colors, int[] percents) {
2383 	setBackground(colors, percents, false);
2384 }
2385 /**
2386  * Specify a gradient of colors to be drawn in the background of the unselected tab.
2387  * For example to draw a vertical gradient that varies from dark blue to blue and then to
2388  * white, use the following call to setBackground:
2389  * <pre>
2390  *	cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2391  *		                           display.getSystemColor(SWT.COLOR_BLUE),
2392  *		                           display.getSystemColor(SWT.COLOR_WHITE),
2393  *		                           display.getSystemColor(SWT.COLOR_WHITE)},
2394  *		                  new int[] {25, 50, 100}, true);
2395  * </pre>
2396  *
2397  * @param colors an array of Color that specifies the colors to appear in the gradient
2398  *               in order of appearance left to right.  The value <code>null</code> clears the
2399  *               background gradient. The value <code>null</code> can be used inside the array of
2400  *               Color to specify the background color.
2401  * @param percents an array of integers between 0 and 100 specifying the percent of the width
2402  *                 of the widget at which the color should change.  The size of the <code>percents</code>
2403  *                 array must be one less than the size of the <code>colors</code> array.
2404  *
2405  * @param vertical indicate the direction of the gradient. <code>True</code> is vertical and <code>false</code> is horizontal.
2406  *
2407  * @exception SWTException <ul>
2408  *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2409  *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2410  *	</ul>
2411  *
2412  * @since 3.6
2413  */
setBackground(Color[] colors, int[] percents, boolean vertical)2414 public void setBackground(Color[] colors, int[] percents, boolean vertical) {
2415 	checkWidget();
2416 	if (colors != null) {
2417 		if (percents == null || percents.length != colors.length - 1) {
2418 			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2419 		}
2420 		for (int i = 0; i < percents.length; i++) {
2421 			if (percents[i] < 0 || percents[i] > 100) {
2422 				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2423 			}
2424 			if (i > 0 && percents[i] < percents[i-1]) {
2425 				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2426 			}
2427 		}
2428 		if (getDisplay().getDepth() < 15) {
2429 			// Don't use gradients on low color displays
2430 			colors = new Color[] {colors[colors.length - 1]};
2431 			percents = new int[] {};
2432 		}
2433 	}
2434 
2435 	// Are these settings the same as before?
2436 	if ((gradientColors != null) && (colors != null) &&
2437 		(gradientColors.length == colors.length)) {
2438 		boolean same = false;
2439 		for (int i = 0; i < gradientColors.length; i++) {
2440 			if (gradientColors[i] == null) {
2441 			same = colors[i] == null;
2442 			} else {
2443 			same = gradientColors[i].equals(colors[i]);
2444 			}
2445 			if (!same) break;
2446 		}
2447 		if (same) {
2448 			for (int i = 0; i < gradientPercents.length; i++) {
2449 			same = gradientPercents[i] == percents[i];
2450 			if (!same) break;
2451 			}
2452 		}
2453 		if (same && this.gradientVertical == vertical) return;
2454 	}
2455 	// Store the new settings
2456 	if (colors == null) {
2457 		gradientColors = null;
2458 		gradientPercents = null;
2459 		gradientVertical = false;
2460 		setBackground((Color)null);
2461 	} else {
2462 		gradientColors = new Color[colors.length];
2463 		for (int i = 0; i < colors.length; ++i) {
2464 			gradientColors[i] = colors[i];
2465 		}
2466 		gradientPercents = new int[percents.length];
2467 		for (int i = 0; i < percents.length; ++i) {
2468 			gradientPercents[i] = percents[i];
2469 		}
2470 		gradientVertical = vertical;
2471 		setBackground(gradientColors[gradientColors.length-1]);
2472 	}
2473 
2474 	// Refresh with the new settings
2475 	redraw();
2476 }
2477 @Override
setBackgroundImage(Image image)2478 public void setBackgroundImage(Image image) {
2479 		super.setBackgroundImage(image);
2480 		renderer.createAntialiasColors(); //TODO: need better caching strategy
2481 		redraw();
2482 }
2483 /**
2484  * Toggle the visibility of the border
2485  *
2486  * @param show true if the border should be displayed
2487  *
2488  * @exception SWTException <ul>
2489  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2490  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2491  * </ul>
2492  */
setBorderVisible(boolean show)2493 public void setBorderVisible(boolean show) {
2494 	checkWidget();
2495 	if (borderVisible == show) return;
2496 	this.borderVisible = show;
2497 	updateFolder(REDRAW);
2498 }
2499 
2500 /**
2501  * Create or dispose min/max buttons.
2502  */
updateButtons()2503 void updateButtons() {
2504 	// max button
2505 	Display display = getDisplay();
2506 	if (showMax) {
2507 		if (minMaxTb == null) {
2508 			minMaxTb = new ToolBar(this, SWT.FLAT);
2509 			initAccessibleMinMaxTb();
2510 			addTabControl(minMaxTb, SWT.TRAIL, 0, false);
2511 		}
2512 		if (maxItem == null) {
2513 			maxItem = new ToolItem(minMaxTb, SWT.PUSH);
2514 			if (maxImage == null) {
2515 				maxImage = createButtonImage(display, CTabFolderRenderer.PART_MAX_BUTTON);
2516 			}
2517 			maxItem.setImage(maxImage);
2518 			maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
2519 			maxItem.addListener(SWT.Selection, listener);
2520 		}
2521 	} else {
2522 		//might need to remove it if already there
2523 		if (maxItem != null) {
2524 			maxItem.dispose();
2525 			maxItem = null;
2526 		}
2527 	}
2528 	// min button
2529 	if (showMin) {
2530 		if (minMaxTb == null) {
2531 			minMaxTb = new ToolBar(this, SWT.FLAT);
2532 			initAccessibleMinMaxTb();
2533 			addTabControl(minMaxTb, SWT.TRAIL, 0, false);
2534 		}
2535 		if (minItem == null) {
2536 			minItem = new ToolItem(minMaxTb, SWT.PUSH, 0);
2537 			if (minImage == null) {
2538 				minImage = createButtonImage(display, CTabFolderRenderer.PART_MIN_BUTTON);
2539 			}
2540 			minItem.setImage(minImage);
2541 			minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
2542 			minItem.addListener(SWT.Selection, listener);
2543 		}
2544 	} else {
2545 		//might need to remove it if already there
2546 		if (minItem != null) {
2547 			minItem.dispose();
2548 			minItem = null;
2549 		}
2550 	}
2551 	if (minMaxTb != null && minMaxTb.getItemCount() == 0) {
2552 		removeTabControl(minMaxTb, false);
2553 		minMaxTb.dispose();
2554 		minMaxTb = null;
2555 	}
2556 }
2557 
2558 /**
2559  * Update button bounds for min/max and update chevron button.
2560  */
setButtonBounds()2561 void setButtonBounds() {
2562 	if (showChevron) {
2563 		updateChevronImage(false);
2564 	}
2565 
2566 	Point size = getSize();
2567 	boolean[][] overflow = new boolean[1][0];
2568 	Rectangle[] rects = computeControlBounds(size, overflow);
2569 	if (fixedTabHeight != SWT.DEFAULT) {
2570 		int height = fixedTabHeight;
2571 		if (!hovering) {
2572 			hoverTb = false;
2573 			Rectangle tabBounds = this.getBounds();
2574 			for (int i = 0; i < rects.length; i++) {
2575 				if (!(overflow[0][i])) {
2576 					if (rects[i].height > height) {
2577 						hoverTb = true;
2578 						break;
2579 					}
2580 				}
2581 			}
2582 			if (hoverTb) {
2583 				for (int i = 0; i < rects.length; i++) {
2584 					if (!(overflow[0][i])) {
2585 						if (rects[i].height > height) {
2586 							rects[i].x = tabBounds.width + 20;
2587 						}
2588 					}
2589 				}
2590 			}
2591 		}
2592 	}
2593 	int headerHeight = 0;
2594 	for (int i = 0; i < rects.length; i++) {
2595 		if (!overflow[0][i]) headerHeight = Math.max(rects[i].height, headerHeight);
2596 	}
2597 	boolean changed = false;
2598 	ignoreResize = true;
2599 	for (int i = 0; i < controls.length; i++) {
2600 		if (!controls[i].isDisposed()) {
2601 			if (overflow[0][i]) {
2602 				controls[i].setBounds(rects[i]);
2603 			} else {
2604 				controls[i].moveAbove(null);
2605 				controls[i].setBounds(rects[i].x, rects[i].y, rects[i].width, headerHeight);
2606 			}
2607 		}
2608 		if (!changed && !rects[i].equals(controlRects[i])) changed = true;
2609 	}
2610 	ignoreResize = false;
2611 	controlRects = rects;
2612 	if (changed || hovering) updateBkImages();
2613 }
2614 
2615 /**
2616  * Get the number of hidden items or the number which is to be drawn in the
2617  * chevon item.
2618  * <p>
2619  * Note: do not confuse this with {@link #chevronCount} which contains the count
2620  * from the last time the cached chevron image was drawn. It can be different
2621  * from the value returned by this method.
2622  * </p>
2623  *
2624  * @return the chevron count
2625  */
getChevronCount()2626 int getChevronCount() {
2627 	int itemCount = items.length;
2628 	int count;
2629 	if (single) {
2630 		count = selectedIndex == -1 ? itemCount : itemCount - 1;
2631 	} else {
2632 		int showCount = 0;
2633 		while (showCount < priority.length && items[priority[showCount]].showing) {
2634 			showCount++;
2635 		}
2636 		count = itemCount - showCount;
2637 	}
2638 	return count;
2639 }
2640 
2641 /**
2642  * Update the cached chevron image.
2643  *
2644  * @param styleChange <code>true</code> if the update is required for changed
2645  *                    appearance of the chevron. In this case the image is not
2646  *                    created if it does not already exist and is updated even
2647  *                    if the drawn number (chevonCount) has not changed.
2648  */
updateChevronImage(boolean styleChange)2649 private void updateChevronImage(boolean styleChange) {
2650 	if (styleChange && chevronImage == null) return;
2651 	int newCount = getChevronCount();
2652 	if (!styleChange && chevronCount == newCount) return;
2653 	if (chevronImage != null) chevronImage.dispose();
2654 	chevronImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_CHEVRON_BUTTON);
2655 	chevronItem.setImage(chevronImage);
2656 	chevronCount = newCount;
2657 }
2658 @Override
setFocus()2659 public boolean setFocus () {
2660 	checkWidget ();
2661 
2662 	/*
2663 	* Feature in SWT.  When a new tab item is selected
2664 	* and the previous tab item had focus, removing focus
2665 	* from the previous tab item causes fixFocus() to give
2666 	* focus to the first child, which is usually one of the
2667 	* toolbars. This is unexpected.
2668 	* The fix is to try to set focus on the first tab item
2669 	* if fixFocus() is called.
2670 	*/
2671 	Control focusControl = getDisplay().getFocusControl ();
2672 	boolean fixFocus = isAncestor (focusControl);
2673 	if (fixFocus) {
2674 		CTabItem item = getSelection();
2675 		if (item != null) {
2676 			if (item.setFocus ()) return true;
2677 		}
2678 	}
2679 	return super.setFocus ();
2680 }
2681 /* Copy of isFocusAncestor from Control. */
isAncestor(Control control)2682 boolean isAncestor (Control control) {
2683 	while (control != null && control != this && !(control instanceof Shell)) {
2684 		control = control.getParent();
2685 	}
2686 	return control == this;
2687 }
2688 @Override
setFont(Font font)2689 public void setFont(Font font) {
2690 	checkWidget();
2691 	if (font != null && font.equals(getFont())) return;
2692 	super.setFont(font);
2693 	oldFont = getFont();
2694 	// Chevron painting is cached as image and only recreated if number of hidden tabs changed.
2695 	// To apply the new font the cached image must be recreated with new font.
2696 	// Redraw request alone would only redraw the cached image with old font.
2697 	renderer.chevronFont = null; // renderer will pickup and adjust(!) the new font automatically
2698 	updateChevronImage(true);
2699 	updateFolder(REDRAW);
2700 }
2701 @Override
setForeground(Color color)2702 public void setForeground (Color color) {
2703 	super.setForeground(color);
2704 	// Chevron painting is cached as image and only recreated if number of hidden tabs changed.
2705 	// To apply the new foreground color the image must be recreated with new foreground color.
2706 	// redraw() alone would only redraw the cached image with old color.
2707 	updateChevronImage(true);
2708 	redraw();
2709 }
2710 /**
2711  * Display an insert marker before or after the specified tab item.
2712  *
2713  * A value of null will clear the mark.
2714  *
2715  * @param item the item with which the mark is associated or null
2716  *
2717  * @param after true if the mark should be displayed after the specified item
2718  *
2719  * @exception SWTException <ul>
2720  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2721  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2722  * </ul>
2723  */
setInsertMark(CTabItem item, boolean after)2724 public void setInsertMark(CTabItem item, boolean after) {
2725 	checkWidget();
2726 }
2727 /**
2728  * Display an insert marker before or after the specified tab item.
2729  *
2730  * A value of -1 will clear the mark.
2731  *
2732  * @param index the index of the item with which the mark is associated or -1
2733  *
2734  * @param after true if the mark should be displayed after the specified item
2735  *
2736  * @exception IllegalArgumentException <ul>
2737  *    <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
2738  * </ul>
2739  *
2740  * @exception SWTException <ul>
2741  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2742  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2743  * </ul>
2744  */
setInsertMark(int index, boolean after)2745 public void setInsertMark(int index, boolean after) {
2746 	checkWidget();
2747 	if (index < -1 || index >= getItemCount()) {
2748 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2749 	}
2750 }
setItemLocation(GC gc)2751 boolean setItemLocation(GC gc) {
2752 	boolean changed = false;
2753 	if (items.length == 0) return false;
2754 	Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
2755 	int borderBottom = trim.height + trim.y;
2756 	int borderTop = -trim.y;
2757 	Point size = getSize();
2758 	int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop;
2759 	Point closeButtonSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, 0, gc, SWT.DEFAULT, SWT.DEFAULT);
2760 	int leftItemEdge = getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
2761 	if (single) {
2762 		int defaultX = getDisplay().getBounds().width + 10; // off screen
2763 		for (int i = 0; i < items.length; i++) {
2764 			CTabItem item = items[i];
2765 			if (i == selectedIndex) {
2766 				firstIndex = selectedIndex;
2767 				int oldX = item.x, oldY = item.y;
2768 				item.x = leftItemEdge;
2769 				item.y = y;
2770 				item.showing = true;
2771 				if (showClose || item.showClose) {
2772 					item.closeRect.x = leftItemEdge - renderer.computeTrim(i, SWT.NONE, 0, 0, 0, 0).x;
2773 					item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
2774 				}
2775 				if (item.x != oldX || item.y != oldY) changed = true;
2776 			} else {
2777 				item.x = defaultX;
2778 				item.showing = false;
2779 			}
2780 		}
2781 	} else {
2782 		int rightItemEdge = getRightItemEdge(gc);
2783 		int maxWidth = rightItemEdge - leftItemEdge;
2784 		int width = 0;
2785 		for (int i = 0; i < priority.length; i++) {
2786 			CTabItem item = items[priority[i]];
2787 			width += item.width;
2788 			item.showing = i == 0 ? true : item.width > 0 && width <= maxWidth;
2789 		}
2790 		int x = getLeftItemEdge(gc, CTabFolderRenderer.PART_HEADER);
2791 		int defaultX = getDisplay().getBounds().width + 10; // off screen
2792 		firstIndex = items.length - 1;
2793 		for (int i = 0; i < items.length; i++) {
2794 			CTabItem item = items[i];
2795 			if (!item.showing) {
2796 				if (item.x != defaultX) changed = true;
2797 				item.x = defaultX;
2798 			} else {
2799 				firstIndex = Math.min(firstIndex, i);
2800 				if (item.x != x || item.y != y) changed = true;
2801 				item.x = x;
2802 				item.y = y;
2803 				int state = SWT.NONE;
2804 				if (i == selectedIndex) state |= SWT.SELECTED;
2805 				Rectangle edgeTrim = renderer.computeTrim(i, state, 0, 0, 0, 0);
2806 				item.closeRect.x = item.x + item.width  - (edgeTrim.width + edgeTrim.x) - closeButtonSize.x;
2807 				item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
2808 				x = x + item.width;
2809 				if (!simple && i == selectedIndex) x -= renderer.curveIndent; //TODO: fix next item position
2810 			}
2811 		}
2812 	}
2813 	return changed;
2814 }
2815 /**
2816  * Reorder the items of the receiver.
2817  * @param indices an array containing the new indices for all items
2818  *
2819  * @exception IllegalArgumentException <ul>
2820  *    <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
2821  *    <li>ERROR_INVALID_ARGUMENT - if the indices array is not the same length as the number of items,
2822  *    if there are duplicate indices or an index is out of range.</li>
2823  * </ul>
2824  *
2825  * @exception SWTException <ul>
2826  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2827  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2828  * </ul>
2829  */
setItemOrder(int[] indices)2830 /*public*/ void setItemOrder (int[] indices) {
2831 	checkWidget();
2832 	if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2833 	if (indices.length != items.length) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2834 	int newSelectedIndex = -1;
2835 	boolean[] seen = new boolean[items.length];
2836 	CTabItem[] temp = new CTabItem[items.length];
2837 	for (int i=0; i<indices.length; i++) {
2838 		int index = indices[i];
2839 		if (!(0 <= index && index < items.length)) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2840 		if (seen[index]) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2841 		seen[index] = true;
2842 		if (index == selectedIndex) newSelectedIndex = i;
2843 		temp[i] = items[index];
2844 	}
2845 	items = temp;
2846 	selectedIndex = newSelectedIndex;
2847 	updateFolder(REDRAW);
2848 }
setItemSize(GC gc)2849 boolean setItemSize(GC gc) {
2850 	boolean changed = false;
2851 	if (isDisposed()) return changed;
2852 	Point size = getSize();
2853 	if (size.x <= 0 || size.y <= 0) return changed;
2854 	ToolBar chevron = getChevron();
2855 	if (chevron != null) chevron.setVisible(false);
2856 	showChevron = false;
2857 	if (single) {
2858 		showChevron = chevronVisible && items.length > 1;
2859 		if (showChevron) {
2860 			chevron.setVisible(true);
2861 		}
2862 		if (selectedIndex != -1) {
2863 			CTabItem tab = items[selectedIndex];
2864 			int width = renderer.computeSize(selectedIndex, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2865 			width = Math.min(width, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
2866 			if (tab.height != tabHeight || tab.width != width) {
2867 				changed = true;
2868 				tab.shortenedText = null;
2869 				tab.shortenedTextWidth = 0;
2870 				tab.height = tabHeight;
2871 				tab.width = width;
2872 				tab.closeRect.width = tab.closeRect.height = 0;
2873 				if (showClose || tab.showClose) {
2874 					Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT);
2875 					tab.closeRect.width = closeSize.x;
2876 					tab.closeRect.height = closeSize.y;
2877 				}
2878 			}
2879 		}
2880 		return changed;
2881 	}
2882 
2883 	if (items.length == 0) return changed;
2884 	int[] widths;
2885 	int tabAreaWidth = Math.max(0, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
2886 	// First, try the minimum tab size at full compression.
2887 	int minWidth = 0;
2888 	int[] minWidths = new int[items.length];
2889 	for (int element : priority) {
2890 		int index = element;
2891 		int state = CTabFolderRenderer.MINIMUM_SIZE;
2892 		if (index == selectedIndex) state |= SWT.SELECTED;
2893 		minWidths[index] = renderer.computeSize(index, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2894 		minWidth += minWidths[index];
2895 		if (minWidth > tabAreaWidth) break;
2896 	}
2897 	if (minWidth > tabAreaWidth) {
2898 		// full compression required and a chevron
2899 		showChevron = chevronVisible && items.length > 1;
2900 		if (showChevron) {
2901 			tabAreaWidth -= chevron.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
2902 			chevron.setVisible(true);
2903 		}
2904 		widths = minWidths;
2905 		int index = selectedIndex != -1 ? selectedIndex : 0;
2906 		if (tabAreaWidth < widths[index]) {
2907 			widths[index] = Math.max(0, tabAreaWidth);
2908 		}
2909 	} else {
2910 		int maxWidth = 0;
2911 		int[] maxWidths = new int[items.length];
2912 		for (int i = 0; i < items.length; i++) {
2913 			int state = 0;
2914 			if (i == selectedIndex) state |= SWT.SELECTED;
2915 			maxWidths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2916 			maxWidth += maxWidths[i];
2917 		}
2918 		if (maxWidth <= tabAreaWidth) {
2919 			// no compression required
2920 			widths = maxWidths;
2921 		} else {
2922 			// determine compression for each item
2923 			int extra = (tabAreaWidth - minWidth) / items.length;
2924 			while (true) {
2925 				int large = 0, totalWidth = 0;
2926 				for (int i = 0 ; i < items.length; i++) {
2927 					if (maxWidths[i] > minWidths[i] + extra) {
2928 						totalWidth += minWidths[i] + extra;
2929 						large++;
2930 					} else {
2931 						totalWidth += maxWidths[i];
2932 					}
2933 				}
2934 				if (totalWidth >= tabAreaWidth) {
2935 					extra--;
2936 					break;
2937 				}
2938 				if (large == 0 || tabAreaWidth - totalWidth < large) break;
2939 				extra++;
2940 			}
2941 			widths = new int[items.length];
2942 			for (int i = 0; i < items.length; i++) {
2943 				widths[i] = Math.min(maxWidths[i], minWidths[i] + extra);
2944 			}
2945 		}
2946 	}
2947 
2948 	for (int i = 0; i < items.length; i++) {
2949 		CTabItem tab = items[i];
2950 		int width = widths[i];
2951 		if (tab.height != tabHeight || tab.width != width) {
2952 			changed = true;
2953 			tab.shortenedText = null;
2954 			tab.shortenedTextWidth = 0;
2955 			tab.height = tabHeight;
2956 			tab.width = width;
2957 			tab.closeRect.width = tab.closeRect.height = 0;
2958 			if (showClose || tab.showClose) {
2959 				if (i == selectedIndex || showUnselectedClose) {
2960 					Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT);
2961 					tab.closeRect.width = closeSize.x;
2962 					tab.closeRect.height = closeSize.y;
2963 				}
2964 			}
2965 		}
2966 	}
2967 	return changed;
2968 }
2969 /**
2970  * Marks the receiver's maximize button as visible if the argument is <code>true</code>,
2971  * and marks it invisible otherwise.
2972  *
2973  * @param visible the new visibility state
2974  *
2975  * @exception SWTException <ul>
2976  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2977  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2978  * </ul>
2979  *
2980  * @since 3.0
2981  */
setMaximizeVisible(boolean visible)2982 public void setMaximizeVisible(boolean visible) {
2983 	checkWidget();
2984 	if (showMax == visible) return;
2985 	// display maximize button
2986 	showMax = visible;
2987 	updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
2988 }
2989 /**
2990  * Sets the layout which is associated with the receiver to be
2991  * the argument which may be null.
2992  * <p>
2993  * Note: No Layout can be set on this Control because it already
2994  * manages the size and position of its children.
2995  * </p>
2996  *
2997  * @param layout the receiver's new layout or null
2998  *
2999  * @exception SWTException <ul>
3000  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3001  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3002  * </ul>
3003  */
3004 @Override
setLayout(Layout layout)3005 public void setLayout (Layout layout) {
3006 	checkWidget();
3007 	return;
3008 }
3009 /**
3010  * Sets the maximized state of the receiver.
3011  *
3012  * @param maximize the new maximized state
3013  *
3014  * @exception SWTException <ul>
3015  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3016  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3017  * </ul>
3018  *
3019  * @since 3.0
3020  */
setMaximized(boolean maximize)3021 public void setMaximized(boolean maximize) {
3022 	checkWidget ();
3023 	if (this.maximized == maximize) return;
3024 	if (maximize && this.minimized) setMinimized(false);
3025 	this.maximized = maximize;
3026 	if (minMaxTb != null && maxItem != null) {
3027 		if (maxImage != null) maxImage.dispose();
3028 		maxImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MAX_BUTTON);
3029 		maxItem.setImage(maxImage);
3030 		maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
3031 	}
3032 }
3033 /**
3034  * Marks the receiver's minimize button as visible if the argument is <code>true</code>,
3035  * and marks it invisible otherwise.
3036  *
3037  * @param visible the new visibility state
3038  *
3039  * @exception SWTException <ul>
3040  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3041  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3042  * </ul>
3043  *
3044  * @since 3.0
3045  */
setMinimizeVisible(boolean visible)3046 public void setMinimizeVisible(boolean visible) {
3047 	checkWidget();
3048 	if (showMin == visible) return;
3049 	// display minimize button
3050 	showMin = visible;
3051 	updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3052 }
3053 /**
3054  * Sets the minimized state of the receiver.
3055  *
3056  * @param minimize the new minimized state
3057  *
3058  * @exception SWTException <ul>
3059  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3060  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3061  * </ul>
3062  *
3063  * @since 3.0
3064  */
setMinimized(boolean minimize)3065 public void setMinimized(boolean minimize) {
3066 	checkWidget ();
3067 	if (this.minimized == minimize) return;
3068 	if (minimize && this.maximized) setMaximized(false);
3069 	this.minimized = minimize;
3070 	if (minMaxTb != null && minItem != null) {
3071 		if (minImage != null) minImage.dispose();
3072 		minImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MIN_BUTTON);
3073 		minItem.setImage(minImage);
3074 		minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
3075 	}
3076 }
3077 
3078 /**
3079  * Sets the minimum number of characters that will
3080  * be displayed in a fully compressed tab.
3081  *
3082  * @param count the minimum number of characters that will be displayed in a fully compressed tab
3083  *
3084  * @exception SWTException <ul>
3085  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3086  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3087  *    <li>ERROR_INVALID_RANGE - if the count is less than zero</li>
3088  * </ul>
3089  *
3090  * @since 3.0
3091  */
setMinimumCharacters(int count)3092 public void setMinimumCharacters(int count) {
3093 	checkWidget ();
3094 	if (count < 0) SWT.error(SWT.ERROR_INVALID_RANGE);
3095 	if (minChars == count) return;
3096 	minChars = count;
3097 	updateFolder(REDRAW_TABS);
3098 }
3099 
3100 /**
3101  * When there is not enough horizontal space to show all the tabs,
3102  * by default, tabs are shown sequentially from left to right in
3103  * order of their index.  When the MRU visibility is turned on,
3104  * the tabs that are visible will be the tabs most recently selected.
3105  * Tabs will still maintain their left to right order based on index
3106  * but only the most recently selected tabs are visible.
3107  * <p>
3108  * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
3109  * "Tab 3" and "Tab 4" (in order by index).  The user selects
3110  * "Tab 1" and then "Tab 3".  If the CTabFolder is now
3111  * compressed so that only two tabs are visible, by default,
3112  * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
3113  * selected and "Tab 2" because it is the previous item in index order).
3114  * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
3115  * and "Tab 3" (in that order from left to right).</p>
3116  *
3117  * @param show the new visibility state
3118  *
3119  * @exception SWTException <ul>
3120  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3121  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3122  * </ul>
3123  *
3124  * @since 3.1
3125  */
setMRUVisible(boolean show)3126 public void setMRUVisible(boolean show) {
3127 	checkWidget();
3128 	if (mru == show) return;
3129 	mru = show;
3130 	if (!mru) {
3131 		if (firstIndex == -1) return;
3132 		int idx = firstIndex;
3133 		int next = 0;
3134 		for (int i = firstIndex; i < items.length; i++) {
3135 			priority[next++] = i;
3136 		}
3137 		for (int i = 0; i < idx; i++) {
3138 			priority[next++] = i;
3139 		}
3140 		updateFolder(REDRAW_TABS);
3141 	}
3142 }
3143 /**
3144  * Sets the renderer which is associated with the receiver to be
3145  * the argument which may be null. In the case of null, the default
3146  * renderer is used.
3147  *
3148  * @param renderer a new renderer
3149  *
3150  * @exception SWTException <ul>
3151  *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3152  *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3153  *	</ul>
3154  *
3155  * @see CTabFolderRenderer
3156  *
3157  * @since 3.6
3158  */
setRenderer(CTabFolderRenderer renderer)3159 public void setRenderer(CTabFolderRenderer renderer) {
3160 	checkWidget();
3161 	if (this.renderer == renderer || (useDefaultRenderer && renderer == null)) return;
3162 	if (this.renderer != null) this.renderer.dispose();
3163 	useDefaultRenderer = renderer == null;
3164 	if (useDefaultRenderer) renderer = new CTabFolderRenderer(this);
3165 	this.renderer = renderer;
3166 	updateFolder(REDRAW);
3167 }
3168 /**
3169  * Set the selection to the tab at the specified item.
3170  *
3171  * @param item the tab item to be selected
3172  *
3173  * @exception IllegalArgumentException <ul>
3174  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3175  * </ul>
3176  *
3177  * @exception SWTException <ul>
3178  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3179  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3180  * </ul>
3181  */
setSelection(CTabItem item)3182 public void setSelection(CTabItem item) {
3183 	checkWidget();
3184 	if (item == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3185 	int index = indexOf(item);
3186 	setSelection(index);
3187 }
3188 /**
3189  * Set the selection to the tab at the specified index.
3190  *
3191  * @param index the index of the tab item to be selected
3192  *
3193  * @exception SWTException <ul>
3194  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3195  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3196  * </ul>
3197  */
setSelection(int index)3198 public void setSelection(int index) {
3199 	checkWidget();
3200 	if (index < 0 || index >= items.length) return;
3201 	CTabItem selection = items[index];
3202 	if (selectedIndex == index) {
3203 		showItem(selection);
3204 		return;
3205 	}
3206 
3207 	int oldIndex = selectedIndex;
3208 	selectedIndex = index;
3209 	if (oldIndex != -1) {
3210 		items[oldIndex].closeImageState = SWT.BACKGROUND;
3211 		items[oldIndex].state &= ~SWT.SELECTED;
3212 	}
3213 	selection.closeImageState = SWT.NONE;
3214 	selection.showing = false;
3215 	selection.state |= SWT.SELECTED;
3216 
3217 	Control newControl = selection.control;
3218 	Control oldControl = null;
3219 	if (oldIndex != -1) {
3220 		oldControl = items[oldIndex].control;
3221 	}
3222 
3223 	if (newControl != oldControl) {
3224 		if (newControl != null && !newControl.isDisposed()) {
3225 			newControl.setBounds(getClientArea());
3226 			newControl.setVisible(true);
3227 		}
3228 		if (oldControl != null && !oldControl.isDisposed()) {
3229 			oldControl.setVisible(false);
3230 		}
3231 	}
3232 	showItem(selection);
3233 	redraw();
3234 }
setSelection(int index, boolean notify)3235 void setSelection(int index, boolean notify) {
3236 	int oldSelectedIndex = selectedIndex;
3237 	setSelection(index);
3238 	if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
3239 		Event event = new Event();
3240 		event.item = getItem(selectedIndex);
3241 		notifyListeners(SWT.Selection, event);
3242 	}
3243 }
3244 /**
3245  * Sets the receiver's selection background color to the color specified
3246  * by the argument, or to the default system color for the control
3247  * if the argument is null.
3248  *
3249  * @param color the new color (or null)
3250  *
3251  * @exception IllegalArgumentException <ul>
3252  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
3253  * </ul>
3254  * @exception SWTException <ul>
3255  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3256  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3257  * </ul>
3258  *
3259  * @since 3.0
3260  */
setSelectionBackground(Color color)3261 public void setSelectionBackground (Color color) {
3262 	if (inDispose) return;
3263 	checkWidget();
3264 	setSelectionHighlightGradientColor(null);
3265 	if (selectionBackground == color) return;
3266 	if (color == null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND);
3267 	selectionBackground = color;
3268 	renderer.createAntialiasColors(); //TODO:  need better caching strategy
3269 	if (selectedIndex > -1) redraw();
3270 }
3271 /**
3272  * Specify a gradient of colours to be draw in the background of the selected tab.
3273  * For example to draw a gradient that varies from dark blue to blue and then to
3274  * white, use the following call to setBackground:
3275  * <pre>
3276  *	cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3277  *		                           display.getSystemColor(SWT.COLOR_BLUE),
3278  *		                           display.getSystemColor(SWT.COLOR_WHITE),
3279  *		                           display.getSystemColor(SWT.COLOR_WHITE)},
3280  *		               new int[] {25, 50, 100});
3281  * </pre>
3282  *
3283  * @param colors an array of Color that specifies the colors to appear in the gradient
3284  *               in order of appearance left to right.  The value <code>null</code> clears the
3285  *               background gradient. The value <code>null</code> can be used inside the array of
3286  *               Color to specify the background color.
3287  * @param percents an array of integers between 0 and 100 specifying the percent of the width
3288  *                 of the widget at which the color should change.  The size of the percents array must be one
3289  *                 less than the size of the colors array.
3290  *
3291  * @exception SWTException <ul>
3292  *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3293  *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3294  *	</ul>
3295  */
setSelectionBackground(Color[] colors, int[] percents)3296 public void setSelectionBackground(Color[] colors, int[] percents) {
3297 	setSelectionBackground(colors, percents, false);
3298 }
3299 /**
3300  * Specify a gradient of colours to be draw in the background of the selected tab.
3301  * For example to draw a vertical gradient that varies from dark blue to blue and then to
3302  * white, use the following call to setBackground:
3303  * <pre>
3304  *	cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3305  *		                           display.getSystemColor(SWT.COLOR_BLUE),
3306  *		                           display.getSystemColor(SWT.COLOR_WHITE),
3307  *		                           display.getSystemColor(SWT.COLOR_WHITE)},
3308  *		                  new int[] {25, 50, 100}, true);
3309  * </pre>
3310  *
3311  * @param colors an array of Color that specifies the colors to appear in the gradient
3312  *               in order of appearance left to right.  The value <code>null</code> clears the
3313  *               background gradient. The value <code>null</code> can be used inside the array of
3314  *               Color to specify the background color.
3315  * @param percents an array of integers between 0 and 100 specifying the percent of the width
3316  *                 of the widget at which the color should change.  The size of the percents array must be one
3317  *                 less than the size of the colors array.
3318  *
3319  * @param vertical indicate the direction of the gradient.  True is vertical and false is horizontal.
3320  *
3321  * @exception SWTException <ul>
3322  *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3323  *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3324  *	</ul>
3325  *
3326  * @since 3.0
3327  */
setSelectionBackground(Color[] colors, int[] percents, boolean vertical)3328 public void setSelectionBackground(Color[] colors, int[] percents, boolean vertical) {
3329 	checkWidget();
3330 	int colorsLength;
3331 	Color highlightBeginColor = null;  //null == no highlight
3332 
3333 	if (colors != null) {
3334 		//The colors array can optionally have an extra entry which describes the highlight top color
3335 		//Thus its either one or two larger than the percents array
3336 		if (percents == null ||
3337 				! ((percents.length == colors.length - 1) || (percents.length == colors.length - 2))){
3338 			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3339 		}
3340 		for (int i = 0; i < percents.length; i++) {
3341 			if (percents[i] < 0 || percents[i] > 100) {
3342 				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3343 			}
3344 			if (i > 0 && percents[i] < percents[i-1]) {
3345 				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3346 			}
3347 		}
3348 		//If the colors is exactly two more than percents then last is highlight
3349 		//Keep track of *real* colorsLength (minus the highlight)
3350 		if(percents.length == colors.length - 2) {
3351 			highlightBeginColor = colors[colors.length - 1];
3352 			colorsLength = colors.length - 1;
3353 		} else {
3354 			colorsLength = colors.length;
3355 		}
3356 		if (getDisplay().getDepth() < 15) {
3357 			// Don't use gradients on low color displays
3358 			colors = new Color[] {colors[colorsLength - 1]};
3359 			colorsLength = colors.length;
3360 			percents = new int[] {};
3361 		}
3362 	} else {
3363 		colorsLength = 0;
3364 	}
3365 
3366 	// Are these settings the same as before?
3367 	if (selectionBgImage == null) {
3368 		if ((selectionGradientColors != null) && (colors != null) &&
3369 			(selectionGradientColors.length == colorsLength)) {
3370 			boolean same = false;
3371 			for (int i = 0; i < selectionGradientColors.length; i++) {
3372 				if (selectionGradientColors[i] == null) {
3373 					same = colors[i] == null;
3374 				} else {
3375 					same = selectionGradientColors[i].equals(colors[i]);
3376 				}
3377 				if (!same) break;
3378 			}
3379 			if (same) {
3380 				for (int i = 0; i < selectionGradientPercents.length; i++) {
3381 					same = selectionGradientPercents[i] == percents[i];
3382 					if (!same) break;
3383 				}
3384 			}
3385 			if (same && this.selectionGradientVertical == vertical) return;
3386 		}
3387 	} else {
3388 		selectionBgImage = null;
3389 	}
3390 	// Store the new settings
3391 	if (colors == null) {
3392 		selectionGradientColors = null;
3393 		selectionGradientPercents = null;
3394 		selectionGradientVertical = false;
3395 		setSelectionBackground((Color)null);
3396 		setSelectionHighlightGradientColor(null);
3397 	} else {
3398 		selectionGradientColors = new Color[colorsLength];
3399 		for (int i = 0; i < colorsLength; ++i) {
3400 			selectionGradientColors[i] = colors[i];
3401 		}
3402 		selectionGradientPercents = new int[percents.length];
3403 		for (int i = 0; i < percents.length; ++i) {
3404 			selectionGradientPercents[i] = percents[i];
3405 		}
3406 		selectionGradientVertical = vertical;
3407 		setSelectionBackground(selectionGradientColors[selectionGradientColors.length-1]);
3408 		setSelectionHighlightGradientColor(highlightBeginColor);
3409 	}
3410 
3411 	// Refresh with the new settings
3412 	if (selectedIndex > -1) redraw();
3413 }
3414 
3415 /*
3416  * Set the color for the highlight start for selected tabs.
3417  * Update the cache of highlight gradient colors if required.
3418  */
setSelectionHighlightGradientColor(Color start)3419 void setSelectionHighlightGradientColor(Color start) {
3420 	if (inDispose) return;
3421 	renderer.setSelectionHighlightGradientColor(start);  //TODO: need better caching strategy
3422 }
3423 
3424 /**
3425  * Set the image to be drawn in the background of the selected tab.  Image
3426  * is stretched or compressed to cover entire selection tab area.
3427  *
3428  * @param image the image to be drawn in the background
3429  *
3430  * @exception SWTException <ul>
3431  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3432  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3433  * </ul>
3434  */
setSelectionBackground(Image image)3435 public void setSelectionBackground(Image image) {
3436 	checkWidget();
3437 	setSelectionHighlightGradientColor(null);
3438 	if (image == selectionBgImage) return;
3439 	if (image != null) {
3440 		selectionGradientColors = null;
3441 		selectionGradientPercents = null;
3442 		renderer.disposeSelectionHighlightGradientColors(); //TODO: need better caching strategy
3443 	}
3444 	selectionBgImage = image;
3445 	renderer.createAntialiasColors(); //TODO:  need better caching strategy
3446 	if (selectedIndex > -1) redraw();
3447 }
3448 /**
3449  * Set the foreground color of the selected tab.
3450  *
3451  * @param color the color of the text displayed in the selected tab
3452  *
3453  * @exception SWTException <ul>
3454  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3455  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3456  * </ul>
3457  */
setSelectionForeground(Color color)3458 public void setSelectionForeground (Color color) {
3459 	checkWidget();
3460 	if (selectionForeground == color) return;
3461 	if (color == null) color = getDisplay().getSystemColor(SELECTION_FOREGROUND);
3462 	selectionForeground = color;
3463 	if (selectedIndex > -1) redraw();
3464 }
3465 
3466 /**
3467  * Sets the shape that the CTabFolder will use to render itself.
3468  *
3469  * @param simple <code>true</code> if the CTabFolder should render itself in a simple, traditional style
3470  *
3471  * @exception SWTException <ul>
3472  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3473  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3474  * </ul>
3475  *
3476  * @since 3.0
3477  */
setSimple(boolean simple)3478 public void setSimple(boolean simple) {
3479 	checkWidget();
3480 	if (this.simple != simple) {
3481 		this.simple = simple;
3482 		updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3483 	}
3484 }
3485 /**
3486  * Sets the number of tabs that the CTabFolder should display
3487  *
3488  * @param single <code>true</code> if only the selected tab should be displayed otherwise, multiple tabs will be shown.
3489  *
3490  * @exception SWTException <ul>
3491  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3492  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3493  * </ul>
3494  *
3495  * @since 3.0
3496  */
setSingle(boolean single)3497 public void setSingle(boolean single) {
3498 	checkWidget();
3499 	if (this.single != single) {
3500 		this.single = single;
3501 		if (!single) {
3502 			for (int i = 0; i < items.length; i++) {
3503 				if (i != selectedIndex && items[i].closeImageState == SWT.NONE) {
3504 					items[i].closeImageState = SWT.BACKGROUND;
3505 				}
3506 			}
3507 		}
3508 		updateFolder(REDRAW);
3509 	}
3510 }
3511 
getControlY(Point size, Rectangle[] rects, int borderBottom, int borderTop, int i)3512 int getControlY(Point size, Rectangle[] rects, int borderBottom, int borderTop, int i) {
3513 	int center = fixedTabHeight != SWT.DEFAULT ? 0 : (tabHeight - rects[i].height)/2;
3514 	return onBottom ? size.y - borderBottom - tabHeight + center : 1 + borderTop + center;
3515 }
3516 
3517 /**
3518  * Specify a fixed height for the tab items.  If no height is specified,
3519  * the default height is the height of the text or the image, whichever
3520  * is greater. Specifying a height of -1 will revert to the default height.
3521  *
3522  * @param height the point value of the height or -1
3523  *
3524  * @exception SWTException <ul>
3525  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3526  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3527  *    <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
3528  * </ul>
3529  */
setTabHeight(int height)3530 public void setTabHeight(int height) {
3531 	checkWidget();
3532 	if (height < -1) {
3533 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3534 	}
3535 	fixedTabHeight = height;
3536 	updateFolder(UPDATE_TAB_HEIGHT);
3537 }
3538 /**
3539  * Specify whether the tabs should appear along the top of the folder
3540  * or along the bottom of the folder.
3541  *
3542  * @param position <code>SWT.TOP</code> for tabs along the top or <code>SWT.BOTTOM</code> for tabs along the bottom
3543  *
3544  * @exception SWTException <ul>
3545  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3546  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3547  *    <li>ERROR_INVALID_ARGUMENT - if the position value is not either SWT.TOP or SWT.BOTTOM</li>
3548  * </ul>
3549  *
3550  * @since 3.0
3551  */
setTabPosition(int position)3552 public void setTabPosition(int position) {
3553 	checkWidget();
3554 	if (position != SWT.TOP && position != SWT.BOTTOM) {
3555 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3556 	}
3557 	if (onBottom != (position == SWT.BOTTOM)) {
3558 		onBottom = position == SWT.BOTTOM;
3559 		updateFolder(REDRAW);
3560 	}
3561 }
3562 /**
3563  * Set the control that appears in the top right corner of the tab folder.
3564  * Typically this is a close button or a composite with a Menu and close button.
3565  * The topRight control is optional.  Setting the top right control to null will
3566  * remove it from the tab folder.
3567  *
3568  * @param control the control to be displayed in the top right corner or null
3569  *
3570  * @exception SWTException <ul>
3571  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3572  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3573  *    <li>ERROR_INVALID_ARGUMENT - if the control is disposed, or not a child of this CTabFolder</li>
3574  * </ul>
3575  *
3576  * @since 2.1
3577  */
setTopRight(Control control)3578 public void setTopRight(Control control) {
3579 	setTopRight(control, SWT.RIGHT);
3580 }
3581 /**
3582  * Set the control that appears in the top right corner of the tab folder.
3583  * Typically this is a close button or a composite with a Menu and close button.
3584  * The topRight control is optional.  Setting the top right control to null
3585  * will remove it from the tab folder.
3586  * <p>
3587  * The alignment parameter sets the layout of the control in the tab area.
3588  * <code>SWT.RIGHT</code> will cause the control to be positioned on the far
3589  * right of the folder and it will have its default size.  <code>SWT.FILL</code>
3590  * will size the control to fill all the available space to the right of the
3591  * last tab.  If there is no available space, the control will not be visible.
3592  * <code>SWT.RIGHT | SWT.WRAP</code> will allow the control to wrap below the
3593  * tabs if there is not enough available space to the right of the last tab.
3594  * </p>
3595  *
3596  * @param control the control to be displayed in the top right corner or null
3597  * @param alignment <code>SWT.RIGHT</code> or <code>SWT.FILL</code> or <code>SWT.RIGHT | SWT.WRAP</code>
3598  *
3599  * @exception SWTException <ul>
3600  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3601  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3602  *    <li>ERROR_INVALID_ARGUMENT - if the control is disposed, or not a child of this CTabFolder</li>
3603  * </ul>
3604  *
3605  * @since 3.0
3606  */
setTopRight(Control control, int alignment)3607 public void setTopRight(Control control, int alignment) {
3608 	checkWidget();
3609 	if (alignment != SWT.RIGHT && alignment != SWT.FILL && alignment != (SWT.RIGHT | SWT.WRAP)) {
3610 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3611 	}
3612 	if (control != null && (control.isDisposed() || control.getParent() != this)) {
3613 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3614 	}
3615 	if (topRight == control && topRightAlignment == alignment) return;
3616 	if (topRight != null && !topRight.isDisposed()) removeTabControl(topRight, false);
3617 	topRight = control;
3618 	topRightAlignment = alignment;
3619 	alignment &= ~SWT.RIGHT;
3620 	if (control != null) addTabControl(control, SWT.TRAIL | alignment, -1, false);
3621 	updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3622 }
3623 
3624 
3625 /**
3626  * Specify whether the close button appears
3627  * when the user hovers over an unselected tabs.
3628  *
3629  * @param visible <code>true</code> makes the close button appear
3630  *
3631  * @exception SWTException <ul>
3632  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3633  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3634  * </ul>
3635  *
3636  * @since 3.0
3637  */
setUnselectedCloseVisible(boolean visible)3638 public void setUnselectedCloseVisible(boolean visible) {
3639 	checkWidget();
3640 	if (showUnselectedClose == visible) return;
3641 	// display close button when mouse hovers
3642 	showUnselectedClose = visible;
3643 	updateFolder(REDRAW);
3644 }
3645 /**
3646  * Specify whether the image appears on unselected tabs.
3647  *
3648  * @param visible <code>true</code> makes the image appear
3649  *
3650  * @exception SWTException <ul>
3651  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3652  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3653  * </ul>
3654  *
3655  * @since 3.0
3656  */
setUnselectedImageVisible(boolean visible)3657 public void setUnselectedImageVisible(boolean visible) {
3658 	checkWidget();
3659 	if (showUnselectedImage == visible) return;
3660 	// display image on unselected items
3661 	showUnselectedImage = visible;
3662 	updateFolder(REDRAW);
3663 }
3664 /**
3665  * Shows the item.  If the item is already showing in the receiver,
3666  * this method simply returns.  Otherwise, the items are scrolled until
3667  * the item is visible.
3668  *
3669  * @param item the item to be shown
3670  *
3671  * @exception IllegalArgumentException <ul>
3672  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3673  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
3674  * </ul>
3675  * @exception SWTException <ul>
3676  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3677  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3678  * </ul>
3679  *
3680  * @see CTabFolder#showSelection()
3681  *
3682  * @since 2.0
3683  */
showItem(CTabItem item)3684 public void showItem (CTabItem item) {
3685 	checkWidget();
3686 	if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
3687 	if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3688 	int index = indexOf(item);
3689 	if (index == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3690 	int idx = -1;
3691 	for (int i = 0; i < priority.length; i++) {
3692 		if (priority[i] == index) {
3693 			idx = i;
3694 			break;
3695 		}
3696 	}
3697 	if (mru) {
3698 		// move to front of mru order
3699 		int[] newPriority = new int[priority.length];
3700 		System.arraycopy(priority, 0, newPriority, 1, idx);
3701 		System.arraycopy(priority, idx+1, newPriority, idx+1, priority.length - idx - 1);
3702 		newPriority[0] = index;
3703 		priority = newPriority;
3704 	}
3705 	if (item.showing) return;
3706 	updateFolder(REDRAW_TABS);
3707 }
showList(Rectangle rect)3708 void showList (Rectangle rect) {
3709 	if (items.length == 0 || !showChevron) return;
3710 	if (showMenu == null || showMenu.isDisposed()) {
3711 		showMenu = new Menu(getShell(), getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT));
3712 	} else {
3713 		for (MenuItem item : showMenu.getItems()) {
3714 			item.dispose();
3715 		}
3716 	}
3717 	final String id = "CTabFolder_showList_Index"; //$NON-NLS-1$
3718 	for (CTabItem tab : items) {
3719 		if (tab.showing) continue;
3720 		MenuItem item = new MenuItem(showMenu, SWT.NONE);
3721 		// 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.
3722 		item.setText(tab.getText().replace("\n", " "));
3723 		item.setImage(tab.getImage());
3724 		item.setData(id, tab);
3725 		item.addSelectionListener(new SelectionAdapter() {
3726 			@Override
3727 			public void widgetSelected(SelectionEvent e) {
3728 				MenuItem menuItem = (MenuItem)e.widget;
3729 				int index = indexOf((CTabItem)menuItem.getData(id));
3730 				CTabFolder.this.setSelection(index, true);
3731 			}
3732 		});
3733 	}
3734 	int x = rect.x;
3735 	int y = rect.y + rect.height;
3736 	Point location = getDisplay().map(this, null, x, y);
3737 	showMenu.setLocation(location.x, location.y);
3738 	showMenu.setVisible(true);
3739 }
3740 /**
3741  * Shows the selection.  If the selection is already showing in the receiver,
3742  * this method simply returns.  Otherwise, the items are scrolled until
3743  * the selection is visible.
3744  *
3745  * @exception SWTException <ul>
3746  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3747  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3748  * </ul>
3749  *
3750  * @see CTabFolder#showItem(CTabItem)
3751  *
3752  * @since 2.0
3753  */
showSelection()3754 public void showSelection () {
3755 	checkWidget ();
3756 	if (selectedIndex != -1) {
3757 		showItem(getSelection());
3758 	}
3759 }
3760 
_setToolTipText(int x, int y)3761 void _setToolTipText (int x, int y) {
3762 	String oldTip = getToolTipText();
3763 	String newTip = _getToolTip(x, y);
3764 	if (newTip == null || !newTip.equals(oldTip)) {
3765 		setToolTipText(newTip);
3766 	}
3767 }
3768 
updateItems()3769 boolean updateItems() {
3770 	return updateItems(selectedIndex);
3771 }
3772 
updateItems(int showIndex)3773 boolean updateItems (int showIndex) {
3774 	GC gc = new GC(this);
3775 	if (!single && !mru && showIndex != -1) {
3776 		// make sure selected item will be showing
3777 		int firstIndex = showIndex;
3778 		if (priority[0] < showIndex) {
3779 			int maxWidth = getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
3780 			int width = 0;
3781 			int[] widths = new int[items.length];
3782 			for (int i = priority[0]; i <= showIndex; i++) {
3783 				int state = CTabFolderRenderer.MINIMUM_SIZE;
3784 				if (i == selectedIndex) state |= SWT.SELECTED;
3785 				widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3786 				width += widths[i];
3787 				if (width > maxWidth) break;
3788 			}
3789 			if (width > maxWidth) {
3790 				width = 0;
3791 				for (int i = showIndex; i >= 0; i--) {
3792 					int state = CTabFolderRenderer.MINIMUM_SIZE;
3793 					if (i == selectedIndex) state |= SWT.SELECTED;
3794 					if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3795 					width += widths[i];
3796 					if (width > maxWidth) break;
3797 					firstIndex = i;
3798 				}
3799 			} else {
3800 				firstIndex = priority[0];
3801 				for (int i = showIndex + 1; i < items.length; i++) {
3802 					int state = CTabFolderRenderer.MINIMUM_SIZE;
3803 					if (i == selectedIndex) state |= SWT.SELECTED;
3804 					widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3805 					width += widths[i];
3806 					if (width >= maxWidth) break;
3807 				}
3808 				if (width < maxWidth) {
3809 					for (int i = priority[0] - 1; i >= 0; i--) {
3810 						int state = CTabFolderRenderer.MINIMUM_SIZE;
3811 						if (i == selectedIndex) state |= SWT.SELECTED;
3812 						if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3813 						width += widths[i];
3814 						if (width > maxWidth) break;
3815 						firstIndex = i;
3816 					}
3817 				}
3818 			}
3819 
3820 		}
3821 		if (firstIndex != priority[0]) {
3822 			int index = 0;
3823 			// enumerate tabs from first visible to the last existing one (sorted ascending)
3824 			for (int i = firstIndex; i < items.length; i++) {
3825 				priority[index++] = i;
3826 			}
3827 			// enumerate hidden tabs on the left hand from first visible one
3828 			// in the inverse order (sorted descending) so that the originally
3829 			// first opened tab is always at the end of the list
3830 			for (int i = firstIndex - 1; i >= 0; i--) {
3831 				priority[index++] = i;
3832 			}
3833 		}
3834 	}
3835 
3836 	boolean oldShowChevron = showChevron;
3837 	boolean changed = setItemSize(gc);
3838 	updateButtons();
3839 	boolean chevronChanged = showChevron != oldShowChevron;
3840 	if (chevronChanged) {
3841 		if (updateTabHeight(false)) {
3842 			// Tab height has changed. Item sizes have to be set again.
3843 			changed |= setItemSize(gc);
3844 		}
3845 	}
3846 	changed |= setItemLocation(gc);
3847 	setButtonBounds();
3848 	changed |= chevronChanged;
3849 	if (changed && getToolTipText() != null) {
3850 		Point pt = getDisplay().getCursorLocation();
3851 		pt = toControl(pt);
3852 		_setToolTipText(pt.x, pt.y);
3853 	}
3854 	gc.dispose();
3855 	return changed;
3856 }
updateTabHeight(boolean force)3857 boolean updateTabHeight(boolean force){
3858 	int oldHeight = tabHeight;
3859 	GC gc = new GC(this);
3860 	tabHeight = renderer.computeSize(CTabFolderRenderer.PART_HEADER, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT).y;
3861 	gc.dispose();
3862 	if (fixedTabHeight == SWT.DEFAULT && controls != null && controls.length > 0) {
3863 		for (int i = 0; i < controls.length; i++) {
3864 			if ((controlAlignments[i] & SWT.WRAP) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
3865 				int topHeight = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
3866 				topHeight +=  renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0,0,0,0).height + 1;
3867 				tabHeight = Math.max(topHeight, tabHeight);
3868 			}
3869 		}
3870 	}
3871 	if (!force && tabHeight == oldHeight) return false;
3872 	oldSize = null;
3873 	return true;
3874 }
3875 
updateFolder(int flags)3876 void updateFolder (int flags) {
3877 	updateFlags |= flags;
3878 	if (updateRun != null) return;
3879 	updateRun = () -> {
3880 		updateRun = null;
3881 		if (isDisposed()) return;
3882 		runUpdate();
3883 	};
3884 	getDisplay().asyncExec(updateRun);
3885 }
3886 
runUpdate()3887 void runUpdate() {
3888 	if (updateFlags == 0) return;
3889 	int flags = updateFlags;
3890 	updateFlags = 0;
3891 	Rectangle rectBefore = getClientArea();
3892 	updateButtons();
3893 	updateTabHeight(false);
3894 	updateItems(selectedIndex);
3895 	if ((flags & REDRAW) != 0) {
3896 		redraw();
3897 	} else if ((flags & REDRAW_TABS) != 0) {
3898 		redrawTabs();
3899 	}
3900 	Rectangle rectAfter = getClientArea();
3901 	if (!rectBefore.equals(rectAfter)) {
3902 		notifyListeners(SWT.Resize, new Event());
3903 		layout();
3904 	}
3905 }
3906 
updateBkImages()3907 void updateBkImages() {
3908 	if (controls != null && controls.length > 0) {
3909 		for (int i = 0; i < controls.length; i++) {
3910 			Control control = controls[i];
3911 			if (!control.isDisposed()) {
3912 				if (hovering) {
3913 					if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
3914 					control.setBackgroundImage(null);
3915 					control.setBackground(getBackground());
3916 				} else {
3917 					if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_DEFAULT);
3918 					Rectangle bounds = control.getBounds();
3919 					int tabHeight = getTabHeight();
3920 					int height = this.getSize().y;
3921 					boolean wrapped = onBottom ? bounds.y + bounds.height < height - tabHeight : bounds.y > tabHeight;
3922 					if (wrapped || gradientColors == null) {
3923 						control.setBackgroundImage(null);
3924 						control.setBackground(getBackground());
3925 					} else {
3926 						bounds.width = 10;
3927 						if (!onBottom) {
3928 							bounds.y = -bounds.y;
3929 							bounds.height -= 2*bounds.y - 1;
3930 						} else {
3931 							bounds.height += height - (bounds.y + bounds.height);
3932 							bounds.y = -1;
3933 						}
3934 						bounds.x = 0;
3935 						if (controlBkImages[i] != null) controlBkImages[i].dispose();
3936 						controlBkImages[i] = new Image(control.getDisplay(), bounds);
3937 						GC gc = new GC(controlBkImages[i]);
3938 						renderer.draw(CTabFolderRenderer.PART_BACKGROUND, 0, bounds, gc);
3939 						gc.dispose();
3940 						control.setBackground(null);
3941 						control.setBackgroundImage(controlBkImages[i]);
3942 					}
3943 				}
3944 			}
3945 		}
3946 
3947 	}
3948 }
_getToolTip(int x, int y)3949 String _getToolTip(int x, int y) {
3950 	CTabItem item = getItem(new Point (x, y));
3951 	if (item == null) return null;
3952 	if (!item.showing) return null;
3953 	if ((showClose || item.showClose) && item.closeRect.contains(x, y)) {
3954 		return SWT.getMessage("SWT_Close"); //$NON-NLS-1$
3955 	}
3956 	return item.getToolTipText();
3957 }
3958 /**
3959 * Set a control that can appear to the left or to the right of the folder tabs.
3960 * This method can also be used instead of #setTopRight(Control). To remove a tab
3961 * control, see#removeTabControl(Control);
3962 * <p>
3963 * The flags parameter sets the layout of the control in the tab area.
3964 * <code>SWT.LEAD</code> will cause the control to be positioned on the left
3965 * of the tabs. <code>SWT.TRAIL</code> will cause the control to be positioned on
3966 * the far right of the folder and it will have its default size. <code>SWT.TRAIL</code>
3967 * can be combined with <code>SWT.FILL</code>to fill all the available space to the
3968 * right of the last tab. <code>SWT.WRAP</code> can also be added to <code>SWT.TRAIL</code>
3969 * only to cause a control to wrap if there is not enough space to display it in its
3970 * entirety.
3971 * </p>
3972 * @param control the control to be displayed in the top right corner or null
3973 *
3974 * @param flags valid combinations are:
3975 * <ul><li>SWT.LEAD
3976 * <li> SWT.TRAIL (| SWT.FILL | SWT.WRAP)
3977 * </ul>
3978 * @exception SWTException <ul>
3979 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3980 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3981 *    <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
3982 * </ul>
3983 */
addTabControl(Control control, int flags)3984 /*public*/ void addTabControl(Control control, int flags) {
3985 	checkWidget();
3986 	addTabControl(control, flags, -1, true);
3987 }
3988 
addTabControl(Control control, int flags, int index, boolean update)3989 void addTabControl(Control control, int flags, int index, boolean update) {
3990 	switch (flags) {
3991 		case SWT.TRAIL:
3992 		case SWT.TRAIL | SWT.WRAP:
3993 		case SWT.TRAIL | SWT.FILL:
3994 		case SWT.TRAIL | SWT.FILL | SWT.WRAP:
3995 		case SWT.LEAD:
3996 			break;
3997 		default:
3998 			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3999 			break;
4000 	}
4001 	if (control != null && control.getParent() != this) {
4002 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4003 	}
4004 	//check for duplicates
4005 	for (Control ctrl : controls) {
4006 		if (ctrl == control) {
4007 			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4008 		}
4009 	}
4010 	int length = controls.length;
4011 
4012 	control.addListener(SWT.Resize, listener);
4013 
4014 	//Grow all 4 arrays
4015 	Control[] newControls = new Control [length + 1];
4016 	System.arraycopy(controls, 0, newControls, 0, length);
4017 	controls = newControls;
4018 	int[] newAlignment = new int [length + 1];
4019 	System.arraycopy(controlAlignments, 0, newAlignment, 0, length);
4020 	controlAlignments = newAlignment;
4021 	Rectangle[] newRect = new Rectangle [length + 1];
4022 	System.arraycopy(controlRects, 0, newRect, 0, length);
4023 	controlRects = newRect;
4024 	Image[] newImage = new Image [length + 1];
4025 	System.arraycopy(controlBkImages, 0, newImage, 0, length);
4026 	controlBkImages = newImage;
4027 	if (index == -1) {
4028 		index = length;
4029 		if (chevronTb != null && control != chevronTb) index--;
4030 	}
4031 	System.arraycopy (controls, index, controls, index + 1, length - index);
4032 	System.arraycopy (controlAlignments, index, controlAlignments, index + 1, length - index);
4033 	System.arraycopy (controlRects, index, controlRects, index + 1, length - index);
4034 	System.arraycopy (controlBkImages, index, controlBkImages, index + 1, length - index);
4035 	controls[index] = control;
4036 	controlAlignments[index] = flags;
4037 	controlRects[index] = new Rectangle(0, 0, 0, 0);
4038 	if (update) {
4039 		updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4040 	}
4041 }
4042 
4043 /**
4044 * Removes the control from the list of tab controls.
4045 *
4046 * @param control the control to be removed
4047 *
4048 * @exception SWTException <ul>
4049 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4050 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4051 *    <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
4052 * </ul>
4053 */
removeTabControl(Control control)4054 /*public*/ void removeTabControl (Control control) {
4055 	checkWidget();
4056 	removeTabControl (control, true);
4057 }
4058 
removeTabControl(Control control, boolean update)4059 void removeTabControl (Control control, boolean update) {
4060 	if (control != null && control.getParent() != this) {
4061 		SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4062 	}
4063 	int index = -1;
4064 	for (int i = 0; i < controls.length; i++) {
4065 		if (controls[i] == control){
4066 			index = i;
4067 			break;
4068 		}
4069 	}
4070 	if (index == -1) return;
4071 
4072 	if (!control.isDisposed()) {
4073 		control.removeListener(SWT.Resize, listener);
4074 		control.setBackground (null);
4075 		control.setBackgroundImage (null);
4076 		if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
4077 	}
4078 
4079 	if (controlBkImages[index] != null && !controlBkImages[index].isDisposed()) controlBkImages[index].dispose();
4080 	if (controls.length == 1) {
4081 		controls = new Control[0];
4082 		controlAlignments = new int[0];
4083 		controlRects = new Rectangle[0];
4084 		controlBkImages = new Image[0];
4085 	} else {
4086 		Control[] newControls = new Control [controls.length - 1];
4087 		System.arraycopy(controls, 0, newControls, 0, index);
4088 		System.arraycopy(controls, index + 1, newControls, index, controls.length - index - 1);
4089 		controls = newControls;
4090 
4091 		int[] newAlignments = new int [controls.length];
4092 		System.arraycopy(controlAlignments, 0, newAlignments, 0, index);
4093 		System.arraycopy(controlAlignments, index + 1, newAlignments, index, controls.length - index);
4094 		controlAlignments = newAlignments;
4095 
4096 		Rectangle[] newRects = new Rectangle [controls.length];
4097 		System.arraycopy(controlRects, 0, newRects, 0, index);
4098 		System.arraycopy(controlRects, index + 1, newRects, index, controls.length - index);
4099 		controlRects = newRects;
4100 
4101 		Image[] newBkImages = new Image [controls.length];
4102 		System.arraycopy(controlBkImages, 0, newBkImages, 0, index);
4103 		System.arraycopy(controlBkImages, index + 1, newBkImages, index, controls.length - index);
4104 		controlBkImages = newBkImages;
4105 	}
4106 	if (update) {
4107 		updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4108 	}
4109 }
4110 
getWrappedHeight(Point size)4111 int getWrappedHeight (Point size) {
4112 	boolean[][] positions = new boolean[1][];
4113 	Rectangle[] rects = computeControlBounds(size, positions);
4114 	int minY = Integer.MAX_VALUE, maxY = 0, wrapHeight = 0;
4115 	for (int i = 0; i < rects.length; i++) {
4116 		if (positions[0][i]) {
4117 			minY = Math.min(minY, rects[i].y);
4118 			maxY = Math.max(maxY, rects[i].y + rects[i].height);
4119 			wrapHeight = maxY - minY;
4120 		}
4121 	}
4122 	return wrapHeight;
4123 }
4124 
4125 /**
4126  * Sets whether a chevron is shown when there are more items to be displayed.
4127  *
4128  * @exception IllegalArgumentException <ul>
4129  *    <li>ERROR_INVALID_RANGE - if the index is out of range</li>
4130  * </ul>
4131  * @exception SWTException <ul>
4132  *    <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
4133  *    <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
4134  * </ul>
4135  *
4136  */
setChevronVisible(boolean visible)4137 /*public*/ void setChevronVisible(boolean visible) {
4138 	checkWidget();
4139 	if (chevronVisible == visible) return;
4140 	chevronVisible = visible;
4141 	updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4142 }
4143 
shouldHighlight()4144 	boolean shouldHighlight() {
4145 		return this.highlight && highlightEnabled;
4146 	}
4147 
4148 /**
4149  * Sets whether the selected tab is rendered as highlighted.
4150  *
4151  * @param enabled
4152  *            {@code true} if the selected tab should be highlighted,
4153  *            {@code false} otherwise.
4154  * @exception SWTException
4155  *                <ul>
4156  *                <li>ERROR_WIDGET_DISPOSED - if the receiver has been
4157  *                disposed</li>
4158  *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
4159  *                thread that created the receiver</li>
4160  *                </ul>
4161  * @since 3.106
4162  */
setHighlightEnabled(boolean enabled)4163 public void setHighlightEnabled(boolean enabled) {
4164 	checkWidget();
4165 	if (highlightEnabled == enabled) {
4166 		return;
4167 	}
4168 	highlightEnabled = enabled;
4169 	updateFolder(REDRAW);
4170 }
4171 
4172 /**
4173  * Returns <code>true</code> if the selected tab is rendered as
4174  * highlighted.
4175  *
4176  * @return <code>true</code> if the selected tab is rendered as
4177  *         highlighted
4178  *
4179  * @exception SWTException
4180  *                <ul>
4181  *                <li>ERROR_WIDGET_DISPOSED - if the receiver has been
4182  *                disposed</li>
4183  *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
4184  *                thread that created the receiver</li>
4185  *                </ul>
4186  * @since 3.106
4187  */
getHighlightEnabled()4188 public boolean getHighlightEnabled() {
4189 	checkWidget();
4190 	return highlightEnabled;
4191 }
4192 }
4193