X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.eclipse.swt.win32.win32.x86_64%2Fsrc%2Forg%2Feclipse%2Fswt%2Fcustom%2FCTabFolder.java;fp=bundles%2Forg.eclipse.swt.win32.win32.x86_64%2Fsrc%2Forg%2Feclipse%2Fswt%2Fcustom%2FCTabFolder.java;h=63d9676f57b6d57fed470366204b11e9675e43a4;hb=6b98970d0458754dd67f789afbd0a39e1e7ac6eb;hp=0000000000000000000000000000000000000000;hpb=56a61575ce0d27b340cb12438c8a7f303842095e;p=simantics%2Fplatform.git diff --git a/bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/custom/CTabFolder.java b/bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/custom/CTabFolder.java new file mode 100644 index 000000000..63d9676f5 --- /dev/null +++ b/bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/custom/CTabFolder.java @@ -0,0 +1,4137 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.custom; + +import org.eclipse.swt.*; +import org.eclipse.swt.accessibility.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.DPIUtil.*; +import org.eclipse.swt.widgets.*; + +/** + * + * Instances of this class implement the notebook user interface + * metaphor. It allows the user to select a notebook page from + * set of pages. + *

+ * The item children that may be added to instances of this class + * must be of type CTabItem. + * Control children are created and then set into a + * tab item using CTabItem#setControl. + *

+ * Note that although this class is a subclass of Composite, + * it does not make sense to set a layout on it. + *

+ *
+ *
Styles:
+ *
CLOSE, TOP, BOTTOM, FLAT, BORDER, SINGLE, MULTI
+ *
Events:
+ *
Selection
+ *
"CTabFolder2"
+ *
+ * Note: Only one of the styles TOP and BOTTOM + * may be specified. + *

+ * IMPORTANT: This class is not intended to be subclassed. + *

+ * + * @see CTabFolder, CTabItem snippets + * @see SWT Example: CustomControlExample + * @see Sample code and further information + * @noextend This class is not intended to be subclassed by clients. + */ + +public class CTabFolder extends Composite { + + /** + * marginWidth specifies the number of points of horizontal margin + * that will be placed along the left and right edges of the form. + * + * The default value is 0. + */ + public int marginWidth = 0; + /** + * marginHeight specifies the number of points of vertical margin + * that will be placed along the top and bottom edges of the form. + * + * The default value is 0. + */ + public int marginHeight = 0; + + /** + * A multiple of the tab height that specifies the minimum width to which a tab + * will be compressed before scrolling arrows are used to navigate the tabs. + * + * NOTE This field is badly named and can not be fixed for backwards compatibility. + * It should not be capitalized. + * + * @deprecated This field is no longer used. See setMinimumCharacters(int) + */ + @Deprecated + public int MIN_TAB_WIDTH = 4; + + /** + * Color of innermost line of drop shadow border. + * + * NOTE This field is badly named and can not be fixed for backwards compatibility. + * It should be capitalized. + * + * @deprecated drop shadow border is no longer drawn in 3.0 + */ + @Deprecated + public static RGB borderInsideRGB = new RGB (132, 130, 132); + /** + * Color of middle line of drop shadow border. + * + * NOTE This field is badly named and can not be fixed for backwards compatibility. + * It should be capitalized. + * + * @deprecated drop shadow border is no longer drawn in 3.0 + */ + @Deprecated + public static RGB borderMiddleRGB = new RGB (143, 141, 138); + /** + * Color of outermost line of drop shadow border. + * + * NOTE This field is badly named and can not be fixed for backwards compatibility. + * It should be capitalized. + * + * @deprecated drop shadow border is no longer drawn in 3.0 + */ + @Deprecated + public static RGB borderOutsideRGB = new RGB (171, 168, 165); + + /* sizing, positioning */ + boolean onBottom = false; + boolean single = false; + boolean simple = true; + int fixedTabHeight = SWT.DEFAULT; + int tabHeight; + int minChars = 20; + boolean borderVisible = false; + + /* item management */ + CTabFolderRenderer renderer; + CTabItem items[] = new CTabItem[0]; + /** index of the left most visible tab. */ + int firstIndex = -1; + int selectedIndex = -1; + + /** + * Indices of the elements in the {@link #items} array, used to manage tab + * visibility and candidates to be hidden/shown next. + *

+ * If there is not enough place for all tabs, tabs starting from the end of + * the {@link #priority} array will be hidden first (independently from the + * {@link #mru} flag!) => the right most elements have the highest priority + * to be hidden. + *

+ * If there is more place to show previously hidden tabs, tabs starting from + * the beginning of the {@link #priority} array will be made visible first + * (independently from the {@link #mru} flag!) => the left most elements + * have the highest priority to be shown. + *

+ * The update strategy of the {@link #priority} array however depends on the + * {@link #mru} flag. + *

+ * If {@link #mru} flag is set, the first index is always the index of the + * currently selected tab, next one is the tab selected before current + * etc... + *

+ * Example: [4,2,5,1,3,0], just representing the last selection order. + *

+ * If {@link #mru} flag is not set, the first index is always the index of + * the left most visible tab ({@link #firstIndex} field), next indices are + * incremented by one up to priority.length-1, and the rest + * filled with indices starting with firstIndex-1 and + * decremented by one until 0 index is reached. + *

+ * The tabs between first index and the index of the currently selected tab + * are always visible. + *

+ * Example: 6 tabs, 2 and 3 are indices of currently shown tabs: + * [2,3,4,5,1,0]. The array consists of two blocks: sorted ascending from + * first visible (2) to last available (5), and the rest sorted descending + * (1,0). 4 and 5 are the hidden tabs on the right side, 0 and 1 are the + * hidden tabs on the left side from the visible tabs 2 and 3. + * + * @see #updateItems(int) + * @see #setItemLocation(GC) + */ + int[] priority = new int[0]; + boolean mru = false; + Listener listener; + boolean ignoreTraverse; + boolean useDefaultRenderer; + + /* External Listener management */ + CTabFolder2Listener[] folderListeners = new CTabFolder2Listener[0]; + // support for deprecated listener mechanism + CTabFolderListener[] tabListeners = new CTabFolderListener[0]; + + /* Selected item appearance */ + Image selectionBgImage; + Color[] selectionGradientColors; + int[] selectionGradientPercents; + boolean selectionGradientVertical; + Color selectionForeground; + Color selectionBackground; + + /* Unselected item appearance */ + Color[] gradientColors; + int[] gradientPercents; + boolean gradientVertical; + boolean showUnselectedImage = true; + + // close, min/max and chevron buttons + boolean showClose = false; + boolean showUnselectedClose = true; + + boolean showMin = false; + boolean minimized = false; + boolean showMax = false; + boolean maximized = false; + ToolBar minMaxTb; + ToolItem maxItem; + ToolItem minItem; + Image maxImage; + Image minImage; + boolean hoverTb; + Rectangle hoverRect = new Rectangle(0,0,0,0); + boolean hovering; + boolean hoverTimerRunning; + boolean highlight; + boolean highlightEnabled = true; + + boolean showChevron = false; + Menu showMenu; + ToolBar chevronTb; + ToolItem chevronItem; + int chevronCount; + boolean chevronVisible = true; + + Image chevronImage; + Control topRight; + int topRightAlignment = SWT.RIGHT; + boolean ignoreResize; + Control[] controls; + int[] controlAlignments; + Rectangle[] controlRects; + Image[] controlBkImages; + + int updateFlags; + final static int REDRAW = 1 << 1; + final static int REDRAW_TABS = 1 << 2; + final static int UPDATE_TAB_HEIGHT = 1 << 3; + Runnable updateRun; + + // when disposing CTabFolder, don't try to layout the items or + // change the selection as each child is destroyed. + boolean inDispose = false; + + // keep track of size changes in order to redraw only affected area + // on Resize + Point oldSize; + Font oldFont; + + // internal constants + static final int DEFAULT_WIDTH = 64; + static final int DEFAULT_HEIGHT = 64; + + static final int SELECTION_FOREGROUND = SWT.COLOR_LIST_FOREGROUND; + static final int SELECTION_BACKGROUND = SWT.COLOR_LIST_BACKGROUND; + + static final int FOREGROUND = SWT.COLOR_WIDGET_FOREGROUND; + static final int BACKGROUND = SWT.COLOR_WIDGET_BACKGROUND; + + //TODO: add setter for spacing? + static final int SPACING = 3; +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + *

+ * The style value is either one of the style constants defined in + * class SWT which is applicable to instances of this + * class, or must be built by bitwise OR'ing together + * (that is, using the int "|" operator) two or more + * of those SWT style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + *

+ * + * @param parent a widget which will be the parent of the new instance (cannot be null) + * @param style the style of widget to construct + * + * @exception IllegalArgumentException + * @exception SWTException + * + * @see SWT#TOP + * @see SWT#BOTTOM + * @see SWT#FLAT + * @see SWT#BORDER + * @see SWT#SINGLE + * @see SWT#MULTI + * @see #getStyle() + */ +public CTabFolder(Composite parent, int style) { + super(parent, checkStyle (parent, style)); + init(style); +} + +void init(int style) { + super.setLayout(new CTabFolderLayout()); + int style2 = super.getStyle(); + oldFont = getFont(); + onBottom = (style2 & SWT.BOTTOM) != 0; + showClose = (style2 & SWT.CLOSE) != 0; +// showMin = (style2 & SWT.MIN) != 0; - conflicts with SWT.TOP +// showMax = (style2 & SWT.MAX) != 0; - conflicts with SWT.BOTTOM + single = (style2 & SWT.SINGLE) != 0; + borderVisible = (style & SWT.BORDER) != 0; + //set up default colors + Display display = getDisplay(); + selectionForeground = display.getSystemColor(SELECTION_FOREGROUND); + selectionBackground = display.getSystemColor(SELECTION_BACKGROUND); + renderer = new CTabFolderRenderer(this); + useDefaultRenderer = true; + controls = new Control[0]; + controlAlignments = new int[0]; + controlRects = new Rectangle[0]; + controlBkImages = new Image[0]; + updateTabHeight(false); + + // Add all listeners + listener = event -> { + switch (event.type) { + case SWT.Dispose: onDispose(event); break; + case SWT.DragDetect: onDragDetect(event); break; + case SWT.FocusIn: onFocus(event); break; + case SWT.FocusOut: onFocus(event); break; + case SWT.KeyDown: onKeyDown(event); break; + case SWT.MenuDetect: onMenuDetect(event); break; + case SWT.MouseDoubleClick: onMouseDoubleClick(event); break; + case SWT.MouseDown: onMouse(event); break; + case SWT.MouseEnter: onMouse(event); break; + case SWT.MouseExit: onMouse(event); break; + case SWT.MouseHover: onMouse(event); break; + case SWT.MouseMove: onMouse(event); break; + case SWT.MouseUp: onMouse(event); break; + case SWT.Paint: onPaint(event); break; + case SWT.Resize: onResize(event); break; + case SWT.Traverse: onTraverse(event); break; + case SWT.Selection: onSelection(event); break; + case SWT.Activate: onActivate(event); break; + case SWT.Deactivate: onDeactivate(event); break; + } + }; + + int[] folderEvents = new int[]{ + SWT.Dispose, + SWT.DragDetect, + SWT.FocusIn, + SWT.FocusOut, + SWT.KeyDown, + SWT.MenuDetect, + SWT.MouseDoubleClick, + SWT.MouseDown, + SWT.MouseEnter, + SWT.MouseExit, + SWT.MouseHover, + SWT.MouseMove, + SWT.MouseUp, + SWT.Paint, + SWT.Resize, + SWT.Traverse, + SWT.Activate, + SWT.Deactivate + }; + for (int i = 0; i < folderEvents.length; i++) { + addListener(folderEvents[i], listener); + } + + initAccessible(); +} +void onDeactivate(Event event) { + if (!highlightEnabled) { + return; + } + this.highlight = false; + redraw(); +} + +void onActivate(Event event) { + if (!highlightEnabled) { + return; + } + this.highlight = true; + redraw(); +} + +static int checkStyle (Composite parent, int style) { + int mask = SWT.CLOSE | SWT.TOP | SWT.BOTTOM | SWT.FLAT | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.SINGLE | SWT.MULTI; + style = style & mask; + // TOP and BOTTOM are mutually exclusive. + // TOP is the default + if ((style & SWT.TOP) != 0) style = style & ~SWT.BOTTOM; + // SINGLE and MULTI are mutually exclusive. + // MULTI is the default + if ((style & SWT.MULTI) != 0) style = style & ~SWT.SINGLE; + // reduce the flash by not redrawing the entire area on a Resize event + style |= SWT.NO_REDRAW_RESIZE; + + //TEMPORARY CODE + /* + * In Right To Left orientation on Windows, all GC calls that use a brush are drawing + * offset by one pixel. This results in some parts of the CTabFolder not drawing correctly. + * To alleviate some of the appearance problems, allow the OS to draw the background. + * This does not draw correctly but the result is less obviously wrong. + */ + if ((style & SWT.RIGHT_TO_LEFT) != 0) return style; + if ((parent.getStyle() & SWT.MIRRORED) != 0 && (style & SWT.LEFT_TO_RIGHT) == 0) return style; + + return style | SWT.DOUBLE_BUFFERED; +} + +/** + * + * Adds the listener to the collection of listeners who will + * be notified when a tab item is closed, minimized, maximized, + * restored, or to show the list of items that are not + * currently visible. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException + * + * @exception SWTException + * + * @see CTabFolder2Listener + * @see #removeCTabFolder2Listener(CTabFolder2Listener) + * + * @since 3.0 + */ +public void addCTabFolder2Listener(CTabFolder2Listener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + // add to array + CTabFolder2Listener[] newListeners = new CTabFolder2Listener[folderListeners.length + 1]; + System.arraycopy(folderListeners, 0, newListeners, 0, folderListeners.length); + folderListeners = newListeners; + folderListeners[folderListeners.length - 1] = listener; +} +/** + * Adds the listener to the collection of listeners who will + * be notified when a tab item is closed. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException + * @exception SWTException + * + * @see CTabFolderListener + * @see #removeCTabFolderListener(CTabFolderListener) + * + * @deprecated use addCTabFolder2Listener(CTabFolder2Listener) + */ +@Deprecated +public void addCTabFolderListener(CTabFolderListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + // add to array + CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1]; + System.arraycopy(tabListeners, 0, newTabListeners, 0, tabListeners.length); + tabListeners = newTabListeners; + tabListeners[tabListeners.length - 1] = listener; + // display close button to be backwards compatible + if (!showClose) { + showClose = true; + updateFolder(REDRAW); + } +} +/** + * Adds the listener to the collection of listeners who will + * be notified when the user changes the receiver's selection, by sending + * it one of the messages defined in the SelectionListener + * interface. + *

+ * widgetSelected is called when the user changes the selected tab. + * widgetDefaultSelected is not called. + *

+ * + * @param listener the listener which should be notified when the user changes the receiver's selection + * + * @exception IllegalArgumentException + * @exception SWTException + * + * @see SelectionListener + * @see #removeSelectionListener + * @see SelectionEvent + */ +public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection, typedListener); + addListener(SWT.DefaultSelection, typedListener); +} + +Rectangle[] computeControlBounds (Point size, boolean[][] position) { + if (controls == null || controls.length == 0) return new Rectangle[0]; + Rectangle[] rects = new Rectangle[controls.length]; + for (int i = 0; i < rects.length; i++) { + rects[i] = new Rectangle(0, 0, 0, 0); + } + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0); + int borderRight = trim.width + trim.x; + int borderLeft = -trim.x; + int borderBottom = trim.height + trim.y; + int borderTop = -trim.y; + + Point[] tabControlSize = new Point[controls.length]; + boolean[] overflow = new boolean [controls.length]; + //Left Control + int leftWidth = 0; + int x = borderLeft + SPACING; + int rightWidth = 0; + int allWidth = 0; + for (int i = 0; i < controls.length; i++) { + Point ctrlSize = tabControlSize[i] = !controls[i].isDisposed() && controls[i].getVisible() ? controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT) : new Point(0,0); + int alignment = controlAlignments[i]; + if ((alignment & SWT.LEAD) != 0) { + rects[i].width = ctrlSize.x; + rects[i].height = getControlHeight(ctrlSize); + rects[i].x = x; + rects[i].y = getControlY(size, rects, borderBottom, borderTop, i); + x += ctrlSize.x; + leftWidth += ctrlSize.x; + } else { + if ((alignment & (SWT.FILL | SWT.WRAP)) == 0) { + rightWidth += ctrlSize.x; + } + allWidth += ctrlSize.x; + } + } + if (leftWidth > 0) leftWidth += SPACING * 2; + + int itemWidth = 0; + for (int i = 0; i < items.length; i++) { + if (items[i].showing) itemWidth += items[i].width; + } + + int maxWidth = size.x - borderLeft - leftWidth - borderRight; + int availableWidth = Math.max(0, maxWidth - itemWidth - rightWidth); + if (rightWidth > 0) availableWidth -= SPACING * 2; + x = size.x - borderRight - SPACING; + if (itemWidth + allWidth <= maxWidth) { + //Everything fits + for (int i = 0; i < controls.length; i++) { + int alignment = controlAlignments[i]; + if ((alignment & SWT.TRAIL) != 0) { + Point ctrlSize = tabControlSize[i]; + x -= ctrlSize.x; + rects[i].width = ctrlSize.x; + rects[i].height = getControlHeight(ctrlSize); + rects[i].x = x; + rects[i].y = getControlY(size, rects, borderBottom, borderTop, i); + if ((alignment & (SWT.FILL | SWT.WRAP)) != 0) availableWidth -= ctrlSize.x; + } + if (tabControlSize[i].y >= tabHeight && fixedTabHeight == SWT.DEFAULT) { + overflow[i] = true; + } + } + } else { + for (int i = 0; i < controls.length; i++) { + int alignment = controlAlignments[i]; + Point ctrlSize = tabControlSize[i]; + if ((alignment & SWT.TRAIL) != 0) { + if ((alignment & (SWT.FILL | SWT.WRAP)) == 0) { + x -= ctrlSize.x; + rects[i].width = ctrlSize.x; + rects[i].height = getControlHeight(ctrlSize); + rects[i].x = x; + rects[i].y = getControlY(size, rects, borderBottom, borderTop, i); + } else if (((alignment & (SWT.WRAP)) != 0 && ctrlSize.x < availableWidth)) { + x -= ctrlSize.x; + rects[i].width = ctrlSize.x; + rects[i].height = getControlHeight(ctrlSize); + rects[i].x = x; + rects[i].y = getControlY(size, rects, borderBottom, borderTop, i); + availableWidth -= ctrlSize.x; + } else if ((alignment & (SWT.FILL)) != 0 && (alignment & (SWT.WRAP)) == 0) { + rects[i].width = 0; + rects[i].height = getControlHeight(ctrlSize); + rects[i].x = x; + rects[i].y = getControlY(size, rects, borderBottom, borderTop, i); + } else { + if ((alignment & (SWT.WRAP)) != 0) { + overflow[i] = true; + } + } + } + } + } + + //Any space, distribute amongst FILL + if (availableWidth > 0) { + int fillCount = 0; + for (int i = 0; i < controls.length; i++) { + int alignment = controlAlignments[i]; + if ((alignment & SWT.TRAIL) != 0 && (alignment & SWT.FILL) != 0 && !overflow[i]) { + fillCount++; + } + } + if (fillCount != 0) { + int extraSpace = availableWidth/fillCount; + int addedSpace = 0; + for (int i = 0; i < controls.length; i++) { + int alignment = controlAlignments[i]; + if ((alignment & SWT.TRAIL) != 0) { + if ((alignment & SWT.FILL) != 0 && !overflow[i]) { + rects[i].width += extraSpace; + addedSpace += extraSpace; + } + if (!overflow[i]) { + rects[i].x -= addedSpace; + } + } + } + } + } + + //Go through overflow laying out all wrapped controls + Rectangle bodyTrim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0); + int bodyRight = bodyTrim.width + bodyTrim.x; + int bodyLeft = -bodyTrim.x; + int bodyWidth = size.x - bodyLeft - bodyRight; + x = size.x - bodyRight; + int y = onBottom ? this.getSize().y - getTabHeight() + 2*bodyTrim.y : -bodyTrim.y; + availableWidth = bodyWidth; + int maxHeight = 0; + for (int i = 0; i < controls.length; i++) { + Point ctrlSize = tabControlSize[i]; + if (overflow[i]) { + if (availableWidth > ctrlSize.x) { + x -= ctrlSize.x; + rects[i].width = ctrlSize.x; + rects[i].y = onBottom ? y - ctrlSize.y : y; + rects[i].height = ctrlSize.y; + rects[i].x = x; + availableWidth -= ctrlSize.x; + maxHeight = Math.max(maxHeight, ctrlSize.y); + } else { + x = size.x - bodyRight; + y += maxHeight; + maxHeight = 0; + availableWidth = bodyWidth; + if (availableWidth > ctrlSize.x) { + //Relayout this control in the next line + i--; + } else { + ctrlSize = controls[i].isDisposed() ? new Point(0,0) : controls[i].computeSize(bodyWidth, SWT.DEFAULT); + rects[i].width = bodyWidth; + rects[i].y = onBottom ? y - ctrlSize.y : y; + rects[i].height = ctrlSize.y; + rects[i].x = size.x - ctrlSize.x - bodyRight; + y += ctrlSize.y; + } + } + } + } + + if (showChevron) { + int i = 0, lastIndex = -1; + while (i < priority.length && items[priority[i]].showing) { + lastIndex = Math.max(lastIndex, priority[i++]); + } + if (lastIndex == -1) lastIndex = selectedIndex; + if (lastIndex != -1) { + CTabItem lastItem = items[lastIndex]; + int w = lastItem.x + lastItem.width + SPACING; + if (!simple && lastIndex == selectedIndex) w -= (renderer.curveIndent - 7); + rects[controls.length - 1].x = w; + } + } + + if (position != null) position[0] = overflow; + return rects; +} + +int getControlHeight(Point ctrlSize) { + return fixedTabHeight == SWT.DEFAULT ? Math.max(tabHeight - 1, ctrlSize.y) : ctrlSize.y; +} +/* +* This class was not intended to be subclassed but this restriction +* cannot be enforced without breaking backward compatibility. +*/ +//protected void checkSubclass () { +// String name = getClass ().getName (); +// int index = name.lastIndexOf ('.'); +// if (!name.substring (0, index + 1).equals ("org.eclipse.swt.custom.")) { +// SWT.error (SWT.ERROR_INVALID_SUBCLASS); +// } +//} +@Override +public Rectangle computeTrim (int x, int y, int width, int height) { + checkWidget(); + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, x, y, width, height); + Point size = new Point(width, height); + int wrapHeight = getWrappedHeight(size); + if (onBottom) { + trim.height += wrapHeight; + } else { + trim.y -= wrapHeight; + trim.height += wrapHeight; + } + return trim; +} +Image createButtonImage(Display display, int button) { + GC tempGC = new GC (this); + Point size = renderer.computeSize(button, SWT.NONE, tempGC, SWT.DEFAULT, SWT.DEFAULT); + tempGC.dispose(); + + Rectangle trim = renderer.computeTrim(button, SWT.NONE, 0, 0, 0, 0); + Image image = new Image (display, size.x - trim.width, size.y - trim.height); + GC gc = new GC (image); + Color transColor = renderer.parent.getBackground(); + gc.setBackground(transColor); + gc.fillRectangle(image.getBounds()); + renderer.draw(button, SWT.NONE, new Rectangle(trim.x, trim.y, size.x, size.y), gc); + gc.dispose (); + + final ImageData imageData = image.getImageData (DPIUtil.getDeviceZoom ()); + imageData.transparentPixel = imageData.palette.getPixel(transColor.getRGB()); + image.dispose(); + image = new Image(display, new AutoScaleImageDataProvider(display, imageData, DPIUtil.getDeviceZoom())); + return image; +} +void createItem (CTabItem item, int index) { + if (0 > index || index > getItemCount ())SWT.error (SWT.ERROR_INVALID_RANGE); + item.parent = this; + CTabItem[] newItems = new CTabItem [items.length + 1]; + System.arraycopy(items, 0, newItems, 0, index); + newItems[index] = item; + System.arraycopy(items, index, newItems, index + 1, items.length - index); + items = newItems; + if (selectedIndex >= index) selectedIndex ++; + int[] newPriority = new int[priority.length + 1]; + int next = 0, priorityIndex = priority.length; + for (int i = 0; i < priority.length; i++) { + if (!mru && priority[i] == index) { + priorityIndex = next++; + } + newPriority[next++] = priority[i] >= index ? priority[i] + 1 : priority[i]; + } + newPriority[priorityIndex] = index; + priority = newPriority; + + if (items.length == 1) { + updateFolder(UPDATE_TAB_HEIGHT | REDRAW); + } else { + updateFolder(REDRAW_TABS); + } +} +void destroyItem (CTabItem item) { + if (inDispose) return; + int index = indexOf(item); + if (index == -1) return; + + if (items.length == 1) { + items = new CTabItem[0]; + priority = new int[0]; + firstIndex = -1; + selectedIndex = -1; + + Control control = item.control; + if (control != null && !control.isDisposed()) { + control.setVisible(false); + } + setToolTipText(null); + GC gc = new GC(this); + setButtonBounds(gc); + gc.dispose(); + redraw(); + return; + } + + CTabItem[] newItems = new CTabItem [items.length - 1]; + System.arraycopy(items, 0, newItems, 0, index); + System.arraycopy(items, index + 1, newItems, index, items.length - index - 1); + items = newItems; + + int[] newPriority = new int[priority.length - 1]; + int next = 0; + for (int i = 0; i < priority.length; i++) { + if (priority [i] == index) continue; + newPriority[next++] = priority[i] > index ? priority[i] - 1 : priority [i]; + } + priority = newPriority; + + // move the selection if this item is selected + if (selectedIndex == index) { + Control control = item.getControl(); + selectedIndex = -1; + int nextSelection = mru ? priority[0] : Math.max(0, index - 1); + setSelection(nextSelection, true); + if (control != null && !control.isDisposed()) { + control.setVisible(false); + } + } else if (selectedIndex > index) { + selectedIndex --; + } + + requestLayout(); + updateFolder(UPDATE_TAB_HEIGHT | REDRAW_TABS); +} + +/** + * Returns true if the receiver's border is visible. + * + * @return the receiver's border visibility state + * + * @exception SWTException + * + * @since 3.0 + */ +public boolean getBorderVisible() { + checkWidget(); + return borderVisible; +} +ToolBar getChevron() { + if (chevronTb == null) { + chevronTb = new ToolBar(this, SWT.FLAT); + initAccessibleChevronTb(); + addTabControl(chevronTb, SWT.TRAIL, -1, false); + } + if (chevronItem == null) { + chevronItem = new ToolItem(chevronTb, SWT.PUSH); + chevronItem.setToolTipText(SWT.getMessage("SWT_ShowList")); + chevronItem.addListener(SWT.Selection, listener); + } + return chevronTb; +} +/** + * Returns true if the chevron button + * is visible when necessary. + * + * @return the visibility of the chevron button + * + * @exception SWTException + * + */ +/*public*/ boolean getChevronVisible() { + checkWidget(); + return chevronVisible; +} +@Override +public Rectangle getClientArea() { + checkWidget(); + //TODO: HACK - find a better way to get padding + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.FILL, 0, 0, 0, 0); + Point size = getSize(); + int wrapHeight = getWrappedHeight(size); + if (onBottom) { + trim.height += wrapHeight; + } else { + trim.y -= wrapHeight; + trim.height += wrapHeight; + } + if (minimized) return new Rectangle(-trim.x, -trim.y, 0, 0); + int width = size.x - trim.width; + int height = size.y - trim.height; + return new Rectangle(-trim.x, -trim.y, width, height); +} + +/** + * Return the tab that is located at the specified index. + * + * @param index the index of the tab item + * @return the item at the specified index + * + * @exception IllegalArgumentException + * @exception SWTException + */ +public CTabItem getItem (int index) { + /* + * This call is intentionally commented out, to allow this getter method to be + * called from a thread which is different from one that created the widget. + */ + //checkWidget(); + if (index < 0 || index >= items.length) + SWT.error(SWT.ERROR_INVALID_RANGE); + return items [index]; +} +/** + * Gets the item at a point in the widget. + * + * @param pt the point in coordinates relative to the CTabFolder + * @return the item at a point or null + * + * @exception SWTException + */ +public CTabItem getItem (Point pt) { + /* + * This call is intentionally commented out, to allow this getter method to be + * called from a thread which is different from one that created the widget. + */ + //checkWidget(); + if (items.length == 0) return null; + runUpdate(); + Point size = getSize(); + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0); + if (size.x <= trim.width) return null; + for (int i = 0; i < priority.length; i++) { + CTabItem item = items[priority[i]]; + Rectangle rect = item.getBounds(); + if (rect.contains(pt)) return item; + } + return null; +} +/** + * Return the number of tabs in the folder. + * + * @return the number of tabs in the folder + * + * @exception SWTException + */ +public int getItemCount(){ + //checkWidget(); + return items.length; +} +/** + * Return the tab items. + * + * @return the tab items + * + * @exception SWTException + */ +public CTabItem [] getItems() { + /* + * This call is intentionally commented out, to allow this getter method to be + * called from a thread which is different from one that created the widget. + */ + //checkWidget(); + CTabItem[] tabItems = new CTabItem [items.length]; + System.arraycopy(items, 0, tabItems, 0, items.length); + return tabItems; +} +int getLeftItemEdge (GC gc, int part){ + Rectangle trim = renderer.computeTrim(part, SWT.NONE, 0, 0, 0, 0); + int x = -trim.x; + int width = 0; + for (int i = 0; i < controls.length; i++) { + if ((controlAlignments[i] & SWT.LEAD) != 0 && !controls[i].isDisposed() && controls[i].getVisible()) { + width += controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + } + } + if (width != 0) width += SPACING * 2; + x += width; + return Math.max(0, x); +} +/* + * Return the lowercase of the first non-'&' character following + * an '&' character in the given string. If there are no '&' + * characters in the given string, return '\0'. + */ +char _findMnemonic (String string) { + if (string == null) return '\0'; + int index = 0; + int length = string.length (); + do { + while (index < length && string.charAt (index) != '&') index++; + if (++index >= length) return '\0'; + if (string.charAt (index) != '&') return Character.toLowerCase (string.charAt (index)); + index++; + } while (index < length); + return '\0'; +} +String stripMnemonic (String string) { + int index = 0; + int length = string.length (); + do { + while ((index < length) && (string.charAt (index) != '&')) index++; + if (++index >= length) return string; + if (string.charAt (index) != '&') { + return string.substring(0, index-1) + string.substring(index, length); + } + index++; + } while (index < length); + return string; +} +/** + * Returns true if the receiver is minimized. + * + * @return the receiver's minimized state + * + * @exception SWTException + * + * @since 3.0 + */ +public boolean getMinimized() { + checkWidget(); + return minimized; +} +/** + * Returns true if the minimize button + * is visible. + * + * @return the visibility of the minimized button + * + * @exception SWTException + * + * @since 3.0 + */ +public boolean getMinimizeVisible() { + checkWidget(); + return showMin; +} +/** + * Returns the number of characters that will + * appear in a fully compressed tab. + * + * @return number of characters that will appear in a fully compressed tab + * + * @since 3.0 + */ +public int getMinimumCharacters() { + checkWidget(); + return minChars; +} + +/** + * Returns true if the receiver is maximized. + *

+ * + * @return the receiver's maximized state + * + * @exception SWTException

+ * + * @since 3.0 + */ +public boolean getMaximized() { + checkWidget(); + return maximized; +} +/** + * Returns true if the maximize button + * is visible. + * + * @return the visibility of the maximized button + * + * @exception SWTException + * + * @since 3.0 + */ +public boolean getMaximizeVisible() { + checkWidget(); + return showMax; +} +/** + * Returns true if the receiver displays most + * recently used tabs and false otherwise. + *

+ * When there is not enough horizontal space to show all the tabs, + * by default, tabs are shown sequentially from left to right in + * order of their index. When the MRU visibility is turned on, + * the tabs that are visible will be the tabs most recently selected. + * Tabs will still maintain their left to right order based on index + * but only the most recently selected tabs are visible. + *

+ * For example, consider a CTabFolder that contains "Tab 1", "Tab 2", + * "Tab 3" and "Tab 4" (in order by index). The user selects + * "Tab 1" and then "Tab 3". If the CTabFolder is now + * compressed so that only two tabs are visible, by default, + * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently + * selected and "Tab 2" because it is the previous item in index order). + * If MRU visibility is enabled, the two visible tabs will be "Tab 1" + * and "Tab 3" (in that order from left to right).

+ * + * @return the receiver's header's visibility state + * + * @exception SWTException + * + * @since 3.1 + */ +public boolean getMRUVisible() { + checkWidget(); + return mru; +} +/** + * Returns the receiver's renderer. + * + * @return the receiver's renderer + * + * @exception SWTException + * + * @see #setRenderer(CTabFolderRenderer) + * @see CTabFolderRenderer + * + * @since 3.6 + */ +public CTabFolderRenderer getRenderer() { + checkWidget(); + return renderer; +} +int getRightItemEdge (GC gc){ + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0); + int x = getSize().x - (trim.width + trim.x); + int width = 0; + for (int i = 0; i < controls.length; i++) { + int align = controlAlignments[i]; + if ((align & SWT.WRAP) == 0 && (align & SWT.LEAD) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) { + Point rightSize = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT); + width += rightSize.x; + } + } + if (width != 0) width += SPACING * 2; + x -= width; + return Math.max(0, x); +} +/** + * Return the selected tab item, or null if there is no selection. + * + * @return the selected tab item, or null if none has been selected + * + * @exception SWTException + */ +public CTabItem getSelection() { + /* + * This call is intentionally commented out, to allow this getter method to be + * called from a thread which is different from one that created the widget. + */ + //checkWidget(); + if (selectedIndex == -1) return null; + return items[selectedIndex]; +} +/** + * Returns the receiver's selection background color. + * + * @return the selection background color of the receiver + * + * @exception SWTException + * + * @since 3.0 + */ +public Color getSelectionBackground() { + checkWidget(); + return selectionBackground; +} +/** + * Returns the receiver's selection foreground color. + * + * @return the selection foreground color of the receiver + * + * @exception SWTException + * + * @since 3.0 + */ +public Color getSelectionForeground() { + checkWidget(); + return selectionForeground; +} +/** + * Return the index of the selected tab item, or -1 if there + * is no selection. + * + * @return the index of the selected tab item or -1 + * + * @exception SWTException + */ +public int getSelectionIndex() { + /* + * This call is intentionally commented out, to allow this getter method to be + * called from a thread which is different from one that created the widget. + */ + //checkWidget(); + return selectedIndex; +} +/** + * Returns true if the CTabFolder is rendered + * with a simple, traditional shape. + * + * @return true if the CTabFolder is rendered with a simple shape + * + * @since 3.0 + */ +public boolean getSimple() { + checkWidget(); + return simple; +} +/** + * Returns true if the CTabFolder only displays the selected tab + * and false if the CTabFolder displays multiple tabs. + * + * @return true if the CTabFolder only displays the selected tab and false if the CTabFolder displays multiple tabs + * + * @since 3.0 + */ +public boolean getSingle() { + checkWidget(); + return single; +} + +@Override +public int getStyle() { + int style = super.getStyle(); + style &= ~(SWT.TOP | SWT.BOTTOM); + style |= onBottom ? SWT.BOTTOM : SWT.TOP; + style &= ~(SWT.SINGLE | SWT.MULTI); + style |= single ? SWT.SINGLE : SWT.MULTI; + if (borderVisible) style |= SWT.BORDER; + style &= ~SWT.CLOSE; + if (showClose) style |= SWT.CLOSE; + return style; +} +/** + * Returns the height of the tab + * + * @return the height of the tab + * + * @exception SWTException + */ +public int getTabHeight(){ + checkWidget(); + if (fixedTabHeight != SWT.DEFAULT) return fixedTabHeight; + return tabHeight - 1; // -1 for line drawn across top of tab //TODO: replace w/ computeTrim of tab area? +} +/** + * Returns the position of the tab. Possible values are SWT.TOP or SWT.BOTTOM. + * + * @return the position of the tab + * + * @exception SWTException + */ +public int getTabPosition(){ + checkWidget(); + return onBottom ? SWT.BOTTOM : SWT.TOP; +} +/** + * Returns the control in the top right corner of the tab folder. + * Typically this is a close button or a composite with a menu and close button. + * + * @return the control in the top right corner of the tab folder or null + * + * @exception SWTException + * + * @since 2.1 + */ +public Control getTopRight() { + checkWidget(); + return topRight; +} +/** + * Returns the alignment of the top right control. + * + * @return the alignment of the top right control which is either + * SWT.RIGHT or SWT.FILL + * + * @exception SWTException + * + * @since 3.6 + */ +public int getTopRightAlignment() { + checkWidget(); + return topRightAlignment; +} +/** + * Returns true if the close button appears + * when the user hovers over an unselected tabs. + * + * @return true if the close button appears on unselected tabs + * + * @since 3.0 + */ +public boolean getUnselectedCloseVisible() { + checkWidget(); + return showUnselectedClose; +} +/** + * Returns true if an image appears + * in unselected tabs. + * + * @return true if an image appears in unselected tabs + * + * @since 3.0 + */ +public boolean getUnselectedImageVisible() { + checkWidget(); + return showUnselectedImage; +} +/** + * Return the index of the specified tab or -1 if the tab is not + * in the receiver. + * + * @param item the tab item for which the index is required + * + * @return the index of the specified tab item or -1 + * + * @exception IllegalArgumentException + * + * @exception SWTException + */ +public int indexOf(CTabItem item) { + checkWidget(); + if (item == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + for (int i = 0; i < items.length; i++) { + if (items[i] == item) return i; + } + return -1; +} +void initAccessible() { + final Accessible accessible = getAccessible(); + accessible.addAccessibleListener(new AccessibleAdapter() { + @Override + public void getName(AccessibleEvent e) { + CTabItem item = null; + int childID = e.childID; + if (childID == ACC.CHILDID_SELF) { + if (selectedIndex != -1) { + item = items[selectedIndex]; + } + } else if (childID >= 0 && childID < items.length) { + item = items[childID]; + } + e.result = item == null ? null : stripMnemonic(item.getText()); + } + + @Override + public void getHelp(AccessibleEvent e) { + String help = null; + int childID = e.childID; + if (childID == ACC.CHILDID_SELF) { + help = getToolTipText(); + } else if (childID >= 0 && childID < items.length) { + help = items[childID].getToolTipText(); + } + e.result = help; + } + + @Override + public void getKeyboardShortcut(AccessibleEvent e) { + String shortcut = null; + int childID = e.childID; + if (childID >= 0 && childID < items.length) { + String text = items[childID].getText(); + if (text != null) { + char mnemonic = _findMnemonic(text); + if (mnemonic != '\0') { + shortcut = SWT.getMessage ("SWT_Page_Mnemonic", new Object[] {Character.valueOf(mnemonic)}); //$NON-NLS-1$ + } + } + } + if (childID == ACC.CHILDID_SELF) { + shortcut = SWT.getMessage ("SWT_SwitchPage_Shortcut"); //$NON-NLS-1$ + } + e.result = shortcut; + } + }); + + accessible.addAccessibleControlListener(new AccessibleControlAdapter() { + @Override + public void getChildAtPoint(AccessibleControlEvent e) { + Point testPoint = toControl(e.x, e.y); + int childID = ACC.CHILDID_NONE; + for (int i = 0; i < items.length; i++) { + if (items[i].getBounds().contains(testPoint)) { + childID = i; + break; + } + } + if (childID == ACC.CHILDID_NONE) { + Rectangle location = getBounds(); + location.x = location.y = 0; + location.height = location.height - getClientArea().height; + if (location.contains(testPoint)) { + childID = ACC.CHILDID_SELF; + } + } + e.childID = childID; + } + + @Override + public void getLocation(AccessibleControlEvent e) { + Rectangle location = null; + Point pt = null; + int childID = e.childID; + if (childID == ACC.CHILDID_SELF) { + location = getBounds(); + pt = getParent().toDisplay(location.x, location.y); + } else { + if (childID >= 0 && childID < items.length && items[childID].showing) { + location = items[childID].getBounds(); + } + if (location != null) { + pt = toDisplay(location.x, location.y); + } + } + if (location != null && pt != null) { + e.x = pt.x; + e.y = pt.y; + e.width = location.width; + e.height = location.height; + } + } + + @Override + public void getChildCount(AccessibleControlEvent e) { + e.detail = items.length; + } + + @Override + public void getDefaultAction(AccessibleControlEvent e) { + String action = null; + int childID = e.childID; + if (childID >= 0 && childID < items.length) { + action = SWT.getMessage ("SWT_Switch"); //$NON-NLS-1$ + } + e.result = action; + } + + @Override + public void getFocus(AccessibleControlEvent e) { + int childID = ACC.CHILDID_NONE; + if (isFocusControl()) { + if (selectedIndex == -1) { + childID = ACC.CHILDID_SELF; + } else { + childID = selectedIndex; + } + } + e.childID = childID; + } + + @Override + public void getRole(AccessibleControlEvent e) { + int role = 0; + int childID = e.childID; + if (childID == ACC.CHILDID_SELF) { + role = ACC.ROLE_TABFOLDER; + } else if (childID >= 0 && childID < items.length) { + role = ACC.ROLE_TABITEM; + } + e.detail = role; + } + + @Override + public void getSelection(AccessibleControlEvent e) { + e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex; + } + + @Override + public void getState(AccessibleControlEvent e) { + int state = 0; + int childID = e.childID; + if (childID == ACC.CHILDID_SELF) { + state = ACC.STATE_NORMAL; + } else if (childID >= 0 && childID < items.length) { + state = ACC.STATE_SELECTABLE; + if (isFocusControl()) { + state |= ACC.STATE_FOCUSABLE; + } + if (selectedIndex == childID) { + state |= ACC.STATE_SELECTED; + if (isFocusControl()) { + state |= ACC.STATE_FOCUSED; + } + } + } + e.detail = state; + } + + @Override + public void getChildren(AccessibleControlEvent e) { + int childIdCount = items.length; + Object[] children = new Object[childIdCount]; + for (int i = 0; i < childIdCount; i++) { + children[i] = Integer.valueOf(i); + } + e.children = children; + } + }); + + addListener(SWT.Selection, event -> { + if (isFocusControl()) { + if (selectedIndex == -1) { + accessible.setFocus(ACC.CHILDID_SELF); + } else { + accessible.setFocus(selectedIndex); + } + } + }); + + addListener(SWT.FocusIn, event -> { + if (selectedIndex == -1) { + accessible.setFocus(ACC.CHILDID_SELF); + } else { + accessible.setFocus(selectedIndex); + } + }); +} +void initAccessibleMinMaxTb() { + minMaxTb.getAccessible().addAccessibleListener(new AccessibleAdapter() { + @Override + public void getName(AccessibleEvent e) { + if (e.childID != ACC.CHILDID_SELF) { + if (minItem != null && e.childID == minMaxTb.indexOf(minItem)) { + e.result = minItem.getToolTipText(); + } else if (maxItem != null && e.childID == minMaxTb.indexOf(maxItem)) { + e.result = maxItem.getToolTipText(); + } + } + } + }); +} +void initAccessibleChevronTb() { + chevronTb.getAccessible().addAccessibleListener(new AccessibleAdapter() { + @Override + public void getName(AccessibleEvent e) { + if (e.childID != ACC.CHILDID_SELF) { + if (chevronItem != null && e.childID == chevronTb.indexOf(chevronItem)) { + e.result = chevronItem.getToolTipText(); + } + } + } + }); +} +void onKeyDown (Event event) { + runUpdate(); + switch (event.keyCode) { + case SWT.ARROW_LEFT: + case SWT.ARROW_RIGHT: + int count = items.length; + if (count == 0) return; + if (selectedIndex == -1) return; + int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT; + int offset = event.keyCode == leadKey ? -1 : 1; + int index; + if (!mru) { + index = selectedIndex + offset; + } else { + int[] visible = new int[items.length]; + int idx = 0; + int current = -1; + for (int i = 0; i < items.length; i++) { + if (items[i].showing) { + if (i == selectedIndex) current = idx; + visible [idx++] = i; + } + } + if (current + offset >= 0 && current + offset < idx){ + index = visible [current + offset]; + } else { + if (showChevron) { + Rectangle chevronRect = chevronItem.getBounds(); + chevronRect = event.display.map(chevronTb, this, chevronRect); + CTabFolderEvent e = new CTabFolderEvent(this); + e.widget = this; + e.time = event.time; + e.x = chevronRect.x; + e.y = chevronRect.y; + e.width = chevronRect.width; + e.height = chevronRect.height; + e.doit = true; + for (int i = 0; i < folderListeners.length; i++) { + folderListeners[i].showList(e); + } + if (e.doit && !isDisposed()) { + showList(chevronRect); + } + } + return; + } + } + if (index < 0 || index >= count) return; + setSelection (index, true); + forceFocus(); + } +} +void onDispose(Event event) { + removeListener(SWT.Dispose, listener); + notifyListeners(SWT.Dispose, event); + event.type = SWT.None; + /* + * Usually when an item is disposed, destroyItem will change the size of the items array, + * reset the bounds of all the tabs and manage the widget associated with the tab. + * Since the whole folder is being disposed, this is not necessary. For speed + * the inDispose flag is used to skip over this part of the item dispose. + */ + inDispose = true; + + if (showMenu != null && !showMenu.isDisposed()) { + showMenu.dispose(); + showMenu = null; + } + int length = items.length; + for (int i = 0; i < length; i++) { + if (items[i] != null) { + items[i].dispose(); + } + } + + gradientColors = null; + + selectionGradientColors = null; + selectionGradientPercents = null; + selectionBgImage = null; + + selectionBackground = null; + selectionForeground = null; + + if (controlBkImages != null) { + for (int i = 0; i < controlBkImages.length; i++) { + if (controlBkImages[i] != null) { + controlBkImages[i].dispose(); + controlBkImages[i] = null; + } + } + controlBkImages = null; + } + controls = null; + controlAlignments = null; + controlRects = null; + + if (maxImage != null) maxImage.dispose(); + maxImage = null; + + if (minImage != null) minImage.dispose(); + minImage = null; + + if (chevronImage != null) chevronImage.dispose(); + chevronImage = null; + + if (renderer != null) renderer.dispose(); + renderer = null; + + minItem = null; + maxItem = null; + minMaxTb = null; + + chevronItem = null; + chevronTb = null; + + if (folderListeners.length != 0) folderListeners = new CTabFolder2Listener[0]; + if (tabListeners.length != 0) tabListeners = new CTabFolderListener[0]; +} +void onDragDetect(Event event) { + boolean consume = false; + for (int i = 0; i < items.length; i++) { + if (items[i].closeRect.contains(event.x, event.y)) { + consume = true; + break; + } + } + if (consume) { + event.type = SWT.None; + } +} +void onFocus(Event event) { + checkWidget(); + if (selectedIndex >= 0) { + redraw(); + } else { + setSelection(0, true); + } +} +boolean onMnemonic (Event event, boolean doit) { + char key = event.character; + for (int i = 0; i < items.length; i++) { + if (items[i] != null) { + char mnemonic = _findMnemonic (items[i].getText ()); + if (mnemonic != '\0') { + if (Character.toLowerCase (key) == mnemonic) { + if (doit) { + setSelection(i, true); + forceFocus(); + } + return true; + } + } + } + } + return false; +} +void onMenuDetect(Event event) { + if (event.detail == SWT.MENU_KEYBOARD) { + if (selectedIndex != -1) { + CTabItem item = items[selectedIndex]; + Rectangle rect = getDisplay().map(this, null, item.getBounds()); + if (!rect.contains(event.x, event.y)) { + /* If the mouse is not in the currently-selected tab, + * then pop up the menu near the top-right corner of the current tab. + */ + Rectangle itemTrim = renderer.computeTrim(selectedIndex, SWT.NONE, 0, 0, 0, 0); + Rectangle closeTrim = renderer.computeTrim(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, 0, 0, 0, 0); + event.x = rect.x + rect.width - item.closeRect.width + itemTrim.x - closeTrim.width; + event.y = rect.y - itemTrim.y - closeTrim.y; + } + } + } +} +void onMouseDoubleClick(Event event) { + if (event.button != 1 || + (event.stateMask & SWT.BUTTON2) != 0 || + (event.stateMask & SWT.BUTTON3) != 0) return; + Event e = new Event(); + e.item = getItem(new Point(event.x, event.y)); + if (e.item != null) { + notifyListeners(SWT.DefaultSelection, e); + } +} +void onMouse(Event event) { + if( isDisposed() ) { + return; + } + int x = event.x, y = event.y; + switch (event.type) { + case SWT.MouseEnter: { + setToolTipText(null); + break; + } + case SWT.MouseExit: { + for (int i=0; i= 0 && current + offset < idx){ + index = visible [current + offset]; + } else { + if (showChevron) { + Rectangle chevronRect = chevronItem.getBounds(); + chevronRect = event.display.map(chevronTb, this, chevronRect); + CTabFolderEvent e = new CTabFolderEvent(this); + e.widget = this; + e.time = event.time; + e.x = chevronRect.x; + e.y = chevronRect.y; + e.width = chevronRect.width; + e.height = chevronRect.height; + e.doit = true; + for (int i = 0; i < folderListeners.length; i++) { + folderListeners[i].showList(e); + } + if (e.doit && !isDisposed()) { + showList(chevronRect); + } + } + } + } + } + setSelection (index, true); +} +void onPaint(Event event) { + if (inDispose) return; + Font font = getFont(); + if (oldFont == null || !oldFont.equals(font)) { + // handle case where default font changes + oldFont = font; + if (!updateTabHeight(false)) { + updateItems(); + redraw(); + return; + } + } + + GC gc = event.gc; + Font gcFont = gc.getFont(); + Color gcBackground = gc.getBackground(); + Color gcForeground = gc.getForeground(); + +// Useful for debugging paint problems +//{ +//Point size = getSize(); +//gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN)); +//gc.fillRectangle(-10, -10, size.x + 20, size.y+20); +//} + + Point size = getSize(); + Rectangle bodyRect = new Rectangle(0, 0, size.x, size.y); + renderer.draw(CTabFolderRenderer.PART_BODY, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc); + + gc.setFont(gcFont); + gc.setForeground(gcForeground); + gc.setBackground(gcBackground); + + renderer.draw(CTabFolderRenderer.PART_HEADER, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc); + + gc.setFont(gcFont); + gc.setForeground(gcForeground); + gc.setBackground(gcBackground); + + if (!single) { + for (int i=0; i < items.length; i++) { + Rectangle itemBounds = items[i].getBounds(); + if (i != selectedIndex && event.getBounds().intersects(itemBounds)) { + renderer.draw(i, SWT.BACKGROUND | SWT.FOREGROUND | items[i].state , itemBounds, gc); + } + } + } + + gc.setFont(gcFont); + gc.setForeground(gcForeground); + gc.setBackground(gcBackground); + + if (selectedIndex != -1) { + renderer.draw(selectedIndex, items[selectedIndex].state | SWT.BACKGROUND | SWT.FOREGROUND, items[selectedIndex].getBounds(), gc); + } + + gc.setFont(gcFont); + gc.setForeground(gcForeground); + gc.setBackground(gcBackground); + + if (hoverTb) { + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0); + int x = getSize().x - (trim.width + trim.x); + hoverRect = new Rectangle(x - 16 - SPACING, 2, 16, getTabHeight() - 2); + gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); + x = hoverRect.x; + int y = hoverRect.y; + gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE)); + gc.fillRectangle(x + hoverRect.width - 6, y, 5, 5); + gc.drawRectangle(x + hoverRect.width - 6, y, 5, 5); + gc.drawLine(x + hoverRect.width - 6, y+2, x + hoverRect.width - 6 + 5, y + 2); + gc.fillRectangle(x, y, 5 , 2); + gc.drawRectangle(x, y, 5 , 2); + } + gc.setFont(gcFont); + gc.setForeground(gcForeground); + gc.setBackground(gcBackground); +} + +void onResize(Event event) { + if (inDispose) return; + if (ignoreResize) return; + if (updateItems()) { + redrawTabs(); + } + Point size = getSize(); + if (oldSize == null) { + redraw(); + } else { + if (onBottom && size.y != oldSize.y) { + redraw(); + } else { + int x1 = Math.min(size.x, oldSize.x); + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0); + if (size.x != oldSize.x) x1 -= trim.width + trim.x - marginWidth + 2; + if (!simple) x1 -= 5; // rounded top right corner + int y1 = Math.min(size.y, oldSize.y); + if (size.y != oldSize.y) y1 -= trim.height + trim.y - marginHeight; + int x2 = Math.max(size.x, oldSize.x); + int y2 = Math.max(size.y, oldSize.y); + redraw(0, y1, x2, y2 - y1, false); + redraw(x1, 0, x2 - x1, y2, false); + if (hoverTb) { + redraw(hoverRect.x, hoverRect.y, hoverRect.width, hoverRect.height, false); + } + } + } + oldSize = size; +} +void onSelection(Event event) { + if (hovering) { + hovering = false; + updateItems(); + } + if (event.widget == maxItem) { + CTabFolderEvent e = new CTabFolderEvent(this); + e.widget = CTabFolder.this; + e.time = event.time; + for (int i = 0; i < folderListeners.length; i++) { + if (maximized) { + folderListeners[i].restore(e); + } else { + folderListeners[i].maximize(e); + } + } + } else if (event.widget == minItem) { + CTabFolderEvent e = new CTabFolderEvent(this); + e.widget = CTabFolder.this; + e.time = event.time; + for (int i = 0; i < folderListeners.length; i++) { + if (minimized) { + folderListeners[i].restore(e); + } else { + folderListeners[i].minimize(e); + } + } + } else if (event.widget == chevronItem) { + Rectangle chevronRect = chevronItem.getBounds(); + chevronRect = event.display.map(chevronTb, this, chevronRect); + CTabFolderEvent e = new CTabFolderEvent(this); + e.widget = this; + e.time = event.time; + e.x = chevronRect.x; + e.y = chevronRect.y; + e.width = chevronRect.width; + e.height = chevronRect.height; + e.doit = true; + for (int i = 0; i < folderListeners.length; i++) { + folderListeners[i].showList(e); + } + if (e.doit && !isDisposed()) { + showList(chevronRect); + } + } +} +void onTraverse (Event event) { + if (ignoreTraverse) return; + runUpdate(); + switch (event.detail) { + case SWT.TRAVERSE_ESCAPE: + case SWT.TRAVERSE_RETURN: + case SWT.TRAVERSE_TAB_NEXT: + case SWT.TRAVERSE_TAB_PREVIOUS: + Control focusControl = getDisplay().getFocusControl(); + if (focusControl == this) event.doit = true; + break; + case SWT.TRAVERSE_MNEMONIC: + event.doit = onMnemonic(event, false); + break; + case SWT.TRAVERSE_PAGE_NEXT: + case SWT.TRAVERSE_PAGE_PREVIOUS: + event.doit = items.length > 0; + break; + } + ignoreTraverse = true; + notifyListeners(SWT.Traverse, event); + ignoreTraverse = false; + event.type = SWT.None; + if (isDisposed()) return; + if (!event.doit) return; + switch (event.detail) { + case SWT.TRAVERSE_MNEMONIC: + onMnemonic(event, true); + event.detail = SWT.TRAVERSE_NONE; + break; + case SWT.TRAVERSE_PAGE_NEXT: + case SWT.TRAVERSE_PAGE_PREVIOUS: + onPageTraversal(event); + event.detail = SWT.TRAVERSE_NONE; + break; + } +} +void redrawTabs() { + Point size = getSize(); + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0); + if (onBottom) { + int h = trim.height + trim.y - marginHeight; + redraw(0, size.y - h - 1, size.x, h + 1, false); + } else { + redraw(0, 0, size.x, -trim.y - marginHeight + 1, false); + } +} +/** + * Removes the listener. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException + * + * @exception SWTException + * + * @see #addCTabFolder2Listener(CTabFolder2Listener) + * + * @since 3.0 + */ +public void removeCTabFolder2Listener(CTabFolder2Listener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (folderListeners.length == 0) return; + int index = -1; + for (int i = 0; i < folderListeners.length; i++) { + if (listener == folderListeners[i]){ + index = i; + break; + } + } + if (index == -1) return; + if (folderListeners.length == 1) { + folderListeners = new CTabFolder2Listener[0]; + return; + } + CTabFolder2Listener[] newTabListeners = new CTabFolder2Listener[folderListeners.length - 1]; + System.arraycopy(folderListeners, 0, newTabListeners, 0, index); + System.arraycopy(folderListeners, index + 1, newTabListeners, index, folderListeners.length - index - 1); + folderListeners = newTabListeners; +} +/** + * Removes the listener. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException + * + * @exception SWTException + * + * @deprecated see removeCTabFolderCloseListener(CTabFolderListener) + */ +@Deprecated +public void removeCTabFolderListener(CTabFolderListener listener) { + checkWidget(); + if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (tabListeners.length == 0) return; + int index = -1; + for (int i = 0; i < tabListeners.length; i++) { + if (listener == tabListeners[i]){ + index = i; + break; + } + } + if (index == -1) return; + if (tabListeners.length == 1) { + tabListeners = new CTabFolderListener[0]; + return; + } + CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1]; + System.arraycopy(tabListeners, 0, newTabListeners, 0, index); + System.arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1); + tabListeners = newTabListeners; +} +/** + * Removes the listener from the collection of listeners who will + * be notified when the user changes the receiver's selection. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException + * @exception SWTException + * + * @see SelectionListener + * @see #addSelectionListener + */ +public void removeSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + removeListener(SWT.Selection, listener); + removeListener(SWT.DefaultSelection, listener); +} + +@Override +public void reskin(int flags) { + super.reskin(flags); + for (int i = 0; i < items.length; i++) { + items[i].reskin(flags); + } +} + +@Override +public void setBackground (Color color) { + super.setBackground(color); + renderer.createAntialiasColors(); //TODO: need better caching strategy + updateBkImages(); + redraw(); +} +/** + * Specify a gradient of colors to be drawn in the background of the unselected tabs. + * For example to draw a gradient that varies from dark blue to blue and then to + * white, use the following call to setBackground: + *
+ *	cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
+ *		                           display.getSystemColor(SWT.COLOR_BLUE),
+ *		                           display.getSystemColor(SWT.COLOR_WHITE),
+ *		                           display.getSystemColor(SWT.COLOR_WHITE)},
+ *		               new int[] {25, 50, 100});
+ * 
+ * + * @param colors an array of Color that specifies the colors to appear in the gradient + * in order of appearance left to right. The value null clears the + * background gradient. The value null can be used inside the array of + * Color to specify the background color. + * @param percents an array of integers between 0 and 100 specifying the percent of the width + * of the widget at which the color should change. The size of the percents + * array must be one less than the size of the colors array. + * + * @exception SWTException + * + * @since 3.6 + */ +public void setBackground(Color[] colors, int[] percents) { + setBackground(colors, percents, false); +} +/** + * Specify a gradient of colors to be drawn in the background of the unselected tab. + * For example to draw a vertical gradient that varies from dark blue to blue and then to + * white, use the following call to setBackground: + *
+ *	cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
+ *		                           display.getSystemColor(SWT.COLOR_BLUE),
+ *		                           display.getSystemColor(SWT.COLOR_WHITE),
+ *		                           display.getSystemColor(SWT.COLOR_WHITE)},
+ *		                  new int[] {25, 50, 100}, true);
+ * 
+ * + * @param colors an array of Color that specifies the colors to appear in the gradient + * in order of appearance left to right. The value null clears the + * background gradient. The value null can be used inside the array of + * Color to specify the background color. + * @param percents an array of integers between 0 and 100 specifying the percent of the width + * of the widget at which the color should change. The size of the percents + * array must be one less than the size of the colors array. + * + * @param vertical indicate the direction of the gradient. True is vertical and false is horizontal. + * + * @exception SWTException + * + * @since 3.6 + */ +public void setBackground(Color[] colors, int[] percents, boolean vertical) { + checkWidget(); + if (colors != null) { + if (percents == null || percents.length != colors.length - 1) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + for (int i = 0; i < percents.length; i++) { + if (percents[i] < 0 || percents[i] > 100) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + if (i > 0 && percents[i] < percents[i-1]) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + if (getDisplay().getDepth() < 15) { + // Don't use gradients on low color displays + colors = new Color[] {colors[colors.length - 1]}; + percents = new int[] {}; + } + } + + // Are these settings the same as before? + if ((gradientColors != null) && (colors != null) && + (gradientColors.length == colors.length)) { + boolean same = false; + for (int i = 0; i < gradientColors.length; i++) { + if (gradientColors[i] == null) { + same = colors[i] == null; + } else { + same = gradientColors[i].equals(colors[i]); + } + if (!same) break; + } + if (same) { + for (int i = 0; i < gradientPercents.length; i++) { + same = gradientPercents[i] == percents[i]; + if (!same) break; + } + } + if (same && this.gradientVertical == vertical) return; + } + // Store the new settings + if (colors == null) { + gradientColors = null; + gradientPercents = null; + gradientVertical = false; + setBackground((Color)null); + } else { + gradientColors = new Color[colors.length]; + for (int i = 0; i < colors.length; ++i) { + gradientColors[i] = colors[i]; + } + gradientPercents = new int[percents.length]; + for (int i = 0; i < percents.length; ++i) { + gradientPercents[i] = percents[i]; + } + gradientVertical = vertical; + setBackground(gradientColors[gradientColors.length-1]); + } + + // Refresh with the new settings + redraw(); +} +@Override +public void setBackgroundImage(Image image) { + super.setBackgroundImage(image); + renderer.createAntialiasColors(); //TODO: need better caching strategy + redraw(); +} +/** + * Toggle the visibility of the border + * + * @param show true if the border should be displayed + * + * @exception SWTException + */ +public void setBorderVisible(boolean show) { + checkWidget(); + if (borderVisible == show) return; + this.borderVisible = show; + updateFolder(REDRAW); +} +void setButtonBounds(GC gc) { + Point size = getSize(); + // max button + Display display = getDisplay(); + if (showMax) { + if (minMaxTb == null) { + minMaxTb = new ToolBar(this, SWT.FLAT); + initAccessibleMinMaxTb(); + addTabControl(minMaxTb, SWT.TRAIL, 0, false); + } + if (maxItem == null) { + maxItem = new ToolItem(minMaxTb, SWT.PUSH); + if (maxImage == null) { + maxImage = createButtonImage(display, CTabFolderRenderer.PART_MAX_BUTTON); + } + maxItem.setImage(maxImage); + maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$ + maxItem.addListener(SWT.Selection, listener); + } + } else { + //might need to remove it if already there + if (maxItem != null) { + maxItem.dispose(); + maxItem = null; + } + } + // min button + if (showMin) { + if (minMaxTb == null) { + minMaxTb = new ToolBar(this, SWT.FLAT); + initAccessibleMinMaxTb(); + addTabControl(minMaxTb, SWT.TRAIL, 0, false); + } + if (minItem == null) { + minItem = new ToolItem(minMaxTb, SWT.PUSH, 0); + if (minImage == null) { + minImage = createButtonImage(display, CTabFolderRenderer.PART_MIN_BUTTON); + } + minItem.setImage(minImage); + minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$ + minItem.addListener(SWT.Selection, listener); + } + } else { + //might need to remove it if already there + if (minItem != null) { + minItem.dispose(); + minItem = null; + } + } + if (minMaxTb != null && minMaxTb.getItemCount() == 0) { + removeTabControl(minMaxTb, false); + minMaxTb.dispose(); + minMaxTb = null; + } + if (showChevron) { + int itemCount = items.length; + int count; + if (single) { + count = selectedIndex == -1 ? itemCount : itemCount - 1; + } else { + int showCount = 0; + while (showCount < priority.length && items[priority[showCount]].showing) { + showCount++; + } + count = itemCount - showCount; + } + if (count != chevronCount) { + chevronCount = count; + if (chevronImage != null) chevronImage.dispose(); + chevronImage = createButtonImage(display, CTabFolderRenderer.PART_CHEVRON_BUTTON); + chevronItem.setImage(chevronImage); + } + } + + boolean[][] overflow = new boolean[1][0]; + Rectangle[] rects = computeControlBounds(size, overflow); + if (fixedTabHeight != SWT.DEFAULT) { + int height = fixedTabHeight; + if (!hovering) { + hoverTb = false; + Rectangle tabBounds = this.getBounds(); + for (int i = 0; i < rects.length; i++) { + if (!(overflow[0][i])) { + if (rects[i].height > height) { + hoverTb = true; + break; + } + } + } + if (hoverTb) { + for (int i = 0; i < rects.length; i++) { + if (!(overflow[0][i])) { + if (rects[i].height > height) { + rects[i].x = tabBounds.width + 20; + } + } + } + } + } + } + int headerHeight = 0; + for (int i = 0; i < rects.length; i++) { + if (!overflow[0][i]) headerHeight = Math.max(rects[i].height, headerHeight); + } + boolean changed = false; + ignoreResize = true; + for (int i = 0; i < controls.length; i++) { + if (!controls[i].isDisposed()) { + if (overflow[0][i]) { + controls[i].setBounds(rects[i]); + } else { + controls[i].moveAbove(null); + controls[i].setBounds(rects[i].x, rects[i].y, rects[i].width, headerHeight); + } + } + if (!changed && !rects[i].equals(controlRects[i])) changed = true; + } + ignoreResize = false; + controlRects = rects; + if (changed || hovering) updateBkImages(); +} +@Override +public boolean setFocus () { + checkWidget (); + + /* + * Feature in SWT. When a new tab item is selected + * and the previous tab item had focus, removing focus + * from the previous tab item causes fixFocus() to give + * focus to the first child, which is usually one of the + * toolbars. This is unexpected. + * The fix is to try to set focus on the first tab item + * if fixFocus() is called. + */ + Control focusControl = getDisplay().getFocusControl (); + boolean fixFocus = isAncestor (focusControl); + if (fixFocus) { + CTabItem item = getSelection(); + if (item != null) { + if (item.setFocus ()) return true; + } + } + return super.setFocus (); +} +/* Copy of isFocusAncestor from Control. */ +boolean isAncestor (Control control) { + while (control != null && control != this && !(control instanceof Shell)) { + control = control.getParent(); + } + return control == this; +} +@Override +public void setFont(Font font) { + checkWidget(); + if (font != null && font.equals(getFont())) return; + super.setFont(font); + oldFont = getFont(); + updateFolder(REDRAW); +} +@Override +public void setForeground (Color color) { + super.setForeground(color); + redraw(); +} +/** + * Display an insert marker before or after the specified tab item. + * + * A value of null will clear the mark. + * + * @param item the item with which the mark is associated or null + * + * @param after true if the mark should be displayed after the specified item + * + * @exception SWTException + */ +public void setInsertMark(CTabItem item, boolean after) { + checkWidget(); +} +/** + * Display an insert marker before or after the specified tab item. + * + * A value of -1 will clear the mark. + * + * @param index the index of the item with which the mark is associated or -1 + * + * @param after true if the mark should be displayed after the specified item + * + * @exception IllegalArgumentException + * + * @exception SWTException + */ +public void setInsertMark(int index, boolean after) { + checkWidget(); + if (index < -1 || index >= getItemCount()) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } +} +boolean setItemLocation(GC gc) { + boolean changed = false; + if (items.length == 0) return false; + Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0); + int borderBottom = trim.height + trim.y; + int borderTop = -trim.y; + Point size = getSize(); + int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop; + Point closeButtonSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, 0, gc, SWT.DEFAULT, SWT.DEFAULT); + int leftItemEdge = getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER); + if (single) { + int defaultX = getDisplay().getBounds().width + 10; // off screen + for (int i = 0; i < items.length; i++) { + CTabItem item = items[i]; + if (i == selectedIndex) { + firstIndex = selectedIndex; + int oldX = item.x, oldY = item.y; + item.x = leftItemEdge; + item.y = y; + item.showing = true; + if (showClose || item.showClose) { + item.closeRect.x = leftItemEdge - renderer.computeTrim(i, SWT.NONE, 0, 0, 0, 0).x; + item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2; + } + if (item.x != oldX || item.y != oldY) changed = true; + } else { + item.x = defaultX; + item.showing = false; + } + } + } else { + int rightItemEdge = getRightItemEdge(gc); + int maxWidth = rightItemEdge - leftItemEdge; + int width = 0; + for (int i = 0; i < priority.length; i++) { + CTabItem item = items[priority[i]]; + width += item.width; + item.showing = i == 0 ? true : item.width > 0 && width <= maxWidth; + } + int x = getLeftItemEdge(gc, CTabFolderRenderer.PART_HEADER); + int defaultX = getDisplay().getBounds().width + 10; // off screen + firstIndex = items.length - 1; + for (int i = 0; i < items.length; i++) { + CTabItem item = items[i]; + if (!item.showing) { + if (item.x != defaultX) changed = true; + item.x = defaultX; + } else { + firstIndex = Math.min(firstIndex, i); + if (item.x != x || item.y != y) changed = true; + item.x = x; + item.y = y; + int state = SWT.NONE; + if (i == selectedIndex) state |= SWT.SELECTED; + Rectangle edgeTrim = renderer.computeTrim(i, state, 0, 0, 0, 0); + item.closeRect.x = item.x + item.width - (edgeTrim.width + edgeTrim.x) - closeButtonSize.x; + item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2; + x = x + item.width; + if (!simple && i == selectedIndex) x -= renderer.curveIndent; //TODO: fix next item position + } + } + } + return changed; +} +/** + * Reorder the items of the receiver. + * @param indices an array containing the new indices for all items + * + * @exception IllegalArgumentException + * + * @exception SWTException + */ +/*public*/ void setItemOrder (int[] indices) { + checkWidget(); + if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (indices.length != items.length) SWT.error (SWT.ERROR_INVALID_ARGUMENT); + int newSelectedIndex = -1; + boolean[] seen = new boolean[items.length]; + CTabItem[] temp = new CTabItem[items.length]; + for (int i=0; i 1; + if (showChevron) { + chevron.setVisible(true); + } + if (selectedIndex != -1) { + CTabItem tab = items[selectedIndex]; + int width = renderer.computeSize(selectedIndex, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT).x; + width = Math.min(width, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER)); + if (tab.height != tabHeight || tab.width != width) { + changed = true; + tab.shortenedText = null; + tab.shortenedTextWidth = 0; + tab.height = tabHeight; + tab.width = width; + tab.closeRect.width = tab.closeRect.height = 0; + if (showClose || tab.showClose) { + Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT); + tab.closeRect.width = closeSize.x; + tab.closeRect.height = closeSize.y; + } + } + } + return changed; + } + + if (items.length == 0) return changed; + int[] widths; + int tabAreaWidth = Math.max(0, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER)); + // First, try the minimum tab size at full compression. + int minWidth = 0; + int[] minWidths = new int[items.length]; + for (int i = 0; i < priority.length; i++) { + int index = priority[i]; + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (index == selectedIndex) state |= SWT.SELECTED; + minWidths[index] = renderer.computeSize(index, state, gc, SWT.DEFAULT, SWT.DEFAULT).x; + minWidth += minWidths[index]; + if (minWidth > tabAreaWidth) break; + } + if (minWidth > tabAreaWidth) { + // full compression required and a chevron + showChevron = chevronVisible && items.length > 1; + if (showChevron) { + tabAreaWidth -= chevron.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + chevron.setVisible(true); + } + widths = minWidths; + int index = selectedIndex != -1 ? selectedIndex : 0; + if (tabAreaWidth < widths[index]) { + widths[index] = Math.max(0, tabAreaWidth); + } + } else { + int maxWidth = 0; + int[] maxWidths = new int[items.length]; + for (int i = 0; i < items.length; i++) { + int state = 0; + if (i == selectedIndex) state |= SWT.SELECTED; + maxWidths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x; + maxWidth += maxWidths[i]; + } + if (maxWidth <= tabAreaWidth) { + // no compression required + widths = maxWidths; + } else { + // determine compression for each item + int extra = (tabAreaWidth - minWidth) / items.length; + while (true) { + int large = 0, totalWidth = 0; + for (int i = 0 ; i < items.length; i++) { + if (maxWidths[i] > minWidths[i] + extra) { + totalWidth += minWidths[i] + extra; + large++; + } else { + totalWidth += maxWidths[i]; + } + } + if (totalWidth >= tabAreaWidth) { + extra--; + break; + } + if (large == 0 || tabAreaWidth - totalWidth < large) break; + extra++; + } + widths = new int[items.length]; + for (int i = 0; i < items.length; i++) { + widths[i] = Math.min(maxWidths[i], minWidths[i] + extra); + } + } + } + + for (int i = 0; i < items.length; i++) { + CTabItem tab = items[i]; + int width = widths[i]; + if (tab.height != tabHeight || tab.width != width) { + changed = true; + tab.shortenedText = null; + tab.shortenedTextWidth = 0; + tab.height = tabHeight; + tab.width = width; + tab.closeRect.width = tab.closeRect.height = 0; + if (showClose || tab.showClose) { + if (i == selectedIndex || showUnselectedClose) { + Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT); + tab.closeRect.width = closeSize.x; + tab.closeRect.height = closeSize.y; + } + } + } + } + return changed; +} +/** + * Marks the receiver's maximize button as visible if the argument is true, + * and marks it invisible otherwise. + * + * @param visible the new visibility state + * + * @exception SWTException + * + * @since 3.0 + */ +public void setMaximizeVisible(boolean visible) { + checkWidget(); + if (showMax == visible) return; + // display maximize button + showMax = visible; + updateFolder(UPDATE_TAB_HEIGHT | REDRAW); +} +/** + * Sets the layout which is associated with the receiver to be + * the argument which may be null. + *

+ * Note: No Layout can be set on this Control because it already + * manages the size and position of its children. + *

+ * + * @param layout the receiver's new layout or null + * + * @exception SWTException + */ +@Override +public void setLayout (Layout layout) { + checkWidget(); + return; +} +/** + * Sets the maximized state of the receiver. + * + * @param maximize the new maximized state + * + * @exception SWTException + * + * @since 3.0 + */ +public void setMaximized(boolean maximize) { + checkWidget (); + if (this.maximized == maximize) return; + if (maximize && this.minimized) setMinimized(false); + this.maximized = maximize; + if (minMaxTb != null && maxItem != null) { + if (maxImage != null) maxImage.dispose(); + maxImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MAX_BUTTON); + maxItem.setImage(maxImage); + maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$ + } +} +/** + * Marks the receiver's minimize button as visible if the argument is true, + * and marks it invisible otherwise. + * + * @param visible the new visibility state + * + * @exception SWTException + * + * @since 3.0 + */ +public void setMinimizeVisible(boolean visible) { + checkWidget(); + if (showMin == visible) return; + // display minimize button + showMin = visible; + updateFolder(UPDATE_TAB_HEIGHT | REDRAW); +} +/** + * Sets the minimized state of the receiver. + * + * @param minimize the new minimized state + * + * @exception SWTException + * + * @since 3.0 + */ +public void setMinimized(boolean minimize) { + checkWidget (); + if (this.minimized == minimize) return; + if (minimize && this.maximized) setMaximized(false); + this.minimized = minimize; + if (minMaxTb != null && minItem != null) { + if (minImage != null) minImage.dispose(); + minImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MIN_BUTTON); + minItem.setImage(minImage); + minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$ + } +} + +/** + * Sets the minimum number of characters that will + * be displayed in a fully compressed tab. + * + * @param count the minimum number of characters that will be displayed in a fully compressed tab + * + * @exception SWTException + * + * @since 3.0 + */ +public void setMinimumCharacters(int count) { + checkWidget (); + if (count < 0) SWT.error(SWT.ERROR_INVALID_RANGE); + if (minChars == count) return; + minChars = count; + updateFolder(REDRAW_TABS); +} + +/** + * When there is not enough horizontal space to show all the tabs, + * by default, tabs are shown sequentially from left to right in + * order of their index. When the MRU visibility is turned on, + * the tabs that are visible will be the tabs most recently selected. + * Tabs will still maintain their left to right order based on index + * but only the most recently selected tabs are visible. + *

+ * For example, consider a CTabFolder that contains "Tab 1", "Tab 2", + * "Tab 3" and "Tab 4" (in order by index). The user selects + * "Tab 1" and then "Tab 3". If the CTabFolder is now + * compressed so that only two tabs are visible, by default, + * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently + * selected and "Tab 2" because it is the previous item in index order). + * If MRU visibility is enabled, the two visible tabs will be "Tab 1" + * and "Tab 3" (in that order from left to right).

+ * + * @param show the new visibility state + * + * @exception SWTException + * + * @since 3.1 + */ +public void setMRUVisible(boolean show) { + checkWidget(); + if (mru == show) return; + mru = show; + if (!mru) { + if (firstIndex == -1) return; + int idx = firstIndex; + int next = 0; + for (int i = firstIndex; i < items.length; i++) { + priority[next++] = i; + } + for (int i = 0; i < idx; i++) { + priority[next++] = i; + } + updateFolder(REDRAW_TABS); + } +} +/** + * Sets the renderer which is associated with the receiver to be + * the argument which may be null. In the case of null, the default + * renderer is used. + * + * @param renderer a new renderer + * + * @exception SWTException + * + * @see CTabFolderRenderer + * + * @since 3.6 + */ +public void setRenderer(CTabFolderRenderer renderer) { + checkWidget(); + if (this.renderer == renderer || (useDefaultRenderer && renderer == null)) return; + if (this.renderer != null) this.renderer.dispose(); + useDefaultRenderer = renderer == null; + if (useDefaultRenderer) renderer = new CTabFolderRenderer(this); + this.renderer = renderer; + updateFolder(REDRAW); +} +/** + * Set the selection to the tab at the specified item. + * + * @param item the tab item to be selected + * + * @exception IllegalArgumentException + * + * @exception SWTException + */ +public void setSelection(CTabItem item) { + checkWidget(); + if (item == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); + int index = indexOf(item); + setSelection(index); +} +/** + * Set the selection to the tab at the specified index. + * + * @param index the index of the tab item to be selected + * + * @exception SWTException + */ +public void setSelection(int index) { + checkWidget(); + if (index < 0 || index >= items.length) return; + CTabItem selection = items[index]; + if (selectedIndex == index) { + showItem(selection); + return; + } + + int oldIndex = selectedIndex; + selectedIndex = index; + if (oldIndex != -1) { + items[oldIndex].closeImageState = SWT.BACKGROUND; + items[oldIndex].state &= ~SWT.SELECTED; + } + selection.closeImageState = SWT.NONE; + selection.showing = false; + selection.state |= SWT.SELECTED; + + Control newControl = selection.control; + Control oldControl = null; + if (oldIndex != -1) { + oldControl = items[oldIndex].control; + } + + if (newControl != oldControl) { + if (newControl != null && !newControl.isDisposed()) { + newControl.setBounds(getClientArea()); + newControl.setVisible(true); + } + if (oldControl != null && !oldControl.isDisposed()) { + oldControl.setVisible(false); + } + } + showItem(selection); + redraw(); +} +void setSelection(int index, boolean notify) { + int oldSelectedIndex = selectedIndex; + setSelection(index); + if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) { + Event event = new Event(); + event.item = getItem(selectedIndex); + notifyListeners(SWT.Selection, event); + } +} +/** + * Sets the receiver's selection background color to the color specified + * by the argument, or to the default system color for the control + * if the argument is null. + * + * @param color the new color (or null) + * + * @exception IllegalArgumentException + * @exception SWTException + * + * @since 3.0 + */ +public void setSelectionBackground (Color color) { + if (inDispose) return; + checkWidget(); + setSelectionHighlightGradientColor(null); + if (selectionBackground == color) return; + if (color == null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND); + selectionBackground = color; + renderer.createAntialiasColors(); //TODO: need better caching strategy + if (selectedIndex > -1) redraw(); +} +/** + * Specify a gradient of colours to be draw in the background of the selected tab. + * For example to draw a gradient that varies from dark blue to blue and then to + * white, use the following call to setBackground: + *
+ *	cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
+ *		                           display.getSystemColor(SWT.COLOR_BLUE),
+ *		                           display.getSystemColor(SWT.COLOR_WHITE),
+ *		                           display.getSystemColor(SWT.COLOR_WHITE)},
+ *		               new int[] {25, 50, 100});
+ * 
+ * + * @param colors an array of Color that specifies the colors to appear in the gradient + * in order of appearance left to right. The value null clears the + * background gradient. The value null can be used inside the array of + * Color to specify the background color. + * @param percents an array of integers between 0 and 100 specifying the percent of the width + * of the widget at which the color should change. The size of the percents array must be one + * less than the size of the colors array. + * + * @exception SWTException + */ +public void setSelectionBackground(Color[] colors, int[] percents) { + setSelectionBackground(colors, percents, false); +} +/** + * Specify a gradient of colours to be draw in the background of the selected tab. + * For example to draw a vertical gradient that varies from dark blue to blue and then to + * white, use the following call to setBackground: + *
+ *	cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
+ *		                           display.getSystemColor(SWT.COLOR_BLUE),
+ *		                           display.getSystemColor(SWT.COLOR_WHITE),
+ *		                           display.getSystemColor(SWT.COLOR_WHITE)},
+ *		                  new int[] {25, 50, 100}, true);
+ * 
+ * + * @param colors an array of Color that specifies the colors to appear in the gradient + * in order of appearance left to right. The value null clears the + * background gradient. The value null can be used inside the array of + * Color to specify the background color. + * @param percents an array of integers between 0 and 100 specifying the percent of the width + * of the widget at which the color should change. The size of the percents array must be one + * less than the size of the colors array. + * + * @param vertical indicate the direction of the gradient. True is vertical and false is horizontal. + * + * @exception SWTException + * + * @since 3.0 + */ +public void setSelectionBackground(Color[] colors, int[] percents, boolean vertical) { + checkWidget(); + int colorsLength; + Color highlightBeginColor = null; //null == no highlight + + if (colors != null) { + //The colors array can optionally have an extra entry which describes the highlight top color + //Thus its either one or two larger than the percents array + if (percents == null || + ! ((percents.length == colors.length - 1) || (percents.length == colors.length - 2))){ + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + for (int i = 0; i < percents.length; i++) { + if (percents[i] < 0 || percents[i] > 100) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + if (i > 0 && percents[i] < percents[i-1]) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + //If the colors is exactly two more than percents then last is highlight + //Keep track of *real* colorsLength (minus the highlight) + if(percents.length == colors.length - 2) { + highlightBeginColor = colors[colors.length - 1]; + colorsLength = colors.length - 1; + } else { + colorsLength = colors.length; + } + if (getDisplay().getDepth() < 15) { + // Don't use gradients on low color displays + colors = new Color[] {colors[colorsLength - 1]}; + colorsLength = colors.length; + percents = new int[] {}; + } + } else { + colorsLength = 0; + } + + // Are these settings the same as before? + if (selectionBgImage == null) { + if ((selectionGradientColors != null) && (colors != null) && + (selectionGradientColors.length == colorsLength)) { + boolean same = false; + for (int i = 0; i < selectionGradientColors.length; i++) { + if (selectionGradientColors[i] == null) { + same = colors[i] == null; + } else { + same = selectionGradientColors[i].equals(colors[i]); + } + if (!same) break; + } + if (same) { + for (int i = 0; i < selectionGradientPercents.length; i++) { + same = selectionGradientPercents[i] == percents[i]; + if (!same) break; + } + } + if (same && this.selectionGradientVertical == vertical) return; + } + } else { + selectionBgImage = null; + } + // Store the new settings + if (colors == null) { + selectionGradientColors = null; + selectionGradientPercents = null; + selectionGradientVertical = false; + setSelectionBackground((Color)null); + setSelectionHighlightGradientColor(null); + } else { + selectionGradientColors = new Color[colorsLength]; + for (int i = 0; i < colorsLength; ++i) { + selectionGradientColors[i] = colors[i]; + } + selectionGradientPercents = new int[percents.length]; + for (int i = 0; i < percents.length; ++i) { + selectionGradientPercents[i] = percents[i]; + } + selectionGradientVertical = vertical; + setSelectionBackground(selectionGradientColors[selectionGradientColors.length-1]); + setSelectionHighlightGradientColor(highlightBeginColor); + } + + // Refresh with the new settings + if (selectedIndex > -1) redraw(); +} + +/* + * Set the color for the highlight start for selected tabs. + * Update the cache of highlight gradient colors if required. + */ +void setSelectionHighlightGradientColor(Color start) { + if (inDispose) return; + renderer.setSelectionHighlightGradientColor(start); //TODO: need better caching strategy +} + +/** + * Set the image to be drawn in the background of the selected tab. Image + * is stretched or compressed to cover entire selection tab area. + * + * @param image the image to be drawn in the background + * + * @exception SWTException + */ +public void setSelectionBackground(Image image) { + checkWidget(); + setSelectionHighlightGradientColor(null); + if (image == selectionBgImage) return; + if (image != null) { + selectionGradientColors = null; + selectionGradientPercents = null; + renderer.disposeSelectionHighlightGradientColors(); //TODO: need better caching strategy + } + selectionBgImage = image; + renderer.createAntialiasColors(); //TODO: need better caching strategy + if (selectedIndex > -1) redraw(); +} +/** + * Set the foreground color of the selected tab. + * + * @param color the color of the text displayed in the selected tab + * + * @exception SWTException + */ +public void setSelectionForeground (Color color) { + checkWidget(); + if (selectionForeground == color) return; + if (color == null) color = getDisplay().getSystemColor(SELECTION_FOREGROUND); + selectionForeground = color; + if (selectedIndex > -1) redraw(); +} + +/** + * Sets the shape that the CTabFolder will use to render itself. + * + * @param simple true if the CTabFolder should render itself in a simple, traditional style + * + * @exception SWTException + * + * @since 3.0 + */ +public void setSimple(boolean simple) { + checkWidget(); + if (this.simple != simple) { + this.simple = simple; + updateFolder(UPDATE_TAB_HEIGHT | REDRAW); + } +} +/** + * Sets the number of tabs that the CTabFolder should display + * + * @param single true if only the selected tab should be displayed otherwise, multiple tabs will be shown. + * + * @exception SWTException + * + * @since 3.0 + */ +public void setSingle(boolean single) { + checkWidget(); + if (this.single != single) { + this.single = single; + if (!single) { + for (int i = 0; i < items.length; i++) { + if (i != selectedIndex && items[i].closeImageState == SWT.NONE) { + items[i].closeImageState = SWT.BACKGROUND; + } + } + } + updateFolder(REDRAW); + } +} + +int getControlY(Point size, Rectangle[] rects, int borderBottom, int borderTop, int i) { + int center = fixedTabHeight != SWT.DEFAULT ? 0 : (tabHeight - rects[i].height)/2; + return onBottom ? size.y - borderBottom - tabHeight + center : 1 + borderTop + center; +} + +/** + * Specify a fixed height for the tab items. If no height is specified, + * the default height is the height of the text or the image, whichever + * is greater. Specifying a height of -1 will revert to the default height. + * + * @param height the point value of the height or -1 + * + * @exception SWTException + */ +public void setTabHeight(int height) { + checkWidget(); + if (height < -1) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + fixedTabHeight = height; + updateFolder(UPDATE_TAB_HEIGHT); +} +/** + * Specify whether the tabs should appear along the top of the folder + * or along the bottom of the folder. + * + * @param position SWT.TOP for tabs along the top or SWT.BOTTOM for tabs along the bottom + * + * @exception SWTException + * + * @since 3.0 + */ +public void setTabPosition(int position) { + checkWidget(); + if (position != SWT.TOP && position != SWT.BOTTOM) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + if (onBottom != (position == SWT.BOTTOM)) { + onBottom = position == SWT.BOTTOM; + updateFolder(REDRAW); + } +} +/** + * Set the control that appears in the top right corner of the tab folder. + * Typically this is a close button or a composite with a Menu and close button. + * The topRight control is optional. Setting the top right control to null will + * remove it from the tab folder. + * + * @param control the control to be displayed in the top right corner or null + * + * @exception SWTException + * + * @since 2.1 + */ +public void setTopRight(Control control) { + setTopRight(control, SWT.RIGHT); +} +/** + * Set the control that appears in the top right corner of the tab folder. + * Typically this is a close button or a composite with a Menu and close button. + * The topRight control is optional. Setting the top right control to null + * will remove it from the tab folder. + *

+ * The alignment parameter sets the layout of the control in the tab area. + * SWT.RIGHT will cause the control to be positioned on the far + * right of the folder and it will have its default size. SWT.FILL + * will size the control to fill all the available space to the right of the + * last tab. If there is no available space, the control will not be visible. + * SWT.RIGHT | SWT.WRAP will allow the control to wrap below the + * tabs if there is not enough available space to the right of the last tab. + *

+ * + * @param control the control to be displayed in the top right corner or null + * @param alignment SWT.RIGHT or SWT.FILL or SWT.RIGHT | SWT.WRAP + * + * @exception SWTException + * + * @since 3.0 + */ +public void setTopRight(Control control, int alignment) { + checkWidget(); + if (alignment != SWT.RIGHT && alignment != SWT.FILL && alignment != (SWT.RIGHT | SWT.WRAP)) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + if (control != null && (control.isDisposed() || control.getParent() != this)) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + if (topRight == control && topRightAlignment == alignment) return; + if (topRight != null && !topRight.isDisposed()) removeTabControl(topRight, false); + topRight = control; + topRightAlignment = alignment; + alignment &= ~SWT.RIGHT; + if (control != null) addTabControl(control, SWT.TRAIL | alignment, -1, false); + updateFolder(UPDATE_TAB_HEIGHT | REDRAW); +} + + +/** + * Specify whether the close button appears + * when the user hovers over an unselected tabs. + * + * @param visible true makes the close button appear + * + * @exception SWTException + * + * @since 3.0 + */ +public void setUnselectedCloseVisible(boolean visible) { + checkWidget(); + if (showUnselectedClose == visible) return; + // display close button when mouse hovers + showUnselectedClose = visible; + updateFolder(REDRAW); +} +/** + * Specify whether the image appears on unselected tabs. + * + * @param visible true makes the image appear + * + * @exception SWTException + * + * @since 3.0 + */ +public void setUnselectedImageVisible(boolean visible) { + checkWidget(); + if (showUnselectedImage == visible) return; + // display image on unselected items + showUnselectedImage = visible; + updateFolder(REDRAW); +} +/** + * Shows the item. If the item is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled until + * the item is visible. + * + * @param item the item to be shown + * + * @exception IllegalArgumentException + * @exception SWTException + * + * @see CTabFolder#showSelection() + * + * @since 2.0 + */ +public void showItem (CTabItem item) { + checkWidget(); + if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); + if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + int index = indexOf(item); + if (index == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); + int idx = -1; + for (int i = 0; i < priority.length; i++) { + if (priority[i] == index) { + idx = i; + break; + } + } + if (mru) { + // move to front of mru order + int[] newPriority = new int[priority.length]; + System.arraycopy(priority, 0, newPriority, 1, idx); + System.arraycopy(priority, idx+1, newPriority, idx+1, priority.length - idx - 1); + newPriority[0] = index; + priority = newPriority; + } + if (item.showing) return; + updateFolder(REDRAW_TABS); +} +void showList (Rectangle rect) { + if (items.length == 0 || !showChevron) return; + if (showMenu == null || showMenu.isDisposed()) { + showMenu = new Menu(getShell(), getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT)); + } else { + MenuItem[] items = showMenu.getItems(); + for (int i = 0; i < items.length; i++) { + items[i].dispose(); + } + } + final String id = "CTabFolder_showList_Index"; //$NON-NLS-1$ + for (int i = 0; i < items.length; i++) { + CTabItem tab = items[i]; + if (tab.showing) continue; + MenuItem item = new MenuItem(showMenu, SWT.NONE); + // Bug 533124 In the case where you have multi line tab text, we force the drop-down menu to have single line entries to ensure consistent behavior across platforms. + item.setText(tab.getText().replace("\n", " ")); + item.setImage(tab.getImage()); + item.setData(id, tab); + item.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + MenuItem menuItem = (MenuItem)e.widget; + int index = indexOf((CTabItem)menuItem.getData(id)); + CTabFolder.this.setSelection(index, true); + } + }); + } + int x = rect.x; + int y = rect.y + rect.height; + Point location = getDisplay().map(this, null, x, y); + showMenu.setLocation(location.x, location.y); + showMenu.setVisible(true); +} +/** + * Shows the selection. If the selection is already showing in the receiver, + * this method simply returns. Otherwise, the items are scrolled until + * the selection is visible. + * + * @exception SWTException + * + * @see CTabFolder#showItem(CTabItem) + * + * @since 2.0 + */ +public void showSelection () { + checkWidget (); + if (selectedIndex != -1) { + showItem(getSelection()); + } +} + +void _setToolTipText (int x, int y) { + String oldTip = getToolTipText(); + String newTip = _getToolTip(x, y); + if (newTip == null || !newTip.equals(oldTip)) { + setToolTipText(newTip); + } +} + +boolean updateItems() { + return updateItems(selectedIndex); +} + +boolean updateItems (int showIndex) { + GC gc = new GC(this); + if (!single && !mru && showIndex != -1) { + // make sure selected item will be showing + int firstIndex = showIndex; + if (priority[0] < showIndex) { + int maxWidth = getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER); + int width = 0; + int[] widths = new int[items.length]; + for (int i = priority[0]; i <= showIndex; i++) { + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (i == selectedIndex) state |= SWT.SELECTED; + widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x; + width += widths[i]; + if (width > maxWidth) break; + } + if (width > maxWidth) { + width = 0; + for (int i = showIndex; i >= 0; i--) { + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (i == selectedIndex) state |= SWT.SELECTED; + if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x; + width += widths[i]; + if (width > maxWidth) break; + firstIndex = i; + } + } else { + firstIndex = priority[0]; + for (int i = showIndex + 1; i < items.length; i++) { + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (i == selectedIndex) state |= SWT.SELECTED; + widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x; + width += widths[i]; + if (width >= maxWidth) break; + } + if (width < maxWidth) { + for (int i = priority[0] - 1; i >= 0; i--) { + int state = CTabFolderRenderer.MINIMUM_SIZE; + if (i == selectedIndex) state |= SWT.SELECTED; + if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x; + width += widths[i]; + if (width > maxWidth) break; + firstIndex = i; + } + } + } + + } + if (firstIndex != priority[0]) { + int index = 0; + // enumerate tabs from first visible to the last existing one (sorted ascending) + for (int i = firstIndex; i < items.length; i++) { + priority[index++] = i; + } + // enumerate hidden tabs on the left hand from first visible one + // in the inverse order (sorted descending) so that the originally + // first opened tab is always at the end of the list + for (int i = firstIndex - 1; i >= 0; i--) { + priority[index++] = i; + } + } + } + + boolean oldShowChevron = showChevron; + boolean changed = setItemSize(gc); + changed |= setItemLocation(gc); + setButtonBounds(gc); + changed |= showChevron != oldShowChevron; + if (changed && getToolTipText() != null) { + Point pt = getDisplay().getCursorLocation(); + pt = toControl(pt); + _setToolTipText(pt.x, pt.y); + } + gc.dispose(); + return changed; +} +boolean updateTabHeight(boolean force){ + int oldHeight = tabHeight; + GC gc = new GC(this); + tabHeight = renderer.computeSize(CTabFolderRenderer.PART_HEADER, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT).y; + gc.dispose(); + if (fixedTabHeight == SWT.DEFAULT && controls != null && controls.length > 0) { + for (int i = 0; i < controls.length; i++) { + if ((controlAlignments[i] & SWT.WRAP) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) { + int topHeight = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).y; + topHeight += renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0,0,0,0).height + 1; + tabHeight = Math.max(topHeight, tabHeight); + } + } + } + if (!force && tabHeight == oldHeight) return false; + oldSize = null; + return true; +} + +void updateFolder (int flags) { + updateFlags |= flags; + if (updateRun != null) return; + updateRun = () -> { + updateRun = null; + if (isDisposed()) return; + runUpdate(); + }; + getDisplay().asyncExec(updateRun); +} + +void runUpdate() { + if (updateFlags == 0) return; + int flags = updateFlags; + updateFlags = 0; + Rectangle rectBefore = getClientArea(); + updateTabHeight(false); + updateItems(selectedIndex); + if ((flags & REDRAW) != 0) { + redraw(); + } else if ((flags & REDRAW_TABS) != 0) { + redrawTabs(); + } + Rectangle rectAfter = getClientArea(); + if (!rectBefore.equals(rectAfter)) { + notifyListeners(SWT.Resize, new Event()); + layout(); + } +} + +void updateBkImages() { + if (controls != null && controls.length > 0) { + for (int i = 0; i < controls.length; i++) { + Control control = controls[i]; + if (!control.isDisposed()) { + if (hovering) { + if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE); + control.setBackgroundImage(null); + control.setBackground(getBackground()); + } else { + if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_DEFAULT); + Rectangle bounds = control.getBounds(); + int tabHeight = getTabHeight(); + int height = this.getSize().y; + boolean wrapped = onBottom ? bounds.y + bounds.height < height - tabHeight : bounds.y > tabHeight; + if (wrapped || gradientColors == null) { + control.setBackgroundImage(null); + control.setBackground(getBackground()); + } else { + bounds.width = 10; + if (!onBottom) { + bounds.y = -bounds.y; + bounds.height -= 2*bounds.y - 1; + } else { + bounds.height += height - (bounds.y + bounds.height); + bounds.y = -1; + } + bounds.x = 0; + if (controlBkImages[i] != null) controlBkImages[i].dispose(); + controlBkImages[i] = new Image(control.getDisplay(), bounds); + GC gc = new GC(controlBkImages[i]); + renderer.draw(CTabFolderRenderer.PART_BACKGROUND, 0, bounds, gc); + gc.dispose(); + control.setBackground(null); + control.setBackgroundImage(controlBkImages[i]); + } + } + } + } + + } +} +String _getToolTip(int x, int y) { + CTabItem item = getItem(new Point (x, y)); + if (item == null) return null; + if (!item.showing) return null; + if ((showClose || item.showClose) && item.closeRect.contains(x, y)) { + return SWT.getMessage("SWT_Close"); //$NON-NLS-1$ + } + return item.getToolTipText(); +} +/** +* Set a control that can appear to the left or to the right of the folder tabs. +* This method can also be used instead of #setTopRight(Control). To remove a tab +* control, see#removeTabControl(Control); +*

+* The flags parameter sets the layout of the control in the tab area. +* SWT.LEAD will cause the control to be positioned on the left +* of the tabs. SWT.TRAIL will cause the control to be positioned on +* the far right of the folder and it will have its default size. SWT.TRAIL +* can be combined with SWT.FILLto fill all the available space to the +* right of the last tab. SWT.WRAP can also be added to SWT.TRAIL +* only to cause a control to wrap if there is not enough space to display it in its +* entirety. +*

+* @param control the control to be displayed in the top right corner or null +* +* @param flags valid combinations are: +* +* @exception SWTException +*/ +/*public*/ void addTabControl(Control control, int flags) { + checkWidget(); + addTabControl(control, flags, -1, true); +} + +void addTabControl(Control control, int flags, int index, boolean update) { + switch (flags) { + case SWT.TRAIL: + case SWT.TRAIL | SWT.WRAP: + case SWT.TRAIL | SWT.FILL: + case SWT.TRAIL | SWT.FILL | SWT.WRAP: + case SWT.LEAD: + break; + default: + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + break; + } + if (control != null && control.getParent() != this) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + //check for duplicates + for (int i = 0; i < controls.length; i++) { + if (controls[i] == control) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + } + int length = controls.length; + + control.addListener(SWT.Resize, listener); + + //Grow all 4 arrays + Control[] newControls = new Control [length + 1]; + System.arraycopy(controls, 0, newControls, 0, length); + controls = newControls; + int[] newAlignment = new int [length + 1]; + System.arraycopy(controlAlignments, 0, newAlignment, 0, length); + controlAlignments = newAlignment; + Rectangle[] newRect = new Rectangle [length + 1]; + System.arraycopy(controlRects, 0, newRect, 0, length); + controlRects = newRect; + Image[] newImage = new Image [length + 1]; + System.arraycopy(controlBkImages, 0, newImage, 0, length); + controlBkImages = newImage; + if (index == -1) { + index = length; + if (chevronTb != null && control != chevronTb) index--; + } + System.arraycopy (controls, index, controls, index + 1, length - index); + System.arraycopy (controlAlignments, index, controlAlignments, index + 1, length - index); + System.arraycopy (controlRects, index, controlRects, index + 1, length - index); + System.arraycopy (controlBkImages, index, controlBkImages, index + 1, length - index); + controls[index] = control; + controlAlignments[index] = flags; + controlRects[index] = new Rectangle(0, 0, 0, 0); + if (update) { + updateFolder(UPDATE_TAB_HEIGHT | REDRAW); + } +} + +/** +* Removes the control from the list of tab controls. +* +* @param control the control to be removed +* +* @exception SWTException +*/ +/*public*/ void removeTabControl (Control control) { + checkWidget(); + removeTabControl (control, true); +} + +void removeTabControl (Control control, boolean update) { + if (control != null && control.getParent() != this) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT); + } + int index = -1; + for (int i = 0; i < controls.length; i++) { + if (controls[i] == control){ + index = i; + break; + } + } + if (index == -1) return; + + if (!control.isDisposed()) { + control.removeListener(SWT.Resize, listener); + control.setBackground (null); + control.setBackgroundImage (null); + if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE); + } + + if (controlBkImages[index] != null && !controlBkImages[index].isDisposed()) controlBkImages[index].dispose(); + if (controls.length == 1) { + controls = new Control[0]; + controlAlignments = new int[0]; + controlRects = new Rectangle[0]; + controlBkImages = new Image[0]; + } else { + Control[] newControls = new Control [controls.length - 1]; + System.arraycopy(controls, 0, newControls, 0, index); + System.arraycopy(controls, index + 1, newControls, index, controls.length - index - 1); + controls = newControls; + + int[] newAlignments = new int [controls.length]; + System.arraycopy(controlAlignments, 0, newAlignments, 0, index); + System.arraycopy(controlAlignments, index + 1, newAlignments, index, controls.length - index); + controlAlignments = newAlignments; + + Rectangle[] newRects = new Rectangle [controls.length]; + System.arraycopy(controlRects, 0, newRects, 0, index); + System.arraycopy(controlRects, index + 1, newRects, index, controls.length - index); + controlRects = newRects; + + Image[] newBkImages = new Image [controls.length]; + System.arraycopy(controlBkImages, 0, newBkImages, 0, index); + System.arraycopy(controlBkImages, index + 1, newBkImages, index, controls.length - index); + controlBkImages = newBkImages; + } + if (update) { + updateFolder(UPDATE_TAB_HEIGHT | REDRAW); + } +} + +int getWrappedHeight (Point size) { + boolean[][] positions = new boolean[1][]; + Rectangle[] rects = computeControlBounds(size, positions); + int minY = Integer.MAX_VALUE, maxY = 0, wrapHeight = 0; + for (int i = 0; i < rects.length; i++) { + if (positions[0][i]) { + minY = Math.min(minY, rects[i].y); + maxY = Math.max(maxY, rects[i].y + rects[i].height); + wrapHeight = maxY - minY; + } + } + return wrapHeight; +} + +/** + * Sets whether a chevron is shown when there are more items to be displayed. + * + * @exception IllegalArgumentException + * @exception SWTException + * + */ +/*public*/ void setChevronVisible(boolean visible) { + checkWidget(); + if (chevronVisible == visible) return; + chevronVisible = visible; + updateFolder(UPDATE_TAB_HEIGHT | REDRAW); +} + + boolean shouldHighlight() { + return this.highlight && highlightEnabled; + } + +/** + * Sets whether the selected tab is rendered as highlighted. + * + * @param enabled + * {@code true} if the selected tab should be highlighted, + * {@code false} otherwise. + * @exception SWTException + * + * @since 3.106 + */ +public void setHighlightEnabled(boolean enabled) { + checkWidget(); + if (highlightEnabled == enabled) { + return; + } + highlightEnabled = enabled; + updateFolder(REDRAW); +} + +/** + * Returns true if the selected tab is rendered as + * highlighted. + * + * @return true if the selected tab is rendered as + * highlighted + * + * @exception SWTException + * + * @since 3.106 + */ +public boolean getHighlightEnabled() { + checkWidget(); + return highlightEnabled; +} +}