1 /*******************************************************************************
2 * Copyright (c) 2000, 2018 IBM Corporation and others.
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/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.custom;
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.*;
26 * Instances of this class implement the notebook user interface
27 * metaphor. It allows the user to select a notebook page from
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>.
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.
39 * <dt><b>Styles:</b></dt>
40 * <dd>CLOSE, TOP, BOTTOM, FLAT, BORDER, SINGLE, MULTI</dd>
41 * <dt><b>Events:</b></dt>
43 * <dd>"CTabFolder2"</dd>
45 * Note: Only one of the styles TOP and BOTTOM
48 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
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.
57 public class CTabFolder extends Composite {
60 * marginWidth specifies the number of points of horizontal margin
61 * that will be placed along the left and right edges of the form.
63 * The default value is 0.
65 public int marginWidth = 0;
67 * marginHeight specifies the number of points of vertical margin
68 * that will be placed along the top and bottom edges of the form.
70 * The default value is 0.
72 public int marginHeight = 0;
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.
78 * NOTE This field is badly named and can not be fixed for backwards compatibility.
79 * It should not be capitalized.
81 * @deprecated This field is no longer used. See setMinimumCharacters(int)
84 public int MIN_TAB_WIDTH = 4;
87 * Color of innermost line of drop shadow border.
89 * NOTE This field is badly named and can not be fixed for backwards compatibility.
90 * It should be capitalized.
92 * @deprecated drop shadow border is no longer drawn in 3.0
95 public static RGB borderInsideRGB = new RGB (132, 130, 132);
97 * Color of middle line of drop shadow border.
99 * NOTE This field is badly named and can not be fixed for backwards compatibility.
100 * It should be capitalized.
102 * @deprecated drop shadow border is no longer drawn in 3.0
105 public static RGB borderMiddleRGB = new RGB (143, 141, 138);
107 * Color of outermost line of drop shadow border.
109 * NOTE This field is badly named and can not be fixed for backwards compatibility.
110 * It should be capitalized.
112 * @deprecated drop shadow border is no longer drawn in 3.0
115 public static RGB borderOutsideRGB = new RGB (171, 168, 165);
117 /* sizing, positioning */
118 boolean onBottom = false;
119 boolean single = false;
120 boolean simple = true;
121 int fixedTabHeight = SWT.DEFAULT;
124 boolean borderVisible = false;
126 /* item management */
127 CTabFolderRenderer renderer;
128 CTabItem items[] = new CTabItem[0];
129 /** index of the left most visible tab. */
131 int selectedIndex = -1;
134 * Indices of the elements in the {@link #items} array, used to manage tab
135 * visibility and candidates to be hidden/shown next.
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!) => the right most elements have the highest priority
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!) => the left most elements
145 * have the highest priority to be shown.
147 * The update strategy of the {@link #priority} array however depends on the
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
154 * Example: [4,2,5,1,3,0], just representing the last selection order.
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.
162 * The tabs between first index and the index of the currently selected tab
163 * are always visible.
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.
171 * @see #updateItems(int)
172 * @see #setItemLocation(GC)
174 int[] priority = new int[0];
177 boolean ignoreTraverse;
178 boolean useDefaultRenderer;
180 /* External Listener management */
181 CTabFolder2Listener[] folderListeners = new CTabFolder2Listener[0];
182 // support for deprecated listener mechanism
183 CTabFolderListener[] tabListeners = new CTabFolderListener[0];
185 /* Selected item appearance */
186 Image selectionBgImage;
187 Color[] selectionGradientColors;
188 int[] selectionGradientPercents;
189 boolean selectionGradientVertical;
190 Color selectionForeground;
191 Color selectionBackground;
193 /* Unselected item appearance */
194 Color[] gradientColors;
195 int[] gradientPercents;
196 boolean gradientVertical;
197 boolean showUnselectedImage = true;
199 // close, min/max and chevron buttons
200 boolean showClose = false;
201 boolean showUnselectedClose = true;
203 boolean showMin = false;
204 boolean minimized = false;
205 boolean showMax = false;
206 boolean maximized = false;
213 Rectangle hoverRect = new Rectangle(0,0,0,0);
215 boolean hoverTimerRunning;
217 boolean highlightEnabled = true;
219 boolean showChevron = false;
222 ToolItem chevronItem;
224 boolean chevronVisible = true;
228 int topRightAlignment = SWT.RIGHT;
229 boolean ignoreResize;
231 int[] controlAlignments;
232 Rectangle[] controlRects;
233 Image[] controlBkImages;
236 final static int REDRAW = 1 << 1;
237 final static int REDRAW_TABS = 1 << 2;
238 final static int UPDATE_TAB_HEIGHT = 1 << 3;
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;
245 // keep track of size changes in order to redraw only affected area
250 // internal constants
251 static final int DEFAULT_WIDTH = 64;
252 static final int DEFAULT_HEIGHT = 64;
254 static final int SELECTION_FOREGROUND = SWT.COLOR_LIST_FOREGROUND;
255 static final int SELECTION_BACKGROUND = SWT.COLOR_LIST_BACKGROUND;
257 static final int FOREGROUND = SWT.COLOR_WIDGET_FOREGROUND;
258 static final int BACKGROUND = SWT.COLOR_WIDGET_BACKGROUND;
260 //TODO: add setter for spacing?
261 static final int SPACING = 3;
263 * Constructs a new instance of this class given its parent
264 * and a style value describing its behavior and appearance.
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.
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
278 * @exception IllegalArgumentException <ul>
279 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
281 * @exception SWTException <ul>
282 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
293 public CTabFolder(Composite parent, int style) {
294 super(parent, checkStyle (parent, style));
298 void init(int style) {
299 super.setLayout(new CTabFolderLayout());
300 int style2 = super.getStyle();
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);
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;
345 int[] folderEvents = new int[]{
352 SWT.MouseDoubleClick,
365 for (int i = 0; i < folderEvents.length; i++) {
366 addListener(folderEvents[i], listener);
371 void onDeactivate(Event event) {
372 if (!highlightEnabled) {
375 this.highlight = false;
379 void onActivate(Event event) {
380 if (!highlightEnabled) {
383 this.highlight = true;
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;
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.
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;
409 return style | SWT.DOUBLE_BUFFERED;
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
419 * @param listener the listener which should be notified
421 * @exception IllegalArgumentException <ul>
422 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
430 * @see CTabFolder2Listener
431 * @see #removeCTabFolder2Listener(CTabFolder2Listener)
435 public void addCTabFolder2Listener(CTabFolder2Listener listener) {
437 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
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;
445 * Adds the listener to the collection of listeners who will
446 * be notified when a tab item is closed.
448 * @param listener the listener which should be notified
450 * @exception IllegalArgumentException <ul>
451 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
458 * @see CTabFolderListener
459 * @see #removeCTabFolderListener(CTabFolderListener)
461 * @deprecated use addCTabFolder2Listener(CTabFolder2Listener)
464 public void addCTabFolderListener(CTabFolderListener listener) {
466 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
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
475 updateFolder(REDRAW);
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>
484 * <code>widgetSelected</code> is called when the user changes the selected tab.
485 * <code>widgetDefaultSelected</code> is not called.
488 * @param listener the listener which should be notified when the user changes the receiver's selection
490 * @exception IllegalArgumentException <ul>
491 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
498 * @see SelectionListener
499 * @see #removeSelectionListener
500 * @see SelectionEvent
502 public void addSelectionListener(SelectionListener listener) {
504 if (listener == null) {
505 SWT.error(SWT.ERROR_NULL_ARGUMENT);
507 TypedListener typedListener = new TypedListener(listener);
508 addListener(SWT.Selection, typedListener);
509 addListener(SWT.DefaultSelection, typedListener);
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);
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;
524 Point[] tabControlSize = new Point[controls.length];
525 boolean[] overflow = new boolean [controls.length];
528 int x = borderLeft + SPACING;
531 for (int i = 0; i < controls.length; i++) {
532 Point ctrlSize = tabControlSize[i] = !controls[i].isDisposed() && controls[i].getVisible() ? controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT) : new Point(0,0);
533 int alignment = controlAlignments[i];
534 if ((alignment & SWT.LEAD) != 0) {
535 rects[i].width = ctrlSize.x;
536 rects[i].height = getControlHeight(ctrlSize);
538 rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
540 leftWidth += ctrlSize.x;
542 if ((alignment & (SWT.FILL | SWT.WRAP)) == 0) {
543 rightWidth += ctrlSize.x;
545 allWidth += ctrlSize.x;
548 if (leftWidth > 0) leftWidth += SPACING * 2;
551 for (int i = 0; i < items.length; i++) {
552 if (items[i].showing) itemWidth += items[i].width;
555 int maxWidth = size.x - borderLeft - leftWidth - borderRight;
556 int availableWidth = Math.max(0, maxWidth - itemWidth - rightWidth);
557 if (rightWidth > 0) availableWidth -= SPACING * 2;
558 x = size.x - borderRight - SPACING;
559 if (itemWidth + allWidth <= maxWidth) {
561 for (int i = 0; i < controls.length; i++) {
562 int alignment = controlAlignments[i];
563 if ((alignment & SWT.TRAIL) != 0) {
564 Point ctrlSize = tabControlSize[i];
566 rects[i].width = ctrlSize.x;
567 rects[i].height = getControlHeight(ctrlSize);
569 rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
570 if ((alignment & (SWT.FILL | SWT.WRAP)) != 0) availableWidth -= ctrlSize.x;
572 if (tabControlSize[i].y >= tabHeight && fixedTabHeight == SWT.DEFAULT) {
577 for (int i = 0; i < controls.length; i++) {
578 int alignment = controlAlignments[i];
579 Point ctrlSize = tabControlSize[i];
580 if ((alignment & SWT.TRAIL) != 0) {
581 if ((alignment & (SWT.FILL | SWT.WRAP)) == 0) {
583 rects[i].width = ctrlSize.x;
584 rects[i].height = getControlHeight(ctrlSize);
586 rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
587 } else if (((alignment & (SWT.WRAP)) != 0 && ctrlSize.x < availableWidth)) {
589 rects[i].width = ctrlSize.x;
590 rects[i].height = getControlHeight(ctrlSize);
592 rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
593 availableWidth -= ctrlSize.x;
594 } else if ((alignment & (SWT.FILL)) != 0 && (alignment & (SWT.WRAP)) == 0) {
596 rects[i].height = getControlHeight(ctrlSize);
598 rects[i].y = getControlY(size, rects, borderBottom, borderTop, i);
600 if ((alignment & (SWT.WRAP)) != 0) {
608 //Any space, distribute amongst FILL
609 if (availableWidth > 0) {
611 for (int i = 0; i < controls.length; i++) {
612 int alignment = controlAlignments[i];
613 if ((alignment & SWT.TRAIL) != 0 && (alignment & SWT.FILL) != 0 && !overflow[i]) {
617 if (fillCount != 0) {
618 int extraSpace = availableWidth/fillCount;
620 for (int i = 0; i < controls.length; i++) {
621 int alignment = controlAlignments[i];
622 if ((alignment & SWT.TRAIL) != 0) {
623 if ((alignment & SWT.FILL) != 0 && !overflow[i]) {
624 rects[i].width += extraSpace;
625 addedSpace += extraSpace;
628 rects[i].x -= addedSpace;
635 //Go through overflow laying out all wrapped controls
636 Rectangle bodyTrim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
637 int bodyRight = bodyTrim.width + bodyTrim.x;
638 int bodyLeft = -bodyTrim.x;
639 int bodyWidth = size.x - bodyLeft - bodyRight;
640 x = size.x - bodyRight;
641 int y = onBottom ? this.getSize().y - getTabHeight() + 2*bodyTrim.y : -bodyTrim.y;
642 availableWidth = bodyWidth;
644 for (int i = 0; i < controls.length; i++) {
645 Point ctrlSize = tabControlSize[i];
647 if (availableWidth > ctrlSize.x) {
649 rects[i].width = ctrlSize.x;
650 rects[i].y = onBottom ? y - ctrlSize.y : y;
651 rects[i].height = ctrlSize.y;
653 availableWidth -= ctrlSize.x;
654 maxHeight = Math.max(maxHeight, ctrlSize.y);
656 x = size.x - bodyRight;
659 availableWidth = bodyWidth;
660 if (availableWidth > ctrlSize.x) {
661 //Relayout this control in the next line
664 ctrlSize = controls[i].isDisposed() ? new Point(0,0) : controls[i].computeSize(bodyWidth, SWT.DEFAULT);
665 rects[i].width = bodyWidth;
666 rects[i].y = onBottom ? y - ctrlSize.y : y;
667 rects[i].height = ctrlSize.y;
668 rects[i].x = size.x - ctrlSize.x - bodyRight;
676 int i = 0, lastIndex = -1;
677 while (i < priority.length && items[priority[i]].showing) {
678 lastIndex = Math.max(lastIndex, priority[i++]);
680 if (lastIndex == -1) lastIndex = selectedIndex;
681 if (lastIndex != -1) {
682 CTabItem lastItem = items[lastIndex];
683 int w = lastItem.x + lastItem.width + SPACING;
684 if (!simple && lastIndex == selectedIndex) w -= (renderer.curveIndent - 7);
685 rects[controls.length - 1].x = w;
689 if (position != null) position[0] = overflow;
693 int getControlHeight(Point ctrlSize) {
694 return fixedTabHeight == SWT.DEFAULT ? Math.max(tabHeight - 1, ctrlSize.y) : ctrlSize.y;
697 * This class was not intended to be subclassed but this restriction
698 * cannot be enforced without breaking backward compatibility.
700 //protected void checkSubclass () {
701 // String name = getClass ().getName ();
702 // int index = name.lastIndexOf ('.');
703 // if (!name.substring (0, index + 1).equals ("org.eclipse.swt.custom.")) {
704 // SWT.error (SWT.ERROR_INVALID_SUBCLASS);
708 public Rectangle computeTrim (int x, int y, int width, int height) {
710 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, x, y, width, height);
711 Point size = new Point(width, height);
712 int wrapHeight = getWrappedHeight(size);
714 trim.height += wrapHeight;
716 trim.y -= wrapHeight;
717 trim.height += wrapHeight;
721 Image createButtonImage(Display display, int button) {
722 GC tempGC = new GC (this);
723 Point size = renderer.computeSize(button, SWT.NONE, tempGC, SWT.DEFAULT, SWT.DEFAULT);
726 Rectangle trim = renderer.computeTrim(button, SWT.NONE, 0, 0, 0, 0);
727 Image image = new Image (display, size.x - trim.width, size.y - trim.height);
728 GC gc = new GC (image);
729 Color transColor = renderer.parent.getBackground();
730 gc.setBackground(transColor);
731 gc.fillRectangle(image.getBounds());
732 renderer.draw(button, SWT.NONE, new Rectangle(trim.x, trim.y, size.x, size.y), gc);
735 final ImageData imageData = image.getImageData (DPIUtil.getDeviceZoom ());
736 imageData.transparentPixel = imageData.palette.getPixel(transColor.getRGB());
738 image = new Image(display, new AutoScaleImageDataProvider(display, imageData, DPIUtil.getDeviceZoom()));
741 void createItem (CTabItem item, int index) {
742 if (0 > index || index > getItemCount ())SWT.error (SWT.ERROR_INVALID_RANGE);
744 CTabItem[] newItems = new CTabItem [items.length + 1];
745 System.arraycopy(items, 0, newItems, 0, index);
746 newItems[index] = item;
747 System.arraycopy(items, index, newItems, index + 1, items.length - index);
749 if (selectedIndex >= index) selectedIndex ++;
750 int[] newPriority = new int[priority.length + 1];
751 int next = 0, priorityIndex = priority.length;
752 for (int i = 0; i < priority.length; i++) {
753 if (!mru && priority[i] == index) {
754 priorityIndex = next++;
756 newPriority[next++] = priority[i] >= index ? priority[i] + 1 : priority[i];
758 newPriority[priorityIndex] = index;
759 priority = newPriority;
761 if (items.length == 1) {
762 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
764 updateFolder(REDRAW_TABS);
767 void destroyItem (CTabItem item) {
768 if (inDispose) return;
769 int index = indexOf(item);
770 if (index == -1) return;
772 if (items.length == 1) {
773 items = new CTabItem[0];
774 priority = new int[0];
778 Control control = item.control;
779 if (control != null && !control.isDisposed()) {
780 control.setVisible(false);
782 setToolTipText(null);
783 GC gc = new GC(this);
790 CTabItem[] newItems = new CTabItem [items.length - 1];
791 System.arraycopy(items, 0, newItems, 0, index);
792 System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
795 int[] newPriority = new int[priority.length - 1];
797 for (int i = 0; i < priority.length; i++) {
798 if (priority [i] == index) continue;
799 newPriority[next++] = priority[i] > index ? priority[i] - 1 : priority [i];
801 priority = newPriority;
803 // move the selection if this item is selected
804 if (selectedIndex == index) {
805 Control control = item.getControl();
807 int nextSelection = mru ? priority[0] : Math.max(0, index - 1);
808 setSelection(nextSelection, true);
809 if (control != null && !control.isDisposed()) {
810 control.setVisible(false);
812 } else if (selectedIndex > index) {
817 updateFolder(UPDATE_TAB_HEIGHT | REDRAW_TABS);
821 * Returns <code>true</code> if the receiver's border is visible.
823 * @return the receiver's border visibility state
825 * @exception SWTException <ul>
826 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
827 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
832 public boolean getBorderVisible() {
834 return borderVisible;
836 ToolBar getChevron() {
837 if (chevronTb == null) {
838 chevronTb = new ToolBar(this, SWT.FLAT);
839 initAccessibleChevronTb();
840 addTabControl(chevronTb, SWT.TRAIL, -1, false);
842 if (chevronItem == null) {
843 chevronItem = new ToolItem(chevronTb, SWT.PUSH);
844 chevronItem.setToolTipText(SWT.getMessage("SWT_ShowList"));
845 chevronItem.addListener(SWT.Selection, listener);
850 * Returns <code>true</code> if the chevron button
851 * is visible when necessary.
853 * @return the visibility of the chevron button
855 * @exception SWTException <ul>
856 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
857 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
861 /*public*/ boolean getChevronVisible() {
863 return chevronVisible;
866 public Rectangle getClientArea() {
868 //TODO: HACK - find a better way to get padding
869 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.FILL, 0, 0, 0, 0);
870 Point size = getSize();
871 int wrapHeight = getWrappedHeight(size);
873 trim.height += wrapHeight;
875 trim.y -= wrapHeight;
876 trim.height += wrapHeight;
878 if (minimized) return new Rectangle(-trim.x, -trim.y, 0, 0);
879 int width = size.x - trim.width;
880 int height = size.y - trim.height;
881 return new Rectangle(-trim.x, -trim.y, width, height);
885 * Return the tab that is located at the specified index.
887 * @param index the index of the tab item
888 * @return the item at the specified index
890 * @exception IllegalArgumentException <ul>
891 * <li>ERROR_INVALID_RANGE - if the index is out of range</li>
893 * @exception SWTException <ul>
894 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
895 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
898 public CTabItem getItem (int index) {
900 * This call is intentionally commented out, to allow this getter method to be
901 * called from a thread which is different from one that created the widget.
904 if (index < 0 || index >= items.length)
905 SWT.error(SWT.ERROR_INVALID_RANGE);
906 return items [index];
909 * Gets the item at a point in the widget.
911 * @param pt the point in coordinates relative to the CTabFolder
912 * @return the item at a point or null
914 * @exception SWTException <ul>
915 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
916 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
919 public CTabItem getItem (Point pt) {
921 * This call is intentionally commented out, to allow this getter method to be
922 * called from a thread which is different from one that created the widget.
925 if (items.length == 0) return null;
927 Point size = getSize();
928 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
929 if (size.x <= trim.width) return null;
930 for (int i = 0; i < priority.length; i++) {
931 CTabItem item = items[priority[i]];
932 Rectangle rect = item.getBounds();
933 if (rect.contains(pt)) return item;
938 * Return the number of tabs in the folder.
940 * @return the number of tabs in the folder
942 * @exception SWTException <ul>
943 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
944 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
947 public int getItemCount(){
952 * Return the tab items.
954 * @return the tab items
956 * @exception SWTException <ul>
957 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
958 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
961 public CTabItem [] getItems() {
963 * This call is intentionally commented out, to allow this getter method to be
964 * called from a thread which is different from one that created the widget.
967 CTabItem[] tabItems = new CTabItem [items.length];
968 System.arraycopy(items, 0, tabItems, 0, items.length);
971 int getLeftItemEdge (GC gc, int part){
972 Rectangle trim = renderer.computeTrim(part, SWT.NONE, 0, 0, 0, 0);
975 for (int i = 0; i < controls.length; i++) {
976 if ((controlAlignments[i] & SWT.LEAD) != 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
977 width += controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
980 if (width != 0) width += SPACING * 2;
982 return Math.max(0, x);
985 * Return the lowercase of the first non-'&' character following
986 * an '&' character in the given string. If there are no '&'
987 * characters in the given string, return '\0'.
989 char _findMnemonic (String string) {
990 if (string == null) return '\0';
992 int length = string.length ();
994 while (index < length && string.charAt (index) != '&') index++;
995 if (++index >= length) return '\0';
996 if (string.charAt (index) != '&') return Character.toLowerCase (string.charAt (index));
998 } while (index < length);
1001 String stripMnemonic (String string) {
1003 int length = string.length ();
1005 while ((index < length) && (string.charAt (index) != '&')) index++;
1006 if (++index >= length) return string;
1007 if (string.charAt (index) != '&') {
1008 return string.substring(0, index-1) + string.substring(index, length);
1011 } while (index < length);
1015 * Returns <code>true</code> if the receiver is minimized.
1017 * @return the receiver's minimized state
1019 * @exception SWTException <ul>
1020 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1021 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1026 public boolean getMinimized() {
1031 * Returns <code>true</code> if the minimize button
1034 * @return the visibility of the minimized button
1036 * @exception SWTException <ul>
1037 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1038 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1043 public boolean getMinimizeVisible() {
1048 * Returns the number of characters that will
1049 * appear in a fully compressed tab.
1051 * @return number of characters that will appear in a fully compressed tab
1055 public int getMinimumCharacters() {
1061 * Returns <code>true</code> if the receiver is maximized.
1064 * @return the receiver's maximized state
1066 * @exception SWTException <ul>
1067 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1068 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1073 public boolean getMaximized() {
1078 * Returns <code>true</code> if the maximize button
1081 * @return the visibility of the maximized button
1083 * @exception SWTException <ul>
1084 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1085 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1090 public boolean getMaximizeVisible() {
1095 * Returns <code>true</code> if the receiver displays most
1096 * recently used tabs and <code>false</code> otherwise.
1098 * When there is not enough horizontal space to show all the tabs,
1099 * by default, tabs are shown sequentially from left to right in
1100 * order of their index. When the MRU visibility is turned on,
1101 * the tabs that are visible will be the tabs most recently selected.
1102 * Tabs will still maintain their left to right order based on index
1103 * but only the most recently selected tabs are visible.
1105 * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
1106 * "Tab 3" and "Tab 4" (in order by index). The user selects
1107 * "Tab 1" and then "Tab 3". If the CTabFolder is now
1108 * compressed so that only two tabs are visible, by default,
1109 * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
1110 * selected and "Tab 2" because it is the previous item in index order).
1111 * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
1112 * and "Tab 3" (in that order from left to right).</p>
1114 * @return the receiver's header's visibility state
1116 * @exception SWTException <ul>
1117 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1118 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1123 public boolean getMRUVisible() {
1128 * Returns the receiver's renderer.
1130 * @return the receiver's renderer
1132 * @exception SWTException <ul>
1133 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1134 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1137 * @see #setRenderer(CTabFolderRenderer)
1138 * @see CTabFolderRenderer
1142 public CTabFolderRenderer getRenderer() {
1146 int getRightItemEdge (GC gc){
1147 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
1148 int x = getSize().x - (trim.width + trim.x);
1150 for (int i = 0; i < controls.length; i++) {
1151 int align = controlAlignments[i];
1152 if ((align & SWT.WRAP) == 0 && (align & SWT.LEAD) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
1153 Point rightSize = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT);
1154 width += rightSize.x;
1157 if (width != 0) width += SPACING * 2;
1159 return Math.max(0, x);
1162 * Return the selected tab item, or null if there is no selection.
1164 * @return the selected tab item, or null if none has been selected
1166 * @exception SWTException <ul>
1167 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1168 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1171 public CTabItem getSelection() {
1173 * This call is intentionally commented out, to allow this getter method to be
1174 * called from a thread which is different from one that created the widget.
1177 if (selectedIndex == -1) return null;
1178 return items[selectedIndex];
1181 * Returns the receiver's selection background color.
1183 * @return the selection background color of the receiver
1185 * @exception SWTException <ul>
1186 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1187 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1192 public Color getSelectionBackground() {
1194 return selectionBackground;
1197 * Returns the receiver's selection foreground color.
1199 * @return the selection foreground color of the receiver
1201 * @exception SWTException <ul>
1202 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1203 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1208 public Color getSelectionForeground() {
1210 return selectionForeground;
1213 * Return the index of the selected tab item, or -1 if there
1216 * @return the index of the selected tab item or -1
1218 * @exception SWTException <ul>
1219 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1220 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1223 public int getSelectionIndex() {
1225 * This call is intentionally commented out, to allow this getter method to be
1226 * called from a thread which is different from one that created the widget.
1229 return selectedIndex;
1232 * Returns <code>true</code> if the CTabFolder is rendered
1233 * with a simple, traditional shape.
1235 * @return <code>true</code> if the CTabFolder is rendered with a simple shape
1239 public boolean getSimple() {
1244 * Returns <code>true</code> if the CTabFolder only displays the selected tab
1245 * and <code>false</code> if the CTabFolder displays multiple tabs.
1247 * @return <code>true</code> if the CTabFolder only displays the selected tab and <code>false</code> if the CTabFolder displays multiple tabs
1251 public boolean getSingle() {
1257 public int getStyle() {
1258 int style = super.getStyle();
1259 style &= ~(SWT.TOP | SWT.BOTTOM);
1260 style |= onBottom ? SWT.BOTTOM : SWT.TOP;
1261 style &= ~(SWT.SINGLE | SWT.MULTI);
1262 style |= single ? SWT.SINGLE : SWT.MULTI;
1263 if (borderVisible) style |= SWT.BORDER;
1264 style &= ~SWT.CLOSE;
1265 if (showClose) style |= SWT.CLOSE;
1269 * Returns the height of the tab
1271 * @return the height of the tab
1273 * @exception SWTException <ul>
1274 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1275 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1278 public int getTabHeight(){
1280 if (fixedTabHeight != SWT.DEFAULT) return fixedTabHeight;
1281 return tabHeight - 1; // -1 for line drawn across top of tab //TODO: replace w/ computeTrim of tab area?
1284 * Returns the position of the tab. Possible values are SWT.TOP or SWT.BOTTOM.
1286 * @return the position of the tab
1288 * @exception SWTException <ul>
1289 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1290 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1293 public int getTabPosition(){
1295 return onBottom ? SWT.BOTTOM : SWT.TOP;
1298 * Returns the control in the top right corner of the tab folder.
1299 * Typically this is a close button or a composite with a menu and close button.
1301 * @return the control in the top right corner of the tab folder or null
1303 * @exception SWTException <ul>
1304 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1305 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1310 public Control getTopRight() {
1315 * Returns the alignment of the top right control.
1317 * @return the alignment of the top right control which is either
1318 * <code>SWT.RIGHT</code> or <code>SWT.FILL</code>
1320 * @exception SWTException <ul>
1321 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1322 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1327 public int getTopRightAlignment() {
1329 return topRightAlignment;
1332 * Returns <code>true</code> if the close button appears
1333 * when the user hovers over an unselected tabs.
1335 * @return <code>true</code> if the close button appears on unselected tabs
1339 public boolean getUnselectedCloseVisible() {
1341 return showUnselectedClose;
1344 * Returns <code>true</code> if an image appears
1345 * in unselected tabs.
1347 * @return <code>true</code> if an image appears in unselected tabs
1351 public boolean getUnselectedImageVisible() {
1353 return showUnselectedImage;
1356 * Return the index of the specified tab or -1 if the tab is not
1359 * @param item the tab item for which the index is required
1361 * @return the index of the specified tab item or -1
1363 * @exception IllegalArgumentException <ul>
1364 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1367 * @exception SWTException <ul>
1368 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
1369 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
1372 public int indexOf(CTabItem item) {
1375 SWT.error(SWT.ERROR_NULL_ARGUMENT);
1377 for (int i = 0; i < items.length; i++) {
1378 if (items[i] == item) return i;
1382 void initAccessible() {
1383 final Accessible accessible = getAccessible();
1384 accessible.addAccessibleListener(new AccessibleAdapter() {
1386 public void getName(AccessibleEvent e) {
1387 CTabItem item = null;
1388 int childID = e.childID;
1389 if (childID == ACC.CHILDID_SELF) {
1390 if (selectedIndex != -1) {
1391 item = items[selectedIndex];
1393 } else if (childID >= 0 && childID < items.length) {
1394 item = items[childID];
1396 e.result = item == null ? null : stripMnemonic(item.getText());
1400 public void getHelp(AccessibleEvent e) {
1402 int childID = e.childID;
1403 if (childID == ACC.CHILDID_SELF) {
1404 help = getToolTipText();
1405 } else if (childID >= 0 && childID < items.length) {
1406 help = items[childID].getToolTipText();
1412 public void getKeyboardShortcut(AccessibleEvent e) {
1413 String shortcut = null;
1414 int childID = e.childID;
1415 if (childID >= 0 && childID < items.length) {
1416 String text = items[childID].getText();
1418 char mnemonic = _findMnemonic(text);
1419 if (mnemonic != '\0') {
1420 shortcut = SWT.getMessage ("SWT_Page_Mnemonic", new Object[] {Character.valueOf(mnemonic)}); //$NON-NLS-1$
1424 if (childID == ACC.CHILDID_SELF) {
1425 shortcut = SWT.getMessage ("SWT_SwitchPage_Shortcut"); //$NON-NLS-1$
1427 e.result = shortcut;
1431 accessible.addAccessibleControlListener(new AccessibleControlAdapter() {
1433 public void getChildAtPoint(AccessibleControlEvent e) {
1434 Point testPoint = toControl(e.x, e.y);
1435 int childID = ACC.CHILDID_NONE;
1436 for (int i = 0; i < items.length; i++) {
1437 if (items[i].getBounds().contains(testPoint)) {
1442 if (childID == ACC.CHILDID_NONE) {
1443 Rectangle location = getBounds();
1444 location.x = location.y = 0;
1445 location.height = location.height - getClientArea().height;
1446 if (location.contains(testPoint)) {
1447 childID = ACC.CHILDID_SELF;
1450 e.childID = childID;
1454 public void getLocation(AccessibleControlEvent e) {
1455 Rectangle location = null;
1457 int childID = e.childID;
1458 if (childID == ACC.CHILDID_SELF) {
1459 location = getBounds();
1460 pt = getParent().toDisplay(location.x, location.y);
1462 if (childID >= 0 && childID < items.length && items[childID].showing) {
1463 location = items[childID].getBounds();
1465 if (location != null) {
1466 pt = toDisplay(location.x, location.y);
1469 if (location != null && pt != null) {
1472 e.width = location.width;
1473 e.height = location.height;
1478 public void getChildCount(AccessibleControlEvent e) {
1479 e.detail = items.length;
1483 public void getDefaultAction(AccessibleControlEvent e) {
1484 String action = null;
1485 int childID = e.childID;
1486 if (childID >= 0 && childID < items.length) {
1487 action = SWT.getMessage ("SWT_Switch"); //$NON-NLS-1$
1493 public void getFocus(AccessibleControlEvent e) {
1494 int childID = ACC.CHILDID_NONE;
1495 if (isFocusControl()) {
1496 if (selectedIndex == -1) {
1497 childID = ACC.CHILDID_SELF;
1499 childID = selectedIndex;
1502 e.childID = childID;
1506 public void getRole(AccessibleControlEvent e) {
1508 int childID = e.childID;
1509 if (childID == ACC.CHILDID_SELF) {
1510 role = ACC.ROLE_TABFOLDER;
1511 } else if (childID >= 0 && childID < items.length) {
1512 role = ACC.ROLE_TABITEM;
1518 public void getSelection(AccessibleControlEvent e) {
1519 e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex;
1523 public void getState(AccessibleControlEvent e) {
1525 int childID = e.childID;
1526 if (childID == ACC.CHILDID_SELF) {
1527 state = ACC.STATE_NORMAL;
1528 } else if (childID >= 0 && childID < items.length) {
1529 state = ACC.STATE_SELECTABLE;
1530 if (isFocusControl()) {
1531 state |= ACC.STATE_FOCUSABLE;
1533 if (selectedIndex == childID) {
1534 state |= ACC.STATE_SELECTED;
1535 if (isFocusControl()) {
1536 state |= ACC.STATE_FOCUSED;
1544 public void getChildren(AccessibleControlEvent e) {
1545 int childIdCount = items.length;
1546 Object[] children = new Object[childIdCount];
1547 for (int i = 0; i < childIdCount; i++) {
1548 children[i] = Integer.valueOf(i);
1550 e.children = children;
1554 addListener(SWT.Selection, event -> {
1555 if (isFocusControl()) {
1556 if (selectedIndex == -1) {
1557 accessible.setFocus(ACC.CHILDID_SELF);
1559 accessible.setFocus(selectedIndex);
1564 addListener(SWT.FocusIn, event -> {
1565 if (selectedIndex == -1) {
1566 accessible.setFocus(ACC.CHILDID_SELF);
1568 accessible.setFocus(selectedIndex);
1572 void initAccessibleMinMaxTb() {
1573 minMaxTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
1575 public void getName(AccessibleEvent e) {
1576 if (e.childID != ACC.CHILDID_SELF) {
1577 if (minItem != null && e.childID == minMaxTb.indexOf(minItem)) {
1578 e.result = minItem.getToolTipText();
1579 } else if (maxItem != null && e.childID == minMaxTb.indexOf(maxItem)) {
1580 e.result = maxItem.getToolTipText();
1586 void initAccessibleChevronTb() {
1587 chevronTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
1589 public void getName(AccessibleEvent e) {
1590 if (e.childID != ACC.CHILDID_SELF) {
1591 if (chevronItem != null && e.childID == chevronTb.indexOf(chevronItem)) {
1592 e.result = chevronItem.getToolTipText();
1598 void onKeyDown (Event event) {
1600 switch (event.keyCode) {
1601 case SWT.ARROW_LEFT:
1602 case SWT.ARROW_RIGHT:
1603 int count = items.length;
1604 if (count == 0) return;
1605 if (selectedIndex == -1) return;
1606 int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
1607 int offset = event.keyCode == leadKey ? -1 : 1;
1610 index = selectedIndex + offset;
1612 int[] visible = new int[items.length];
1615 for (int i = 0; i < items.length; i++) {
1616 if (items[i].showing) {
1617 if (i == selectedIndex) current = idx;
1618 visible [idx++] = i;
1621 if (current + offset >= 0 && current + offset < idx){
1622 index = visible [current + offset];
1625 Rectangle chevronRect = chevronItem.getBounds();
1626 chevronRect = event.display.map(chevronTb, this, chevronRect);
1627 CTabFolderEvent e = new CTabFolderEvent(this);
1629 e.time = event.time;
1630 e.x = chevronRect.x;
1631 e.y = chevronRect.y;
1632 e.width = chevronRect.width;
1633 e.height = chevronRect.height;
1635 for (int i = 0; i < folderListeners.length; i++) {
1636 folderListeners[i].showList(e);
1638 if (e.doit && !isDisposed()) {
1639 showList(chevronRect);
1645 if (index < 0 || index >= count) return;
1646 setSelection (index, true);
1650 void onDispose(Event event) {
1651 removeListener(SWT.Dispose, listener);
1652 notifyListeners(SWT.Dispose, event);
1653 event.type = SWT.None;
1655 * Usually when an item is disposed, destroyItem will change the size of the items array,
1656 * reset the bounds of all the tabs and manage the widget associated with the tab.
1657 * Since the whole folder is being disposed, this is not necessary. For speed
1658 * the inDispose flag is used to skip over this part of the item dispose.
1662 if (showMenu != null && !showMenu.isDisposed()) {
1666 int length = items.length;
1667 for (int i = 0; i < length; i++) {
1668 if (items[i] != null) {
1673 gradientColors = null;
1675 selectionGradientColors = null;
1676 selectionGradientPercents = null;
1677 selectionBgImage = null;
1679 selectionBackground = null;
1680 selectionForeground = null;
1682 if (controlBkImages != null) {
1683 for (int i = 0; i < controlBkImages.length; i++) {
1684 if (controlBkImages[i] != null) {
1685 controlBkImages[i].dispose();
1686 controlBkImages[i] = null;
1689 controlBkImages = null;
1692 controlAlignments = null;
1693 controlRects = null;
1695 if (maxImage != null) maxImage.dispose();
1698 if (minImage != null) minImage.dispose();
1701 if (chevronImage != null) chevronImage.dispose();
1702 chevronImage = null;
1704 if (renderer != null) renderer.dispose();
1714 if (folderListeners.length != 0) folderListeners = new CTabFolder2Listener[0];
1715 if (tabListeners.length != 0) tabListeners = new CTabFolderListener[0];
1717 void onDragDetect(Event event) {
1718 boolean consume = false;
1719 for (int i = 0; i < items.length; i++) {
1720 if (items[i].closeRect.contains(event.x, event.y)) {
1726 event.type = SWT.None;
1729 void onFocus(Event event) {
1731 if (selectedIndex >= 0) {
1734 setSelection(0, true);
1737 boolean onMnemonic (Event event, boolean doit) {
1738 char key = event.character;
1739 for (int i = 0; i < items.length; i++) {
1740 if (items[i] != null) {
1741 char mnemonic = _findMnemonic (items[i].getText ());
1742 if (mnemonic != '\0') {
1743 if (Character.toLowerCase (key) == mnemonic) {
1745 setSelection(i, true);
1755 void onMenuDetect(Event event) {
1756 if (event.detail == SWT.MENU_KEYBOARD) {
1757 if (selectedIndex != -1) {
1758 CTabItem item = items[selectedIndex];
1759 Rectangle rect = getDisplay().map(this, null, item.getBounds());
1760 if (!rect.contains(event.x, event.y)) {
1761 /* If the mouse is not in the currently-selected tab,
1762 * then pop up the menu near the top-right corner of the current tab.
1764 Rectangle itemTrim = renderer.computeTrim(selectedIndex, SWT.NONE, 0, 0, 0, 0);
1765 Rectangle closeTrim = renderer.computeTrim(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, 0, 0, 0, 0);
1766 event.x = rect.x + rect.width - item.closeRect.width + itemTrim.x - closeTrim.width;
1767 event.y = rect.y - itemTrim.y - closeTrim.y;
1772 void onMouseDoubleClick(Event event) {
1773 if (event.button != 1 ||
1774 (event.stateMask & SWT.BUTTON2) != 0 ||
1775 (event.stateMask & SWT.BUTTON3) != 0) return;
1776 Event e = new Event();
1777 e.item = getItem(new Point(event.x, event.y));
1778 if (e.item != null) {
1779 notifyListeners(SWT.DefaultSelection, e);
1782 void onMouse(Event event) {
1783 if( isDisposed() ) {
1786 int x = event.x, y = event.y;
1787 switch (event.type) {
1788 case SWT.MouseEnter: {
1789 setToolTipText(null);
1792 case SWT.MouseExit: {
1793 for (int i=0; i<items.length; i++) {
1794 CTabItem item = items[i];
1795 if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND) {
1796 item.closeImageState = SWT.BACKGROUND;
1797 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1799 if ((item.state & SWT.HOT) != 0) {
1800 item.state &= ~SWT.HOT;
1801 redraw(item.x, item.y, item.width, item.height, false);
1803 if (i == selectedIndex && item.closeImageState != SWT.NONE) {
1804 item.closeImageState = SWT.NONE;
1805 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1810 case SWT.MouseHover:
1811 case SWT.MouseDown: {
1812 if (hoverTb && hoverRect.contains(x, y) && !hovering) {
1815 hoverTimerRunning = true;
1816 event.display.timerExec(2000, new Runnable() {
1819 if (isDisposed()) return;
1821 Display display = getDisplay();
1822 Control c = display.getCursorControl();
1823 boolean reschedule = false;
1825 for (int i = 0; i < controls.length; i++) {
1828 if (temp.equals(controls[i])) {
1831 temp = temp.getParent();
1832 if (temp == null || temp.equals(CTabFolder.this)) break;
1834 } while (!reschedule);
1835 if (reschedule) break;
1838 if (reschedule && hoverTimerRunning) {
1839 display.timerExec(2000, this);
1849 if (event.button != 1) return;
1850 CTabItem item = null;
1852 if (selectedIndex != -1) {
1853 Rectangle bounds = items[selectedIndex].getBounds();
1854 if (bounds.contains(x, y)){
1855 item = items[selectedIndex];
1859 for (int i=0; i<items.length; i++) {
1860 Rectangle bounds = items[i].getBounds();
1861 if (bounds.contains(x, y)){
1867 if (item.closeRect.contains(x,y)){
1868 item.closeImageState = SWT.SELECTED;
1869 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1873 int index = indexOf(item);
1875 int oldSelectedIndex = selectedIndex;
1876 setSelection(index, true);
1877 if (oldSelectedIndex == selectedIndex) {
1878 /* If the click is on the selected tabitem, then set focus to the tabfolder */
1886 case SWT.MouseMove: {
1887 _setToolTipText(event.x, event.y);
1888 boolean close = false;
1889 for (int i=0; i<items.length; i++) {
1890 CTabItem item = items[i];
1892 if (item.getBounds().contains(x, y)) {
1894 if (item.closeRect.contains(x, y)) {
1895 if (item.closeImageState != SWT.SELECTED && item.closeImageState != SWT.HOT) {
1896 item.closeImageState = SWT.HOT;
1897 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1900 if (item.closeImageState != SWT.NONE) {
1901 item.closeImageState = SWT.NONE;
1902 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1905 if ((item.state & SWT.HOT) == 0) {
1906 item.state |= SWT.HOT;
1907 redraw(item.x, item.y, item.width, item.height, false);
1910 if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND && !close) {
1911 item.closeImageState = SWT.BACKGROUND;
1912 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1914 if ((item.state & SWT.HOT) != 0 && !close) {
1915 item.state &= ~SWT.HOT;
1916 redraw(item.x, item.y, item.width, item.height, false);
1918 if (i == selectedIndex && item.closeImageState != SWT.NONE && !close) {
1919 item.closeImageState = SWT.NONE;
1920 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1926 if (event.button != 1) return;
1927 CTabItem item = null;
1929 if (selectedIndex != -1) {
1930 Rectangle bounds = items[selectedIndex].getBounds();
1931 if (bounds.contains(x, y)){
1932 item = items[selectedIndex];
1936 for (int i=0; i<items.length; i++) {
1937 Rectangle bounds = items[i].getBounds();
1938 if (bounds.contains(x, y)){
1944 if (item.closeRect.contains(x,y)) {
1945 boolean selected = item.closeImageState == SWT.SELECTED;
1946 item.closeImageState = SWT.HOT;
1947 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1948 if (!selected) return;
1949 CTabFolderEvent e = new CTabFolderEvent(this);
1951 e.time = event.time;
1954 for (int j = 0; j < folderListeners.length; j++) {
1955 CTabFolder2Listener listener = folderListeners[j];
1958 for (int j = 0; j < tabListeners.length; j++) {
1959 CTabFolderListener listener = tabListeners[j];
1960 listener.itemClosed(e);
1962 if (e.doit) item.dispose();
1963 if (!isDisposed() && item.isDisposed()) {
1964 Display display = getDisplay();
1965 Point pt = display.getCursorLocation();
1966 pt = display.map(null, this, pt.x, pt.y);
1967 CTabItem nextItem = getItem(pt);
1968 if (nextItem != null) {
1969 if (nextItem.closeRect.contains(pt)) {
1970 if (nextItem.closeImageState != SWT.SELECTED && nextItem.closeImageState != SWT.HOT) {
1971 nextItem.closeImageState = SWT.HOT;
1972 redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
1975 if (nextItem.closeImageState != SWT.NONE) {
1976 nextItem.closeImageState = SWT.NONE;
1977 redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
1988 void onPageTraversal(Event event) {
1989 int count = items.length;
1990 if (count == 0) return;
1991 int index = selectedIndex;
1995 int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
1997 index = (selectedIndex + offset + count) % count;
1999 int[] visible = new int[items.length];
2002 for (int i = 0; i < items.length; i++) {
2003 if (items[i].showing) {
2004 if (i == selectedIndex) current = idx;
2005 visible [idx++] = i;
2008 if (current + offset >= 0 && current + offset < idx){
2009 index = visible [current + offset];
2012 Rectangle chevronRect = chevronItem.getBounds();
2013 chevronRect = event.display.map(chevronTb, this, chevronRect);
2014 CTabFolderEvent e = new CTabFolderEvent(this);
2016 e.time = event.time;
2017 e.x = chevronRect.x;
2018 e.y = chevronRect.y;
2019 e.width = chevronRect.width;
2020 e.height = chevronRect.height;
2022 for (int i = 0; i < folderListeners.length; i++) {
2023 folderListeners[i].showList(e);
2025 if (e.doit && !isDisposed()) {
2026 showList(chevronRect);
2032 setSelection (index, true);
2034 void onPaint(Event event) {
2035 if (inDispose) return;
2036 Font font = getFont();
2037 if (oldFont == null || !oldFont.equals(font)) {
2038 // handle case where default font changes
2040 if (!updateTabHeight(false)) {
2048 Font gcFont = gc.getFont();
2049 Color gcBackground = gc.getBackground();
2050 Color gcForeground = gc.getForeground();
2052 // Useful for debugging paint problems
2054 //Point size = getSize();
2055 //gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN));
2056 //gc.fillRectangle(-10, -10, size.x + 20, size.y+20);
2059 Point size = getSize();
2060 Rectangle bodyRect = new Rectangle(0, 0, size.x, size.y);
2061 renderer.draw(CTabFolderRenderer.PART_BODY, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
2064 gc.setForeground(gcForeground);
2065 gc.setBackground(gcBackground);
2067 renderer.draw(CTabFolderRenderer.PART_HEADER, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
2070 gc.setForeground(gcForeground);
2071 gc.setBackground(gcBackground);
2074 for (int i=0; i < items.length; i++) {
2075 Rectangle itemBounds = items[i].getBounds();
2076 if (i != selectedIndex && event.getBounds().intersects(itemBounds)) {
2077 renderer.draw(i, SWT.BACKGROUND | SWT.FOREGROUND | items[i].state , itemBounds, gc);
2083 gc.setForeground(gcForeground);
2084 gc.setBackground(gcBackground);
2086 if (selectedIndex != -1) {
2087 renderer.draw(selectedIndex, items[selectedIndex].state | SWT.BACKGROUND | SWT.FOREGROUND, items[selectedIndex].getBounds(), gc);
2091 gc.setForeground(gcForeground);
2092 gc.setBackground(gcBackground);
2095 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
2096 int x = getSize().x - (trim.width + trim.x);
2097 hoverRect = new Rectangle(x - 16 - SPACING, 2, 16, getTabHeight() - 2);
2098 gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
2100 int y = hoverRect.y;
2101 gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
2102 gc.fillRectangle(x + hoverRect.width - 6, y, 5, 5);
2103 gc.drawRectangle(x + hoverRect.width - 6, y, 5, 5);
2104 gc.drawLine(x + hoverRect.width - 6, y+2, x + hoverRect.width - 6 + 5, y + 2);
2105 gc.fillRectangle(x, y, 5 , 2);
2106 gc.drawRectangle(x, y, 5 , 2);
2109 gc.setForeground(gcForeground);
2110 gc.setBackground(gcBackground);
2113 void onResize(Event event) {
2114 if (inDispose) return;
2115 if (ignoreResize) return;
2116 if (updateItems()) {
2119 Point size = getSize();
2120 if (oldSize == null) {
2123 if (onBottom && size.y != oldSize.y) {
2126 int x1 = Math.min(size.x, oldSize.x);
2127 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
2128 if (size.x != oldSize.x) x1 -= trim.width + trim.x - marginWidth + 2;
2129 if (!simple) x1 -= 5; // rounded top right corner
2130 int y1 = Math.min(size.y, oldSize.y);
2131 if (size.y != oldSize.y) y1 -= trim.height + trim.y - marginHeight;
2132 int x2 = Math.max(size.x, oldSize.x);
2133 int y2 = Math.max(size.y, oldSize.y);
2134 redraw(0, y1, x2, y2 - y1, false);
2135 redraw(x1, 0, x2 - x1, y2, false);
2137 redraw(hoverRect.x, hoverRect.y, hoverRect.width, hoverRect.height, false);
2143 void onSelection(Event event) {
2148 if (event.widget == maxItem) {
2149 CTabFolderEvent e = new CTabFolderEvent(this);
2150 e.widget = CTabFolder.this;
2151 e.time = event.time;
2152 for (int i = 0; i < folderListeners.length; i++) {
2154 folderListeners[i].restore(e);
2156 folderListeners[i].maximize(e);
2159 } else if (event.widget == minItem) {
2160 CTabFolderEvent e = new CTabFolderEvent(this);
2161 e.widget = CTabFolder.this;
2162 e.time = event.time;
2163 for (int i = 0; i < folderListeners.length; i++) {
2165 folderListeners[i].restore(e);
2167 folderListeners[i].minimize(e);
2170 } else if (event.widget == chevronItem) {
2171 Rectangle chevronRect = chevronItem.getBounds();
2172 chevronRect = event.display.map(chevronTb, this, chevronRect);
2173 CTabFolderEvent e = new CTabFolderEvent(this);
2175 e.time = event.time;
2176 e.x = chevronRect.x;
2177 e.y = chevronRect.y;
2178 e.width = chevronRect.width;
2179 e.height = chevronRect.height;
2181 for (int i = 0; i < folderListeners.length; i++) {
2182 folderListeners[i].showList(e);
2184 if (e.doit && !isDisposed()) {
2185 showList(chevronRect);
2189 void onTraverse (Event event) {
2190 if (ignoreTraverse) return;
2192 switch (event.detail) {
2193 case SWT.TRAVERSE_ESCAPE:
2194 case SWT.TRAVERSE_RETURN:
2195 case SWT.TRAVERSE_TAB_NEXT:
2196 case SWT.TRAVERSE_TAB_PREVIOUS:
2197 Control focusControl = getDisplay().getFocusControl();
2198 if (focusControl == this) event.doit = true;
2200 case SWT.TRAVERSE_MNEMONIC:
2201 event.doit = onMnemonic(event, false);
2203 case SWT.TRAVERSE_PAGE_NEXT:
2204 case SWT.TRAVERSE_PAGE_PREVIOUS:
2205 event.doit = items.length > 0;
2208 ignoreTraverse = true;
2209 notifyListeners(SWT.Traverse, event);
2210 ignoreTraverse = false;
2211 event.type = SWT.None;
2212 if (isDisposed()) return;
2213 if (!event.doit) return;
2214 switch (event.detail) {
2215 case SWT.TRAVERSE_MNEMONIC:
2216 onMnemonic(event, true);
2217 event.detail = SWT.TRAVERSE_NONE;
2219 case SWT.TRAVERSE_PAGE_NEXT:
2220 case SWT.TRAVERSE_PAGE_PREVIOUS:
2221 onPageTraversal(event);
2222 event.detail = SWT.TRAVERSE_NONE;
2227 Point size = getSize();
2228 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
2230 int h = trim.height + trim.y - marginHeight;
2231 redraw(0, size.y - h - 1, size.x, h + 1, false);
2233 redraw(0, 0, size.x, -trim.y - marginHeight + 1, false);
2237 * Removes the listener.
2239 * @param listener the listener which should no longer be notified
2241 * @exception IllegalArgumentException <ul>
2242 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2245 * @exception SWTException <ul>
2246 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2247 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2250 * @see #addCTabFolder2Listener(CTabFolder2Listener)
2254 public void removeCTabFolder2Listener(CTabFolder2Listener listener) {
2256 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2257 if (folderListeners.length == 0) return;
2259 for (int i = 0; i < folderListeners.length; i++) {
2260 if (listener == folderListeners[i]){
2265 if (index == -1) return;
2266 if (folderListeners.length == 1) {
2267 folderListeners = new CTabFolder2Listener[0];
2270 CTabFolder2Listener[] newTabListeners = new CTabFolder2Listener[folderListeners.length - 1];
2271 System.arraycopy(folderListeners, 0, newTabListeners, 0, index);
2272 System.arraycopy(folderListeners, index + 1, newTabListeners, index, folderListeners.length - index - 1);
2273 folderListeners = newTabListeners;
2276 * Removes the listener.
2278 * @param listener the listener which should no longer be notified
2280 * @exception IllegalArgumentException <ul>
2281 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2284 * @exception SWTException <ul>
2285 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2286 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2289 * @deprecated see removeCTabFolderCloseListener(CTabFolderListener)
2292 public void removeCTabFolderListener(CTabFolderListener listener) {
2294 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2295 if (tabListeners.length == 0) return;
2297 for (int i = 0; i < tabListeners.length; i++) {
2298 if (listener == tabListeners[i]){
2303 if (index == -1) return;
2304 if (tabListeners.length == 1) {
2305 tabListeners = new CTabFolderListener[0];
2308 CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
2309 System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
2310 System.arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1);
2311 tabListeners = newTabListeners;
2314 * Removes the listener from the collection of listeners who will
2315 * be notified when the user changes the receiver's selection.
2317 * @param listener the listener which should no longer be notified
2319 * @exception IllegalArgumentException <ul>
2320 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2322 * @exception SWTException <ul>
2323 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2324 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2327 * @see SelectionListener
2328 * @see #addSelectionListener
2330 public void removeSelectionListener(SelectionListener listener) {
2332 if (listener == null) {
2333 SWT.error(SWT.ERROR_NULL_ARGUMENT);
2335 removeListener(SWT.Selection, listener);
2336 removeListener(SWT.DefaultSelection, listener);
2340 public void reskin(int flags) {
2341 super.reskin(flags);
2342 for (int i = 0; i < items.length; i++) {
2343 items[i].reskin(flags);
2348 public void setBackground (Color color) {
2349 super.setBackground(color);
2350 renderer.createAntialiasColors(); //TODO: need better caching strategy
2355 * Specify a gradient of colors to be drawn in the background of the unselected tabs.
2356 * For example to draw a gradient that varies from dark blue to blue and then to
2357 * white, use the following call to setBackground:
2359 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2360 * display.getSystemColor(SWT.COLOR_BLUE),
2361 * display.getSystemColor(SWT.COLOR_WHITE),
2362 * display.getSystemColor(SWT.COLOR_WHITE)},
2363 * new int[] {25, 50, 100});
2366 * @param colors an array of Color that specifies the colors to appear in the gradient
2367 * in order of appearance left to right. The value <code>null</code> clears the
2368 * background gradient. The value <code>null</code> can be used inside the array of
2369 * Color to specify the background color.
2370 * @param percents an array of integers between 0 and 100 specifying the percent of the width
2371 * of the widget at which the color should change. The size of the <code>percents</code>
2372 * array must be one less than the size of the <code>colors</code> array.
2374 * @exception SWTException <ul>
2375 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2376 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2381 public void setBackground(Color[] colors, int[] percents) {
2382 setBackground(colors, percents, false);
2385 * Specify a gradient of colors to be drawn in the background of the unselected tab.
2386 * For example to draw a vertical gradient that varies from dark blue to blue and then to
2387 * white, use the following call to setBackground:
2389 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2390 * display.getSystemColor(SWT.COLOR_BLUE),
2391 * display.getSystemColor(SWT.COLOR_WHITE),
2392 * display.getSystemColor(SWT.COLOR_WHITE)},
2393 * new int[] {25, 50, 100}, true);
2396 * @param colors an array of Color that specifies the colors to appear in the gradient
2397 * in order of appearance left to right. The value <code>null</code> clears the
2398 * background gradient. The value <code>null</code> can be used inside the array of
2399 * Color to specify the background color.
2400 * @param percents an array of integers between 0 and 100 specifying the percent of the width
2401 * of the widget at which the color should change. The size of the <code>percents</code>
2402 * array must be one less than the size of the <code>colors</code> array.
2404 * @param vertical indicate the direction of the gradient. <code>True</code> is vertical and <code>false</code> is horizontal.
2406 * @exception SWTException <ul>
2407 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2408 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2413 public void setBackground(Color[] colors, int[] percents, boolean vertical) {
2415 if (colors != null) {
2416 if (percents == null || percents.length != colors.length - 1) {
2417 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2419 for (int i = 0; i < percents.length; i++) {
2420 if (percents[i] < 0 || percents[i] > 100) {
2421 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2423 if (i > 0 && percents[i] < percents[i-1]) {
2424 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2427 if (getDisplay().getDepth() < 15) {
2428 // Don't use gradients on low color displays
2429 colors = new Color[] {colors[colors.length - 1]};
2430 percents = new int[] {};
2434 // Are these settings the same as before?
2435 if ((gradientColors != null) && (colors != null) &&
2436 (gradientColors.length == colors.length)) {
2437 boolean same = false;
2438 for (int i = 0; i < gradientColors.length; i++) {
2439 if (gradientColors[i] == null) {
2440 same = colors[i] == null;
2442 same = gradientColors[i].equals(colors[i]);
2447 for (int i = 0; i < gradientPercents.length; i++) {
2448 same = gradientPercents[i] == percents[i];
2452 if (same && this.gradientVertical == vertical) return;
2454 // Store the new settings
2455 if (colors == null) {
2456 gradientColors = null;
2457 gradientPercents = null;
2458 gradientVertical = false;
2459 setBackground((Color)null);
2461 gradientColors = new Color[colors.length];
2462 for (int i = 0; i < colors.length; ++i) {
2463 gradientColors[i] = colors[i];
2465 gradientPercents = new int[percents.length];
2466 for (int i = 0; i < percents.length; ++i) {
2467 gradientPercents[i] = percents[i];
2469 gradientVertical = vertical;
2470 setBackground(gradientColors[gradientColors.length-1]);
2473 // Refresh with the new settings
2477 public void setBackgroundImage(Image image) {
2478 super.setBackgroundImage(image);
2479 renderer.createAntialiasColors(); //TODO: need better caching strategy
2483 * Toggle the visibility of the border
2485 * @param show true if the border should be displayed
2487 * @exception SWTException <ul>
2488 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2489 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2492 public void setBorderVisible(boolean show) {
2494 if (borderVisible == show) return;
2495 this.borderVisible = show;
2496 updateFolder(REDRAW);
2498 void setButtonBounds(GC gc) {
2499 Point size = getSize();
2501 Display display = getDisplay();
2503 if (minMaxTb == null) {
2504 minMaxTb = new ToolBar(this, SWT.FLAT);
2505 initAccessibleMinMaxTb();
2506 addTabControl(minMaxTb, SWT.TRAIL, 0, false);
2508 if (maxItem == null) {
2509 maxItem = new ToolItem(minMaxTb, SWT.PUSH);
2510 if (maxImage == null) {
2511 maxImage = createButtonImage(display, CTabFolderRenderer.PART_MAX_BUTTON);
2513 maxItem.setImage(maxImage);
2514 maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
2515 maxItem.addListener(SWT.Selection, listener);
2518 //might need to remove it if already there
2519 if (maxItem != null) {
2526 if (minMaxTb == null) {
2527 minMaxTb = new ToolBar(this, SWT.FLAT);
2528 initAccessibleMinMaxTb();
2529 addTabControl(minMaxTb, SWT.TRAIL, 0, false);
2531 if (minItem == null) {
2532 minItem = new ToolItem(minMaxTb, SWT.PUSH, 0);
2533 if (minImage == null) {
2534 minImage = createButtonImage(display, CTabFolderRenderer.PART_MIN_BUTTON);
2536 minItem.setImage(minImage);
2537 minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
2538 minItem.addListener(SWT.Selection, listener);
2541 //might need to remove it if already there
2542 if (minItem != null) {
2547 if (minMaxTb != null && minMaxTb.getItemCount() == 0) {
2548 removeTabControl(minMaxTb, false);
2553 int itemCount = items.length;
2556 count = selectedIndex == -1 ? itemCount : itemCount - 1;
2559 while (showCount < priority.length && items[priority[showCount]].showing) {
2562 count = itemCount - showCount;
2564 if (count != chevronCount) {
2565 chevronCount = count;
2566 if (chevronImage != null) chevronImage.dispose();
2567 chevronImage = createButtonImage(display, CTabFolderRenderer.PART_CHEVRON_BUTTON);
2568 chevronItem.setImage(chevronImage);
2572 boolean[][] overflow = new boolean[1][0];
2573 Rectangle[] rects = computeControlBounds(size, overflow);
2574 if (fixedTabHeight != SWT.DEFAULT) {
2575 int height = fixedTabHeight;
2578 Rectangle tabBounds = this.getBounds();
2579 for (int i = 0; i < rects.length; i++) {
2580 if (!(overflow[0][i])) {
2581 if (rects[i].height > height) {
2588 for (int i = 0; i < rects.length; i++) {
2589 if (!(overflow[0][i])) {
2590 if (rects[i].height > height) {
2591 rects[i].x = tabBounds.width + 20;
2598 int headerHeight = 0;
2599 for (int i = 0; i < rects.length; i++) {
2600 if (!overflow[0][i]) headerHeight = Math.max(rects[i].height, headerHeight);
2602 boolean changed = false;
2603 ignoreResize = true;
2604 for (int i = 0; i < controls.length; i++) {
2605 if (!controls[i].isDisposed()) {
2606 if (overflow[0][i]) {
2607 controls[i].setBounds(rects[i]);
2609 controls[i].moveAbove(null);
2610 controls[i].setBounds(rects[i].x, rects[i].y, rects[i].width, headerHeight);
2613 if (!changed && !rects[i].equals(controlRects[i])) changed = true;
2615 ignoreResize = false;
2616 controlRects = rects;
2617 if (changed || hovering) updateBkImages();
2620 public boolean setFocus () {
2624 * Feature in SWT. When a new tab item is selected
2625 * and the previous tab item had focus, removing focus
2626 * from the previous tab item causes fixFocus() to give
2627 * focus to the first child, which is usually one of the
2628 * toolbars. This is unexpected.
2629 * The fix is to try to set focus on the first tab item
2630 * if fixFocus() is called.
2632 Control focusControl = getDisplay().getFocusControl ();
2633 boolean fixFocus = isAncestor (focusControl);
2635 CTabItem item = getSelection();
2637 if (item.setFocus ()) return true;
2640 return super.setFocus ();
2642 /* Copy of isFocusAncestor from Control. */
2643 boolean isAncestor (Control control) {
2644 while (control != null && control != this && !(control instanceof Shell)) {
2645 control = control.getParent();
2647 return control == this;
2650 public void setFont(Font font) {
2652 if (font != null && font.equals(getFont())) return;
2653 super.setFont(font);
2654 oldFont = getFont();
2655 updateFolder(REDRAW);
2658 public void setForeground (Color color) {
2659 super.setForeground(color);
2663 * Display an insert marker before or after the specified tab item.
2665 * A value of null will clear the mark.
2667 * @param item the item with which the mark is associated or null
2669 * @param after true if the mark should be displayed after the specified item
2671 * @exception SWTException <ul>
2672 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2673 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2676 public void setInsertMark(CTabItem item, boolean after) {
2680 * Display an insert marker before or after the specified tab item.
2682 * A value of -1 will clear the mark.
2684 * @param index the index of the item with which the mark is associated or -1
2686 * @param after true if the mark should be displayed after the specified item
2688 * @exception IllegalArgumentException <ul>
2689 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
2692 * @exception SWTException <ul>
2693 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2694 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2697 public void setInsertMark(int index, boolean after) {
2699 if (index < -1 || index >= getItemCount()) {
2700 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2703 boolean setItemLocation(GC gc) {
2704 boolean changed = false;
2705 if (items.length == 0) return false;
2706 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
2707 int borderBottom = trim.height + trim.y;
2708 int borderTop = -trim.y;
2709 Point size = getSize();
2710 int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop;
2711 Point closeButtonSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, 0, gc, SWT.DEFAULT, SWT.DEFAULT);
2712 int leftItemEdge = getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
2714 int defaultX = getDisplay().getBounds().width + 10; // off screen
2715 for (int i = 0; i < items.length; i++) {
2716 CTabItem item = items[i];
2717 if (i == selectedIndex) {
2718 firstIndex = selectedIndex;
2719 int oldX = item.x, oldY = item.y;
2720 item.x = leftItemEdge;
2722 item.showing = true;
2723 if (showClose || item.showClose) {
2724 item.closeRect.x = leftItemEdge - renderer.computeTrim(i, SWT.NONE, 0, 0, 0, 0).x;
2725 item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
2727 if (item.x != oldX || item.y != oldY) changed = true;
2730 item.showing = false;
2734 int rightItemEdge = getRightItemEdge(gc);
2735 int maxWidth = rightItemEdge - leftItemEdge;
2737 for (int i = 0; i < priority.length; i++) {
2738 CTabItem item = items[priority[i]];
2739 width += item.width;
2740 item.showing = i == 0 ? true : item.width > 0 && width <= maxWidth;
2742 int x = getLeftItemEdge(gc, CTabFolderRenderer.PART_HEADER);
2743 int defaultX = getDisplay().getBounds().width + 10; // off screen
2744 firstIndex = items.length - 1;
2745 for (int i = 0; i < items.length; i++) {
2746 CTabItem item = items[i];
2747 if (!item.showing) {
2748 if (item.x != defaultX) changed = true;
2751 firstIndex = Math.min(firstIndex, i);
2752 if (item.x != x || item.y != y) changed = true;
2755 int state = SWT.NONE;
2756 if (i == selectedIndex) state |= SWT.SELECTED;
2757 Rectangle edgeTrim = renderer.computeTrim(i, state, 0, 0, 0, 0);
2758 item.closeRect.x = item.x + item.width - (edgeTrim.width + edgeTrim.x) - closeButtonSize.x;
2759 item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
2761 if (!simple && i == selectedIndex) x -= renderer.curveIndent; //TODO: fix next item position
2768 * Reorder the items of the receiver.
2769 * @param indices an array containing the new indices for all items
2771 * @exception IllegalArgumentException <ul>
2772 * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
2773 * <li>ERROR_INVALID_ARGUMENT - if the indices array is not the same length as the number of items,
2774 * if there are duplicate indices or an index is out of range.</li>
2777 * @exception SWTException <ul>
2778 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2779 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2782 /*public*/ void setItemOrder (int[] indices) {
2784 if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2785 if (indices.length != items.length) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2786 int newSelectedIndex = -1;
2787 boolean[] seen = new boolean[items.length];
2788 CTabItem[] temp = new CTabItem[items.length];
2789 for (int i=0; i<indices.length; i++) {
2790 int index = indices[i];
2791 if (!(0 <= index && index < items.length)) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2792 if (seen[index]) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2794 if (index == selectedIndex) newSelectedIndex = i;
2795 temp[i] = items[index];
2798 selectedIndex = newSelectedIndex;
2799 updateFolder(REDRAW);
2801 boolean setItemSize(GC gc) {
2802 boolean changed = false;
2803 if (isDisposed()) return changed;
2804 Point size = getSize();
2805 if (size.x <= 0 || size.y <= 0) return changed;
2806 ToolBar chevron = getChevron();
2807 if (chevron != null) chevron.setVisible(false);
2808 showChevron = false;
2810 showChevron = chevronVisible && items.length > 1;
2812 chevron.setVisible(true);
2814 if (selectedIndex != -1) {
2815 CTabItem tab = items[selectedIndex];
2816 int width = renderer.computeSize(selectedIndex, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2817 width = Math.min(width, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
2818 if (tab.height != tabHeight || tab.width != width) {
2820 tab.shortenedText = null;
2821 tab.shortenedTextWidth = 0;
2822 tab.height = tabHeight;
2824 tab.closeRect.width = tab.closeRect.height = 0;
2825 if (showClose || tab.showClose) {
2826 Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT);
2827 tab.closeRect.width = closeSize.x;
2828 tab.closeRect.height = closeSize.y;
2835 if (items.length == 0) return changed;
2837 int tabAreaWidth = Math.max(0, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
2838 // First, try the minimum tab size at full compression.
2840 int[] minWidths = new int[items.length];
2841 for (int i = 0; i < priority.length; i++) {
2842 int index = priority[i];
2843 int state = CTabFolderRenderer.MINIMUM_SIZE;
2844 if (index == selectedIndex) state |= SWT.SELECTED;
2845 minWidths[index] = renderer.computeSize(index, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2846 minWidth += minWidths[index];
2847 if (minWidth > tabAreaWidth) break;
2849 if (minWidth > tabAreaWidth) {
2850 // full compression required and a chevron
2851 showChevron = chevronVisible && items.length > 1;
2853 tabAreaWidth -= chevron.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
2854 chevron.setVisible(true);
2857 int index = selectedIndex != -1 ? selectedIndex : 0;
2858 if (tabAreaWidth < widths[index]) {
2859 widths[index] = Math.max(0, tabAreaWidth);
2863 int[] maxWidths = new int[items.length];
2864 for (int i = 0; i < items.length; i++) {
2866 if (i == selectedIndex) state |= SWT.SELECTED;
2867 maxWidths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2868 maxWidth += maxWidths[i];
2870 if (maxWidth <= tabAreaWidth) {
2871 // no compression required
2874 // determine compression for each item
2875 int extra = (tabAreaWidth - minWidth) / items.length;
2877 int large = 0, totalWidth = 0;
2878 for (int i = 0 ; i < items.length; i++) {
2879 if (maxWidths[i] > minWidths[i] + extra) {
2880 totalWidth += minWidths[i] + extra;
2883 totalWidth += maxWidths[i];
2886 if (totalWidth >= tabAreaWidth) {
2890 if (large == 0 || tabAreaWidth - totalWidth < large) break;
2893 widths = new int[items.length];
2894 for (int i = 0; i < items.length; i++) {
2895 widths[i] = Math.min(maxWidths[i], minWidths[i] + extra);
2900 for (int i = 0; i < items.length; i++) {
2901 CTabItem tab = items[i];
2902 int width = widths[i];
2903 if (tab.height != tabHeight || tab.width != width) {
2905 tab.shortenedText = null;
2906 tab.shortenedTextWidth = 0;
2907 tab.height = tabHeight;
2909 tab.closeRect.width = tab.closeRect.height = 0;
2910 if (showClose || tab.showClose) {
2911 if (i == selectedIndex || showUnselectedClose) {
2912 Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT);
2913 tab.closeRect.width = closeSize.x;
2914 tab.closeRect.height = closeSize.y;
2922 * Marks the receiver's maximize button as visible if the argument is <code>true</code>,
2923 * and marks it invisible otherwise.
2925 * @param visible the new visibility state
2927 * @exception SWTException <ul>
2928 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2929 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2934 public void setMaximizeVisible(boolean visible) {
2936 if (showMax == visible) return;
2937 // display maximize button
2939 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
2942 * Sets the layout which is associated with the receiver to be
2943 * the argument which may be null.
2945 * Note: No Layout can be set on this Control because it already
2946 * manages the size and position of its children.
2949 * @param layout the receiver's new layout or null
2951 * @exception SWTException <ul>
2952 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2953 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2957 public void setLayout (Layout layout) {
2962 * Sets the maximized state of the receiver.
2964 * @param maximize the new maximized state
2966 * @exception SWTException <ul>
2967 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2968 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2973 public void setMaximized(boolean maximize) {
2975 if (this.maximized == maximize) return;
2976 if (maximize && this.minimized) setMinimized(false);
2977 this.maximized = maximize;
2978 if (minMaxTb != null && maxItem != null) {
2979 if (maxImage != null) maxImage.dispose();
2980 maxImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MAX_BUTTON);
2981 maxItem.setImage(maxImage);
2982 maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
2986 * Marks the receiver's minimize button as visible if the argument is <code>true</code>,
2987 * and marks it invisible otherwise.
2989 * @param visible the new visibility state
2991 * @exception SWTException <ul>
2992 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2993 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2998 public void setMinimizeVisible(boolean visible) {
3000 if (showMin == visible) return;
3001 // display minimize button
3003 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3006 * Sets the minimized state of the receiver.
3008 * @param minimize the new minimized state
3010 * @exception SWTException <ul>
3011 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3012 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3017 public void setMinimized(boolean minimize) {
3019 if (this.minimized == minimize) return;
3020 if (minimize && this.maximized) setMaximized(false);
3021 this.minimized = minimize;
3022 if (minMaxTb != null && minItem != null) {
3023 if (minImage != null) minImage.dispose();
3024 minImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MIN_BUTTON);
3025 minItem.setImage(minImage);
3026 minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
3031 * Sets the minimum number of characters that will
3032 * be displayed in a fully compressed tab.
3034 * @param count the minimum number of characters that will be displayed in a fully compressed tab
3036 * @exception SWTException <ul>
3037 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3038 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3039 * <li>ERROR_INVALID_RANGE - if the count is less than zero</li>
3044 public void setMinimumCharacters(int count) {
3046 if (count < 0) SWT.error(SWT.ERROR_INVALID_RANGE);
3047 if (minChars == count) return;
3049 updateFolder(REDRAW_TABS);
3053 * When there is not enough horizontal space to show all the tabs,
3054 * by default, tabs are shown sequentially from left to right in
3055 * order of their index. When the MRU visibility is turned on,
3056 * the tabs that are visible will be the tabs most recently selected.
3057 * Tabs will still maintain their left to right order based on index
3058 * but only the most recently selected tabs are visible.
3060 * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
3061 * "Tab 3" and "Tab 4" (in order by index). The user selects
3062 * "Tab 1" and then "Tab 3". If the CTabFolder is now
3063 * compressed so that only two tabs are visible, by default,
3064 * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
3065 * selected and "Tab 2" because it is the previous item in index order).
3066 * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
3067 * and "Tab 3" (in that order from left to right).</p>
3069 * @param show the new visibility state
3071 * @exception SWTException <ul>
3072 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3073 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3078 public void setMRUVisible(boolean show) {
3080 if (mru == show) return;
3083 if (firstIndex == -1) return;
3084 int idx = firstIndex;
3086 for (int i = firstIndex; i < items.length; i++) {
3087 priority[next++] = i;
3089 for (int i = 0; i < idx; i++) {
3090 priority[next++] = i;
3092 updateFolder(REDRAW_TABS);
3096 * Sets the renderer which is associated with the receiver to be
3097 * the argument which may be null. In the case of null, the default
3100 * @param renderer a new renderer
3102 * @exception SWTException <ul>
3103 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3104 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3107 * @see CTabFolderRenderer
3111 public void setRenderer(CTabFolderRenderer renderer) {
3113 if (this.renderer == renderer || (useDefaultRenderer && renderer == null)) return;
3114 if (this.renderer != null) this.renderer.dispose();
3115 useDefaultRenderer = renderer == null;
3116 if (useDefaultRenderer) renderer = new CTabFolderRenderer(this);
3117 this.renderer = renderer;
3118 updateFolder(REDRAW);
3121 * Set the selection to the tab at the specified item.
3123 * @param item the tab item to be selected
3125 * @exception IllegalArgumentException <ul>
3126 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3129 * @exception SWTException <ul>
3130 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3131 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3134 public void setSelection(CTabItem item) {
3136 if (item == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3137 int index = indexOf(item);
3138 setSelection(index);
3141 * Set the selection to the tab at the specified index.
3143 * @param index the index of the tab item to be selected
3145 * @exception SWTException <ul>
3146 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3147 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3150 public void setSelection(int index) {
3152 if (index < 0 || index >= items.length) return;
3153 CTabItem selection = items[index];
3154 if (selectedIndex == index) {
3155 showItem(selection);
3159 int oldIndex = selectedIndex;
3160 selectedIndex = index;
3161 if (oldIndex != -1) {
3162 items[oldIndex].closeImageState = SWT.BACKGROUND;
3163 items[oldIndex].state &= ~SWT.SELECTED;
3165 selection.closeImageState = SWT.NONE;
3166 selection.showing = false;
3167 selection.state |= SWT.SELECTED;
3169 Control newControl = selection.control;
3170 Control oldControl = null;
3171 if (oldIndex != -1) {
3172 oldControl = items[oldIndex].control;
3175 if (newControl != oldControl) {
3176 if (newControl != null && !newControl.isDisposed()) {
3177 newControl.setBounds(getClientArea());
3178 newControl.setVisible(true);
3180 if (oldControl != null && !oldControl.isDisposed()) {
3181 oldControl.setVisible(false);
3184 showItem(selection);
3187 void setSelection(int index, boolean notify) {
3188 int oldSelectedIndex = selectedIndex;
3189 setSelection(index);
3190 if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
3191 Event event = new Event();
3192 event.item = getItem(selectedIndex);
3193 notifyListeners(SWT.Selection, event);
3197 * Sets the receiver's selection background color to the color specified
3198 * by the argument, or to the default system color for the control
3199 * if the argument is null.
3201 * @param color the new color (or null)
3203 * @exception IllegalArgumentException <ul>
3204 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
3206 * @exception SWTException <ul>
3207 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3208 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3213 public void setSelectionBackground (Color color) {
3214 if (inDispose) return;
3216 setSelectionHighlightGradientColor(null);
3217 if (selectionBackground == color) return;
3218 if (color == null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND);
3219 selectionBackground = color;
3220 renderer.createAntialiasColors(); //TODO: need better caching strategy
3221 if (selectedIndex > -1) redraw();
3224 * Specify a gradient of colours to be draw in the background of the selected tab.
3225 * For example to draw a gradient that varies from dark blue to blue and then to
3226 * white, use the following call to setBackground:
3228 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3229 * display.getSystemColor(SWT.COLOR_BLUE),
3230 * display.getSystemColor(SWT.COLOR_WHITE),
3231 * display.getSystemColor(SWT.COLOR_WHITE)},
3232 * new int[] {25, 50, 100});
3235 * @param colors an array of Color that specifies the colors to appear in the gradient
3236 * in order of appearance left to right. The value <code>null</code> clears the
3237 * background gradient. The value <code>null</code> can be used inside the array of
3238 * Color to specify the background color.
3239 * @param percents an array of integers between 0 and 100 specifying the percent of the width
3240 * of the widget at which the color should change. The size of the percents array must be one
3241 * less than the size of the colors array.
3243 * @exception SWTException <ul>
3244 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3245 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3248 public void setSelectionBackground(Color[] colors, int[] percents) {
3249 setSelectionBackground(colors, percents, false);
3252 * Specify a gradient of colours to be draw in the background of the selected tab.
3253 * For example to draw a vertical gradient that varies from dark blue to blue and then to
3254 * white, use the following call to setBackground:
3256 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3257 * display.getSystemColor(SWT.COLOR_BLUE),
3258 * display.getSystemColor(SWT.COLOR_WHITE),
3259 * display.getSystemColor(SWT.COLOR_WHITE)},
3260 * new int[] {25, 50, 100}, true);
3263 * @param colors an array of Color that specifies the colors to appear in the gradient
3264 * in order of appearance left to right. The value <code>null</code> clears the
3265 * background gradient. The value <code>null</code> can be used inside the array of
3266 * Color to specify the background color.
3267 * @param percents an array of integers between 0 and 100 specifying the percent of the width
3268 * of the widget at which the color should change. The size of the percents array must be one
3269 * less than the size of the colors array.
3271 * @param vertical indicate the direction of the gradient. True is vertical and false is horizontal.
3273 * @exception SWTException <ul>
3274 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3275 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3280 public void setSelectionBackground(Color[] colors, int[] percents, boolean vertical) {
3283 Color highlightBeginColor = null; //null == no highlight
3285 if (colors != null) {
3286 //The colors array can optionally have an extra entry which describes the highlight top color
3287 //Thus its either one or two larger than the percents array
3288 if (percents == null ||
3289 ! ((percents.length == colors.length - 1) || (percents.length == colors.length - 2))){
3290 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3292 for (int i = 0; i < percents.length; i++) {
3293 if (percents[i] < 0 || percents[i] > 100) {
3294 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3296 if (i > 0 && percents[i] < percents[i-1]) {
3297 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3300 //If the colors is exactly two more than percents then last is highlight
3301 //Keep track of *real* colorsLength (minus the highlight)
3302 if(percents.length == colors.length - 2) {
3303 highlightBeginColor = colors[colors.length - 1];
3304 colorsLength = colors.length - 1;
3306 colorsLength = colors.length;
3308 if (getDisplay().getDepth() < 15) {
3309 // Don't use gradients on low color displays
3310 colors = new Color[] {colors[colorsLength - 1]};
3311 colorsLength = colors.length;
3312 percents = new int[] {};
3318 // Are these settings the same as before?
3319 if (selectionBgImage == null) {
3320 if ((selectionGradientColors != null) && (colors != null) &&
3321 (selectionGradientColors.length == colorsLength)) {
3322 boolean same = false;
3323 for (int i = 0; i < selectionGradientColors.length; i++) {
3324 if (selectionGradientColors[i] == null) {
3325 same = colors[i] == null;
3327 same = selectionGradientColors[i].equals(colors[i]);
3332 for (int i = 0; i < selectionGradientPercents.length; i++) {
3333 same = selectionGradientPercents[i] == percents[i];
3337 if (same && this.selectionGradientVertical == vertical) return;
3340 selectionBgImage = null;
3342 // Store the new settings
3343 if (colors == null) {
3344 selectionGradientColors = null;
3345 selectionGradientPercents = null;
3346 selectionGradientVertical = false;
3347 setSelectionBackground((Color)null);
3348 setSelectionHighlightGradientColor(null);
3350 selectionGradientColors = new Color[colorsLength];
3351 for (int i = 0; i < colorsLength; ++i) {
3352 selectionGradientColors[i] = colors[i];
3354 selectionGradientPercents = new int[percents.length];
3355 for (int i = 0; i < percents.length; ++i) {
3356 selectionGradientPercents[i] = percents[i];
3358 selectionGradientVertical = vertical;
3359 setSelectionBackground(selectionGradientColors[selectionGradientColors.length-1]);
3360 setSelectionHighlightGradientColor(highlightBeginColor);
3363 // Refresh with the new settings
3364 if (selectedIndex > -1) redraw();
3368 * Set the color for the highlight start for selected tabs.
3369 * Update the cache of highlight gradient colors if required.
3371 void setSelectionHighlightGradientColor(Color start) {
3372 if (inDispose) return;
3373 renderer.setSelectionHighlightGradientColor(start); //TODO: need better caching strategy
3377 * Set the image to be drawn in the background of the selected tab. Image
3378 * is stretched or compressed to cover entire selection tab area.
3380 * @param image the image to be drawn in the background
3382 * @exception SWTException <ul>
3383 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3384 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3387 public void setSelectionBackground(Image image) {
3389 setSelectionHighlightGradientColor(null);
3390 if (image == selectionBgImage) return;
3391 if (image != null) {
3392 selectionGradientColors = null;
3393 selectionGradientPercents = null;
3394 renderer.disposeSelectionHighlightGradientColors(); //TODO: need better caching strategy
3396 selectionBgImage = image;
3397 renderer.createAntialiasColors(); //TODO: need better caching strategy
3398 if (selectedIndex > -1) redraw();
3401 * Set the foreground color of the selected tab.
3403 * @param color the color of the text displayed in the selected tab
3405 * @exception SWTException <ul>
3406 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3407 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3410 public void setSelectionForeground (Color color) {
3412 if (selectionForeground == color) return;
3413 if (color == null) color = getDisplay().getSystemColor(SELECTION_FOREGROUND);
3414 selectionForeground = color;
3415 if (selectedIndex > -1) redraw();
3419 * Sets the shape that the CTabFolder will use to render itself.
3421 * @param simple <code>true</code> if the CTabFolder should render itself in a simple, traditional style
3423 * @exception SWTException <ul>
3424 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3425 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3430 public void setSimple(boolean simple) {
3432 if (this.simple != simple) {
3433 this.simple = simple;
3434 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3438 * Sets the number of tabs that the CTabFolder should display
3440 * @param single <code>true</code> if only the selected tab should be displayed otherwise, multiple tabs will be shown.
3442 * @exception SWTException <ul>
3443 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3444 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3449 public void setSingle(boolean single) {
3451 if (this.single != single) {
3452 this.single = single;
3454 for (int i = 0; i < items.length; i++) {
3455 if (i != selectedIndex && items[i].closeImageState == SWT.NONE) {
3456 items[i].closeImageState = SWT.BACKGROUND;
3460 updateFolder(REDRAW);
3464 int getControlY(Point size, Rectangle[] rects, int borderBottom, int borderTop, int i) {
3465 int center = fixedTabHeight != SWT.DEFAULT ? 0 : (tabHeight - rects[i].height)/2;
3466 return onBottom ? size.y - borderBottom - tabHeight + center : 1 + borderTop + center;
3470 * Specify a fixed height for the tab items. If no height is specified,
3471 * the default height is the height of the text or the image, whichever
3472 * is greater. Specifying a height of -1 will revert to the default height.
3474 * @param height the point value of the height or -1
3476 * @exception SWTException <ul>
3477 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3478 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3479 * <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
3482 public void setTabHeight(int height) {
3485 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3487 fixedTabHeight = height;
3488 updateFolder(UPDATE_TAB_HEIGHT);
3491 * Specify whether the tabs should appear along the top of the folder
3492 * or along the bottom of the folder.
3494 * @param position <code>SWT.TOP</code> for tabs along the top or <code>SWT.BOTTOM</code> for tabs along the bottom
3496 * @exception SWTException <ul>
3497 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3498 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3499 * <li>ERROR_INVALID_ARGUMENT - if the position value is not either SWT.TOP or SWT.BOTTOM</li>
3504 public void setTabPosition(int position) {
3506 if (position != SWT.TOP && position != SWT.BOTTOM) {
3507 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3509 if (onBottom != (position == SWT.BOTTOM)) {
3510 onBottom = position == SWT.BOTTOM;
3511 updateFolder(REDRAW);
3515 * Set the control that appears in the top right corner of the tab folder.
3516 * Typically this is a close button or a composite with a Menu and close button.
3517 * The topRight control is optional. Setting the top right control to null will
3518 * remove it from the tab folder.
3520 * @param control the control to be displayed in the top right corner or null
3522 * @exception SWTException <ul>
3523 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3524 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3525 * <li>ERROR_INVALID_ARGUMENT - if the control is disposed, or not a child of this CTabFolder</li>
3530 public void setTopRight(Control control) {
3531 setTopRight(control, SWT.RIGHT);
3534 * Set the control that appears in the top right corner of the tab folder.
3535 * Typically this is a close button or a composite with a Menu and close button.
3536 * The topRight control is optional. Setting the top right control to null
3537 * will remove it from the tab folder.
3539 * The alignment parameter sets the layout of the control in the tab area.
3540 * <code>SWT.RIGHT</code> will cause the control to be positioned on the far
3541 * right of the folder and it will have its default size. <code>SWT.FILL</code>
3542 * will size the control to fill all the available space to the right of the
3543 * last tab. If there is no available space, the control will not be visible.
3544 * <code>SWT.RIGHT | SWT.WRAP</code> will allow the control to wrap below the
3545 * tabs if there is not enough available space to the right of the last tab.
3548 * @param control the control to be displayed in the top right corner or null
3549 * @param alignment <code>SWT.RIGHT</code> or <code>SWT.FILL</code> or <code>SWT.RIGHT | SWT.WRAP</code>
3551 * @exception SWTException <ul>
3552 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3553 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3554 * <li>ERROR_INVALID_ARGUMENT - if the control is disposed, or not a child of this CTabFolder</li>
3559 public void setTopRight(Control control, int alignment) {
3561 if (alignment != SWT.RIGHT && alignment != SWT.FILL && alignment != (SWT.RIGHT | SWT.WRAP)) {
3562 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3564 if (control != null && (control.isDisposed() || control.getParent() != this)) {
3565 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3567 if (topRight == control && topRightAlignment == alignment) return;
3568 if (topRight != null && !topRight.isDisposed()) removeTabControl(topRight, false);
3570 topRightAlignment = alignment;
3571 alignment &= ~SWT.RIGHT;
3572 if (control != null) addTabControl(control, SWT.TRAIL | alignment, -1, false);
3573 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3578 * Specify whether the close button appears
3579 * when the user hovers over an unselected tabs.
3581 * @param visible <code>true</code> makes the close button appear
3583 * @exception SWTException <ul>
3584 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3585 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3590 public void setUnselectedCloseVisible(boolean visible) {
3592 if (showUnselectedClose == visible) return;
3593 // display close button when mouse hovers
3594 showUnselectedClose = visible;
3595 updateFolder(REDRAW);
3598 * Specify whether the image appears on unselected tabs.
3600 * @param visible <code>true</code> makes the image appear
3602 * @exception SWTException <ul>
3603 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3604 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3609 public void setUnselectedImageVisible(boolean visible) {
3611 if (showUnselectedImage == visible) return;
3612 // display image on unselected items
3613 showUnselectedImage = visible;
3614 updateFolder(REDRAW);
3617 * Shows the item. If the item is already showing in the receiver,
3618 * this method simply returns. Otherwise, the items are scrolled until
3619 * the item is visible.
3621 * @param item the item to be shown
3623 * @exception IllegalArgumentException <ul>
3624 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3625 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
3627 * @exception SWTException <ul>
3628 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3629 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3632 * @see CTabFolder#showSelection()
3636 public void showItem (CTabItem item) {
3638 if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
3639 if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3640 int index = indexOf(item);
3641 if (index == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3643 for (int i = 0; i < priority.length; i++) {
3644 if (priority[i] == index) {
3650 // move to front of mru order
3651 int[] newPriority = new int[priority.length];
3652 System.arraycopy(priority, 0, newPriority, 1, idx);
3653 System.arraycopy(priority, idx+1, newPriority, idx+1, priority.length - idx - 1);
3654 newPriority[0] = index;
3655 priority = newPriority;
3657 if (item.showing) return;
3658 updateFolder(REDRAW_TABS);
3660 void showList (Rectangle rect) {
3661 if (items.length == 0 || !showChevron) return;
3662 if (showMenu == null || showMenu.isDisposed()) {
3663 showMenu = new Menu(getShell(), getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT));
3665 MenuItem[] items = showMenu.getItems();
3666 for (int i = 0; i < items.length; i++) {
3670 final String id = "CTabFolder_showList_Index"; //$NON-NLS-1$
3671 for (int i = 0; i < items.length; i++) {
3672 CTabItem tab = items[i];
3673 if (tab.showing) continue;
3674 MenuItem item = new MenuItem(showMenu, SWT.NONE);
3675 // Bug 533124 In the case where you have multi line tab text, we force the drop-down menu to have single line entries to ensure consistent behavior across platforms.
3676 item.setText(tab.getText().replace("\n", " "));
3677 item.setImage(tab.getImage());
3678 item.setData(id, tab);
3679 item.addSelectionListener(new SelectionAdapter() {
3681 public void widgetSelected(SelectionEvent e) {
3682 MenuItem menuItem = (MenuItem)e.widget;
3683 int index = indexOf((CTabItem)menuItem.getData(id));
3684 CTabFolder.this.setSelection(index, true);
3689 int y = rect.y + rect.height;
3690 Point location = getDisplay().map(this, null, x, y);
3691 showMenu.setLocation(location.x, location.y);
3692 showMenu.setVisible(true);
3695 * Shows the selection. If the selection is already showing in the receiver,
3696 * this method simply returns. Otherwise, the items are scrolled until
3697 * the selection is visible.
3699 * @exception SWTException <ul>
3700 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3701 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3704 * @see CTabFolder#showItem(CTabItem)
3708 public void showSelection () {
3710 if (selectedIndex != -1) {
3711 showItem(getSelection());
3715 void _setToolTipText (int x, int y) {
3716 String oldTip = getToolTipText();
3717 String newTip = _getToolTip(x, y);
3718 if (newTip == null || !newTip.equals(oldTip)) {
3719 setToolTipText(newTip);
3723 boolean updateItems() {
3724 return updateItems(selectedIndex);
3727 boolean updateItems (int showIndex) {
3728 GC gc = new GC(this);
3729 if (!single && !mru && showIndex != -1) {
3730 // make sure selected item will be showing
3731 int firstIndex = showIndex;
3732 if (priority[0] < showIndex) {
3733 int maxWidth = getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
3735 int[] widths = new int[items.length];
3736 for (int i = priority[0]; i <= showIndex; i++) {
3737 int state = CTabFolderRenderer.MINIMUM_SIZE;
3738 if (i == selectedIndex) state |= SWT.SELECTED;
3739 widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3741 if (width > maxWidth) break;
3743 if (width > maxWidth) {
3745 for (int i = showIndex; i >= 0; i--) {
3746 int state = CTabFolderRenderer.MINIMUM_SIZE;
3747 if (i == selectedIndex) state |= SWT.SELECTED;
3748 if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3750 if (width > maxWidth) break;
3754 firstIndex = priority[0];
3755 for (int i = showIndex + 1; i < items.length; i++) {
3756 int state = CTabFolderRenderer.MINIMUM_SIZE;
3757 if (i == selectedIndex) state |= SWT.SELECTED;
3758 widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3760 if (width >= maxWidth) break;
3762 if (width < maxWidth) {
3763 for (int i = priority[0] - 1; i >= 0; i--) {
3764 int state = CTabFolderRenderer.MINIMUM_SIZE;
3765 if (i == selectedIndex) state |= SWT.SELECTED;
3766 if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3768 if (width > maxWidth) break;
3775 if (firstIndex != priority[0]) {
3777 // enumerate tabs from first visible to the last existing one (sorted ascending)
3778 for (int i = firstIndex; i < items.length; i++) {
3779 priority[index++] = i;
3781 // enumerate hidden tabs on the left hand from first visible one
3782 // in the inverse order (sorted descending) so that the originally
3783 // first opened tab is always at the end of the list
3784 for (int i = firstIndex - 1; i >= 0; i--) {
3785 priority[index++] = i;
3790 boolean oldShowChevron = showChevron;
3791 boolean changed = setItemSize(gc);
3792 changed |= setItemLocation(gc);
3793 setButtonBounds(gc);
3794 changed |= showChevron != oldShowChevron;
3795 if (changed && getToolTipText() != null) {
3796 Point pt = getDisplay().getCursorLocation();
3798 _setToolTipText(pt.x, pt.y);
3803 boolean updateTabHeight(boolean force){
3804 int oldHeight = tabHeight;
3805 GC gc = new GC(this);
3806 tabHeight = renderer.computeSize(CTabFolderRenderer.PART_HEADER, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT).y;
3808 if (fixedTabHeight == SWT.DEFAULT && controls != null && controls.length > 0) {
3809 for (int i = 0; i < controls.length; i++) {
3810 if ((controlAlignments[i] & SWT.WRAP) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
3811 int topHeight = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
3812 topHeight += renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0,0,0,0).height + 1;
3813 tabHeight = Math.max(topHeight, tabHeight);
3817 if (!force && tabHeight == oldHeight) return false;
3822 void updateFolder (int flags) {
3823 updateFlags |= flags;
3824 if (updateRun != null) return;
3827 if (isDisposed()) return;
3830 getDisplay().asyncExec(updateRun);
3834 if (updateFlags == 0) return;
3835 int flags = updateFlags;
3837 Rectangle rectBefore = getClientArea();
3838 updateTabHeight(false);
3839 updateItems(selectedIndex);
3840 if ((flags & REDRAW) != 0) {
3842 } else if ((flags & REDRAW_TABS) != 0) {
3845 Rectangle rectAfter = getClientArea();
3846 if (!rectBefore.equals(rectAfter)) {
3847 notifyListeners(SWT.Resize, new Event());
3852 void updateBkImages() {
3853 if (controls != null && controls.length > 0) {
3854 for (int i = 0; i < controls.length; i++) {
3855 Control control = controls[i];
3856 if (!control.isDisposed()) {
3858 if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
3859 control.setBackgroundImage(null);
3860 control.setBackground(getBackground());
3862 if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_DEFAULT);
3863 Rectangle bounds = control.getBounds();
3864 int tabHeight = getTabHeight();
3865 int height = this.getSize().y;
3866 boolean wrapped = onBottom ? bounds.y + bounds.height < height - tabHeight : bounds.y > tabHeight;
3867 if (wrapped || gradientColors == null) {
3868 control.setBackgroundImage(null);
3869 control.setBackground(getBackground());
3873 bounds.y = -bounds.y;
3874 bounds.height -= 2*bounds.y - 1;
3876 bounds.height += height - (bounds.y + bounds.height);
3880 if (controlBkImages[i] != null) controlBkImages[i].dispose();
3881 controlBkImages[i] = new Image(control.getDisplay(), bounds);
3882 GC gc = new GC(controlBkImages[i]);
3883 renderer.draw(CTabFolderRenderer.PART_BACKGROUND, 0, bounds, gc);
3885 control.setBackground(null);
3886 control.setBackgroundImage(controlBkImages[i]);
3894 String _getToolTip(int x, int y) {
3895 CTabItem item = getItem(new Point (x, y));
3896 if (item == null) return null;
3897 if (!item.showing) return null;
3898 if ((showClose || item.showClose) && item.closeRect.contains(x, y)) {
3899 return SWT.getMessage("SWT_Close"); //$NON-NLS-1$
3901 return item.getToolTipText();
3904 * Set a control that can appear to the left or to the right of the folder tabs.
3905 * This method can also be used instead of #setTopRight(Control). To remove a tab
3906 * control, see#removeTabControl(Control);
3908 * The flags parameter sets the layout of the control in the tab area.
3909 * <code>SWT.LEAD</code> will cause the control to be positioned on the left
3910 * of the tabs. <code>SWT.TRAIL</code> will cause the control to be positioned on
3911 * the far right of the folder and it will have its default size. <code>SWT.TRAIL</code>
3912 * can be combined with <code>SWT.FILL</code>to fill all the available space to the
3913 * right of the last tab. <code>SWT.WRAP</code> can also be added to <code>SWT.TRAIL</code>
3914 * only to cause a control to wrap if there is not enough space to display it in its
3917 * @param control the control to be displayed in the top right corner or null
3919 * @param flags valid combinations are:
3921 * <li> SWT.TRAIL (| SWT.FILL | SWT.WRAP)
3923 * @exception SWTException <ul>
3924 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3925 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3926 * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
3929 /*public*/ void addTabControl(Control control, int flags) {
3931 addTabControl(control, flags, -1, true);
3934 void addTabControl(Control control, int flags, int index, boolean update) {
3937 case SWT.TRAIL | SWT.WRAP:
3938 case SWT.TRAIL | SWT.FILL:
3939 case SWT.TRAIL | SWT.FILL | SWT.WRAP:
3943 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3946 if (control != null && control.getParent() != this) {
3947 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3949 //check for duplicates
3950 for (int i = 0; i < controls.length; i++) {
3951 if (controls[i] == control) {
3952 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3955 int length = controls.length;
3957 control.addListener(SWT.Resize, listener);
3960 Control[] newControls = new Control [length + 1];
3961 System.arraycopy(controls, 0, newControls, 0, length);
3962 controls = newControls;
3963 int[] newAlignment = new int [length + 1];
3964 System.arraycopy(controlAlignments, 0, newAlignment, 0, length);
3965 controlAlignments = newAlignment;
3966 Rectangle[] newRect = new Rectangle [length + 1];
3967 System.arraycopy(controlRects, 0, newRect, 0, length);
3968 controlRects = newRect;
3969 Image[] newImage = new Image [length + 1];
3970 System.arraycopy(controlBkImages, 0, newImage, 0, length);
3971 controlBkImages = newImage;
3974 if (chevronTb != null && control != chevronTb) index--;
3976 System.arraycopy (controls, index, controls, index + 1, length - index);
3977 System.arraycopy (controlAlignments, index, controlAlignments, index + 1, length - index);
3978 System.arraycopy (controlRects, index, controlRects, index + 1, length - index);
3979 System.arraycopy (controlBkImages, index, controlBkImages, index + 1, length - index);
3980 controls[index] = control;
3981 controlAlignments[index] = flags;
3982 controlRects[index] = new Rectangle(0, 0, 0, 0);
3984 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3989 * Removes the control from the list of tab controls.
3991 * @param control the control to be removed
3993 * @exception SWTException <ul>
3994 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3995 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3996 * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
3999 /*public*/ void removeTabControl (Control control) {
4001 removeTabControl (control, true);
4004 void removeTabControl (Control control, boolean update) {
4005 if (control != null && control.getParent() != this) {
4006 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4009 for (int i = 0; i < controls.length; i++) {
4010 if (controls[i] == control){
4015 if (index == -1) return;
4017 if (!control.isDisposed()) {
4018 control.removeListener(SWT.Resize, listener);
4019 control.setBackground (null);
4020 control.setBackgroundImage (null);
4021 if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
4024 if (controlBkImages[index] != null && !controlBkImages[index].isDisposed()) controlBkImages[index].dispose();
4025 if (controls.length == 1) {
4026 controls = new Control[0];
4027 controlAlignments = new int[0];
4028 controlRects = new Rectangle[0];
4029 controlBkImages = new Image[0];
4031 Control[] newControls = new Control [controls.length - 1];
4032 System.arraycopy(controls, 0, newControls, 0, index);
4033 System.arraycopy(controls, index + 1, newControls, index, controls.length - index - 1);
4034 controls = newControls;
4036 int[] newAlignments = new int [controls.length];
4037 System.arraycopy(controlAlignments, 0, newAlignments, 0, index);
4038 System.arraycopy(controlAlignments, index + 1, newAlignments, index, controls.length - index);
4039 controlAlignments = newAlignments;
4041 Rectangle[] newRects = new Rectangle [controls.length];
4042 System.arraycopy(controlRects, 0, newRects, 0, index);
4043 System.arraycopy(controlRects, index + 1, newRects, index, controls.length - index);
4044 controlRects = newRects;
4046 Image[] newBkImages = new Image [controls.length];
4047 System.arraycopy(controlBkImages, 0, newBkImages, 0, index);
4048 System.arraycopy(controlBkImages, index + 1, newBkImages, index, controls.length - index);
4049 controlBkImages = newBkImages;
4052 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4056 int getWrappedHeight (Point size) {
4057 boolean[][] positions = new boolean[1][];
4058 Rectangle[] rects = computeControlBounds(size, positions);
4059 int minY = Integer.MAX_VALUE, maxY = 0, wrapHeight = 0;
4060 for (int i = 0; i < rects.length; i++) {
4061 if (positions[0][i]) {
4062 minY = Math.min(minY, rects[i].y);
4063 maxY = Math.max(maxY, rects[i].y + rects[i].height);
4064 wrapHeight = maxY - minY;
4071 * Sets whether a chevron is shown when there are more items to be displayed.
4073 * @exception IllegalArgumentException <ul>
4074 * <li>ERROR_INVALID_RANGE - if the index is out of range</li>
4076 * @exception SWTException <ul>
4077 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
4078 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
4082 /*public*/ void setChevronVisible(boolean visible) {
4084 if (chevronVisible == visible) return;
4085 chevronVisible = visible;
4086 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4089 boolean shouldHighlight() {
4090 return this.highlight && highlightEnabled;
4094 * Sets whether the selected tab is rendered as highlighted.
4097 * {@code true} if the selected tab should be highlighted,
4098 * {@code false} otherwise.
4099 * @exception SWTException
4101 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been
4103 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
4104 * thread that created the receiver</li>
4108 public void setHighlightEnabled(boolean enabled) {
4110 if (highlightEnabled == enabled) {
4113 highlightEnabled = enabled;
4114 updateFolder(REDRAW);
4118 * Returns <code>true</code> if the selected tab is rendered as
4121 * @return <code>true</code> if the selected tab is rendered as
4124 * @exception SWTException
4126 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been
4128 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
4129 * thread that created the receiver</li>
4133 public boolean getHighlightEnabled() {
4135 return highlightEnabled;