Mercurial > projects > dwt-mac
view dwt/custom/CTabFolder.d @ 13:f565d3a95c0a
Ported dwt.internal
author | Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com> |
---|---|
date | Fri, 22 Aug 2008 16:46:34 +0200 |
parents | 380af2bdd8e5 |
children | 6337764516f1 |
line wrap: on
line source
/******************************************************************************* * Copyright (c) 2000, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * * Port to the D programming language: * Jacob Carlborg <jacob.carlborg@gmail.com> *******************************************************************************/ module dwt.custom.CTabFolder; import dwt.DWT; import dwt.DWTException; import dwt.accessibility.ACC; import dwt.accessibility.Accessible; import dwt.accessibility.AccessibleAdapter; import dwt.accessibility.AccessibleControlAdapter; import dwt.accessibility.AccessibleControlEvent; import dwt.accessibility.AccessibleEvent; import dwt.custom.CTabFolderListener; import dwt.custom.CTabFolder2Listener; import dwt.custom.CTabItem; import dwt.events.SelectionAdapter; import dwt.events.SelectionEvent; import dwt.events.SelectionListener; import dwt.graphics.Color; import dwt.graphics.Font; import dwt.graphics.FontData; import dwt.graphics.GC; import dwt.graphics.Image; import dwt.graphics.Point; import dwt.graphics.RGB; import dwt.graphics.Rectangle; import dwt.graphics.Region; import dwt.widgets.Composite; import dwt.widgets.Control; import dwt.widgets.Display; import dwt.widgets.Event; import dwt.widgets.Layout; import dwt.widgets.Listener; import dwt.widgets.Menu; import dwt.widgets.MenuItem; import dwt.widgets.TypedListener; /** * * Instances of this class implement the notebook user interface * metaphor. It allows the user to select a notebook page from * set of pages. * <p> * The item children that may be added to instances of this class * must be of type <code>CTabItem</code>. * <code>Control</code> children are created and then set into a * tab item using <code>CTabItem#setControl</code>. * </p><p> * Note that although this class is a subclass of <code>Composite</code>, * it does not make sense to set a layout on it. * </p><p> * <dl> * <dt><b>Styles:</b></dt> * <dd>CLOSE, TOP, BOTTOM, FLAT, BORDER, SINGLE, MULTI</dd> * <dt><b>Events:</b></dt> * <dd>Selection</dd> * <dd>"CTabFolder2"</dd> * </dl> * <p> * Note: Only one of the styles TOP and BOTTOM * may be specified. * </p><p> * IMPORTANT: This class is <em>not</em> intended to be subclassed. * </p> */ public class CTabFolder : Composite { /** * marginWidth specifies the number of pixels 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 pixels 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) */ 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 */ 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 */ 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 */ public static RGB borderOutsideRGB = new RGB(171, 168, 165); /* sizing, positioning */ int xClient, yClient; bool onBottom = false; bool single = false; bool simple = true; int fixedTabHeight = DWT.DEFAULT; int tabHeight; int minChars = 20; /* item management */ CTabItem items[] = new CTabItem[0]; int firstIndex = -1; // index of the left most visible tab. int selectedIndex = -1; int[] priority = new int[0]; bool mru = false; Listener listener; /* 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; bool selectionGradientVertical; Color selectionForeground; Color selectionBackground; //selection fade end Color selectionFadeStart; Color selectionHighlightGradientBegin = null; //null is no highlight //Although we are given new colours all the time to show different states (active, etc), //some of which may have a highlight and some not, we'd like to retain the highlight colours //as a cache so that we can reuse them if we're again told to show the highlight. //We are relying on the fact that only one tab state usually gets a highlight, so only //a single cache is required. If that happens to not be true, cache simply becomes less effective, //but we don't leak colours. Color[] selectionHighlightGradientColorsCache = null; //null is a legal value, check on access /* Unselected item appearance */ Image bgImage; Color[] gradientColors; int[] gradientPercents; bool gradientVertical; bool showUnselectedImage = true; static Color borderColor; // close, min/max and chevron buttons bool showClose = false; bool showUnselectedClose = true; Rectangle chevronRect = new Rectangle(0, 0, 0, 0); int chevronImageState = NORMAL; bool showChevron = false; Menu showMenu; bool showMin = false; Rectangle minRect = new Rectangle(0, 0, 0, 0); bool minimized = false; int minImageState = NORMAL; bool showMax = false; Rectangle maxRect = new Rectangle(0, 0, 0, 0); bool maximized = false; int maxImageState = NORMAL; Control topRight; Rectangle topRightRect = new Rectangle(0, 0, 0, 0); int topRightAlignment = DWT.RIGHT; // borders and shapes int borderLeft = 0; int borderRight = 0; int borderTop = 0; int borderBottom = 0; int highlight_margin = 0; int highlight_header = 0; int[] curve; int[] topCurveHighlightStart; int[] topCurveHighlightEnd; int curveWidth = 0; int curveIndent = 0; // when disposing CTabFolder, don't try to layout the items or // change the selection as each child is destroyed. bool inDispose = false; // keep track of size changes in order to redraw only affected area // on Resize Point oldSize; Font oldFont; // internal constants static const int DEFAULT_WIDTH = 64; static const int DEFAULT_HEIGHT = 64; static const int BUTTON_SIZE = 18; static const int[] TOP_LEFT_CORNER = new int[][0 , 6 , 1 , 5 , 1 , 4 , 4 , 1 , 5 , 1 , 6 , 0]; // TOP_LEFT_CORNER_HILITE is laid out in reverse (ie. top to bottom) // so can fade in same direction as right swoop curve static const int[] TOP_LEFT_CORNER_HILITE = new int[][5 , 2 , 4 , 2 , 3 , 3 , 2 , 4 , 2 , 5 , 1 , 6]; static const int[] TOP_RIGHT_CORNER = new int[][-6 , 0 , -5 , 1 , -4 , 1 , -1 , 4 , -1 , 5 , 0 , 6]; static const int[] BOTTOM_LEFT_CORNER = new int[][0 , -6 , 1 , -5 , 1 , -4 , 4 , -1 , 5 , -1 , 6 , 0]; static const int[] BOTTOM_RIGHT_CORNER = new int[][-6 , 0 , -5 , -1 , -4 , -1 , -1 , -4 , -1 , -5 , 0 , -6]; static const int[] SIMPLE_TOP_LEFT_CORNER = new int[][0 , 2 , 1 , 1 , 2 , 0]; static const int[] SIMPLE_TOP_RIGHT_CORNER = new int[][-2 , 0 , -1 , 1 , 0 , 2]; static const int[] SIMPLE_BOTTOM_LEFT_CORNER = new int[][0 , -2 , 1 , -1 , 2 , 0]; static const int[] SIMPLE_BOTTOM_RIGHT_CORNER = new int[][-2 , 0 , -1 , -1 , 0 , -2]; static const int[] SIMPLE_UNSELECTED_INNER_CORNER = new int[][0 , 0]; static const int[] TOP_LEFT_CORNER_BORDERLESS = new int[][0 , 6 , 1 , 5 , 1 , 4 , 4 , 1 , 5 , 1 , 6 , 0]; static const int[] TOP_RIGHT_CORNER_BORDERLESS = new int[][-7 , 0 , -6 , 1 , -5 , 1 , -2 , 4 , -2 , 5 , -1 , 6]; static const int[] BOTTOM_LEFT_CORNER_BORDERLESS = new int[][0 , -6 , 1 , -6 , 1 , -5 , 2 , -4 , 4 , -2 , 5 , -1 , 6 , -1 , 6 , 0]; static const int[] BOTTOM_RIGHT_CORNER_BORDERLESS = new int[][-7 , 0 , -7 , -1 , -6 , -1 , -5 , -2 , -3 , -4 , -2 , -5 , -2 , -6 , -1 , -6]; static const int[] SIMPLE_TOP_LEFT_CORNER_BORDERLESS = new int[][0 , 2 , 1 , 1 , 2 , 0]; static const int[] SIMPLE_TOP_RIGHT_CORNER_BORDERLESS = new int[][-3 , 0 , -2 , 1 , -1 , 2]; static const int[] SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS = new int[][0 , -3 , 1 , -2 , 2 , -1 , 3 , 0]; static const int[] SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS = new int[][-4 , 0 , -3 , -1 , -2 , -2 , -1 , -3]; static const int SELECTION_FOREGROUND = DWT.COLOR_LIST_FOREGROUND; static const int SELECTION_BACKGROUND = DWT.COLOR_LIST_BACKGROUND; static const int BORDER1_COLOR = DWT.COLOR_WIDGET_NORMAL_SHADOW; static const int FOREGROUND = DWT.COLOR_WIDGET_FOREGROUND; static const int BACKGROUND = DWT.COLOR_WIDGET_BACKGROUND; static const int BUTTON_BORDER = DWT.COLOR_WIDGET_DARK_SHADOW; static const int BUTTON_FILL = DWT.COLOR_LIST_BACKGROUND; static const int NONE = 0; static const int NORMAL = 1; static const int HOT = 2; static const int SELECTED = 3; static const RGB CLOSE_FILL = new RGB(252, 160, 160); static const int CHEVRON_CHILD_ID = 0; static const int MINIMIZE_CHILD_ID = 1; static const int MAXIMIZE_CHILD_ID = 2; static const int EXTRA_CHILD_ID_COUNT = 3; /** * Constructs a new instance of this class given its parent * and a style value describing its behavior and appearance. * <p> * The style value is either one of the style constants defined in * class <code>DWT</code> which is applicable to instances of this * class, or must be built by <em>bitwise OR</em>'ing together * (that is, using the <code>int</code> "|" operator) two or more * of those <code>DWT</code> style constants. The class description * lists the style constants that are applicable to the class. * Style bits are also inherited from superclasses. * </p> * * @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 <ul> * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> * </ul> * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> * </ul> * * @see DWT#TOP * @see DWT#BOTTOM * @see DWT#FLAT * @see DWT#BORDER * @see DWT#SINGLE * @see DWT#MULTI * @see #getStyle() */ public this (Composite parent, int style) { super(parent, checkStyle(parent, style)); super.setLayout(new CTabFolderLayout()); int style2 = super.getStyle(); oldFont = getFont(); onBottom = (style2 & DWT.BOTTOM) !is 0; showClose = (style2 & DWT.CLOSE) !is 0; // showMin = (style2 & DWT.MIN) !is 0; - conflicts with DWT.TOP // showMax = (style2 & DWT.MAX) !is 0; - conflicts with DWT.BOTTOM single = (style2 & DWT.SINGLE) !is 0; borderLeft = borderRight = (style & DWT.BORDER) !is 0 ? 1 : 0; borderTop = onBottom ? borderLeft : 0; borderBottom = onBottom ? 0 : borderLeft; highlight_header = (style & DWT.FLAT) !is 0 ? 1 : 3; highlight_margin = (style & DWT.FLAT) !is 0 ? 0 : 2; //set up default colors Display display = getDisplay(); selectionForeground = display.getSystemColor(SELECTION_FOREGROUND); selectionBackground = display.getSystemColor(SELECTION_BACKGROUND); borderColor = display.getSystemColor(BORDER1_COLOR); updateTabHeight(false); initAccessible(); // Add all listeners listener = new class Listener { public void handleEvent (Event event) { switch (event.type) { case DWT.Dispose: onDispose(event); break; case DWT.DragDetect: onDragDetect(event); break; case DWT.FocusIn: onFocus(event); break; case DWT.FocusOut: onFocus(event); break; case DWT.KeyDown: onKeyDown(event); break; case DWT.MouseDoubleClick: onMouseDoubleClick(event); break; case DWT.MouseDown: onMouse(event); break; case DWT.MouseEnter: onMouse(event); break; case DWT.MouseExit: onMouse(event); break; case DWT.MouseMove: onMouse(event); break; case DWT.MouseUp: onMouse(event); break; case DWT.Paint: onPaint(event); break; case DWT.Resize: onResize(); break; case DWT.Traverse: onTraverse(event); break; } } }; int[] folderEvents = new int[][DWT.Dispose , DWT.DragDetect , DWT.FocusIn , DWT.FocusOut , DWT.KeyDown , DWT.MouseDoubleClick , DWT.MouseDown , DWT.MouseEnter , DWT.MouseExit , DWT.MouseMove , DWT.MouseUp , DWT.Paint , DWT.Resize , DWT.Traverse]; for (int i = 0; i < folderEvents.length; i++) { addListener(folderEvents[i], listener); } } static int checkStyle (Composite parent, int style) { int mask = DWT.CLOSE | DWT.TOP | DWT.BOTTOM | DWT.FLAT | DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT | DWT.SINGLE | DWT.MULTI; style = style & mask; // TOP and BOTTOM are mutually exclusive. // TOP is the default if ((style & DWT.TOP) !is 0) style = style & ~DWT.BOTTOM; // SINGLE and MULTI are mutually exclusive. // MULTI is the default if ((style & DWT.MULTI) !is 0) style = style & ~DWT.SINGLE; // reduce the flash by not redrawing the entire area on a Resize event style |= DWT.NO_REDRAW_RESIZE; //TEMPORARY CODE /* * The default background on carbon and some GTK themes is not a solid color * but a texture. To show the correct default background, we must allow * the operating system to draw it and therefore, we can not use the * NO_BACKGROUND style. The NO_BACKGROUND style is not required on platforms * that use double buffering which is true in both of these cases. */ String platform = DWT.getPlatform(); if ("carbon".opEquals(platform) || "gtk".opEquals(platform)) return style; //$NON-NLS-1$ //$NON-NLS-2$ //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 & DWT.RIGHT_TO_LEFT) !is 0) return style; if ((parent.getStyle() & DWT.MIRRORED) !is 0 && (style & DWT.LEFT_TO_RIGHT) is 0) return style; return style | DWT.NO_BACKGROUND; } static void fillRegion (GC gc, Region region) { // NOTE: region passed in to this function will be modified Region clipping = new Region(); gc.getClipping(clipping); region.intersect(clipping); gc.setClipping(region); gc.fillRectangle(region.getBounds()); gc.setClipping(clipping); clipping.dispose(); } /** * * 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 <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> * * @see CTabFolder2Listener * @see #removeCTabFolder2Listener(CTabFolder2Listener) * * @since 3.0 */ public void addCTabFolder2Listener (CTabFolder2Listener listener) { checkWidget(); if (listener is null) DWT.error(DWT.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 <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> * * @see CTabFolderListener * @see #removeCTabFolderListener(CTabFolderListener) * * @deprecated use addCTabFolder2Listener(CTabFolder2Listener) */ public void addCTabFolderListener (CTabFolderListener listener) { checkWidget(); if (listener is null) DWT.error(DWT.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; updateItems(); 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 <code>SelectionListener</code> * interface. * <p> * <code>widgetSelected</code> is called when the user changes the selected tab. * <code>widgetDefaultSelected</code> is not called. * </p> * * @param listener the listener which should be notified when the user changes the receiver's selection * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see SelectionListener * @see #removeSelectionListener * @see SelectionEvent */ public void addSelectionListener (SelectionListener listener) { checkWidget(); if (listener is null) { DWT.error(DWT.ERROR_NULL_ARGUMENT); } TypedListener typedListener = new TypedListener(listener); addListener(DWT.Selection, typedListener); addListener(DWT.DefaultSelection, typedListener); } void antialias (int[] shape, RGB lineRGB, RGB innerRGB, RGB outerRGB, GC gc) { // Don't perform anti-aliasing on Mac and WPF because the platform // already does it. The simple style also does not require anti-aliasing. if (simple || "carbon".opEquals(DWT.getPlatform()) || "wpf".opEquals( DWT.getPlatform())) return; //$NON-NLS-1$ // Don't perform anti-aliasing on low resolution displays if (getDisplay().getDepth() < 15) return; if (outerRGB !is null) { int index = 0; bool left = true; int oldY = onBottom ? 0 : getSize().y; int[] outer = new int[shape.length]; for (int i = 0; i < shape.length / 2; i++) { if (left && (index + 3 < shape.length)) { left = onBottom ? oldY <= shape[index + 3] : oldY >= shape[index + 3]; oldY = shape[index + 1]; } outer[index] = shape[index++] + (left ? -1 : +1); outer[index] = shape[index++]; } RGB from = lineRGB; RGB to = outerRGB; int red = from.red + 2 * (to.red - from.red) / 3; int green = from.green + 2 * (to.green - from.green) / 3; int blue = from.blue + 2 * (to.blue - from.blue) / 3; Color color = new Color(getDisplay(), red, green, blue); gc.setForeground(color); gc.drawPolyline(outer); color.dispose(); } if (innerRGB !is null) { int[] inner = new int[shape.length]; int index = 0; bool left = true; int oldY = onBottom ? 0 : getSize().y; for (int i = 0; i < shape.length / 2; i++) { if (left && (index + 3 < shape.length)) { left = onBottom ? oldY <= shape[index + 3] : oldY >= shape[index + 3]; oldY = shape[index + 1]; } inner[index] = shape[index++] + (left ? +1 : -1); inner[index] = shape[index++]; } RGB from = lineRGB; RGB to = innerRGB; int red = from.red + 2 * (to.red - from.red) / 3; int green = from.green + 2 * (to.green - from.green) / 3; int blue = from.blue + 2 * (to.blue - from.blue) / 3; Color color = new Color(getDisplay(), red, green, blue); gc.setForeground(color); gc.drawPolyline(inner); color.dispose(); } } public Rectangle computeTrim (int x, int y, int width, int height) { checkWidget(); int trimX = x - marginWidth - highlight_margin - borderLeft; int trimWidth = width + borderLeft + borderRight + 2 * marginWidth + 2 * highlight_margin; if (minimized) { int trimY = onBottom ? y - borderTop : y - highlight_header - tabHeight - borderTop; int trimHeight = borderTop + borderBottom + tabHeight + highlight_header; return new Rectangle(trimX, trimY, trimWidth, trimHeight); } else { int trimY = onBottom ? y - marginHeight - highlight_margin - borderTop : y - marginHeight - highlight_header - tabHeight - borderTop; int trimHeight = height + borderTop + borderBottom + 2 * marginHeight + tabHeight + highlight_header + highlight_margin; return new Rectangle(trimX, trimY, trimWidth, trimHeight); } } void createItem (CTabItem item, int index) { if (0 > index || index > getItemCount()) DWT.error(DWT.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] is index) { priorityIndex = next++; } newPriority[next++] = priority[i] >= index ? priority[i] + 1 : priority[i]; } newPriority[priorityIndex] = index; priority = newPriority; if (items.length is 1) { if (!updateTabHeight(false)) updateItems(); redraw(); } else { updateItems(); redrawTabs(); } } void destroyItem (CTabItem item) { if (inDispose) return; int index = indexOf(item); if (index is -1) return; if (items.length is 1) { items = new CTabItem[0]; priority = new int[0]; firstIndex = -1; selectedIndex = -1; Control control = item.getControl(); if (control !is null && !control.isDisposed()) { control.setVisible(false); } setToolTipText(null); setButtonBounds(); 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] is index) continue; newPriority[next++] = priority[i] > index ? priority[i] - 1 : priority[i]; } priority = newPriority; // move the selection if this item is selected if (selectedIndex is index) { Control control = item.getControl(); selectedIndex = -1; int nextSelection = mru ? priority[0] : Math.max(0, index - 1); setSelection(nextSelection, true); if (control !is null && !control.isDisposed()) { control.setVisible(false); } } else if (selectedIndex > index) { selectedIndex--; } updateItems(); redrawTabs(); } void drawBackground (GC gc, int[] shape, bool selected) { Color defaultBackground = selected ? selectionBackground : getBackground(); Image image = selected ? selectionBgImage : bgImage; Color[] colors = selected ? selectionGradientColors : gradientColors; int[] percents = selected ? selectionGradientPercents : gradientPercents; bool vertical = selected ? selectionGradientVertical : gradientVertical; Point size = getSize(); int width = size.x; int height = tabHeight + highlight_header; int x = 0; if (borderLeft > 0) { x += 1; width -= 2; } int y = onBottom ? size.y - borderBottom - height : borderTop; drawBackground(gc, shape, x, y, width, height, defaultBackground, image, colors, percents, vertical); } void drawBackground (GC gc, int[] shape, int x, int y, int width, int height, Color defaultBackground, Image image, Color[] colors, int[] percents, bool vertical) { Region clipping = new Region(); gc.getClipping(clipping); Region region = new Region(); region.add(shape); region.intersect(clipping); gc.setClipping(region); if (image !is null) { // draw the background image in shape gc.setBackground(defaultBackground); gc.fillRectangle(x, y, width, height); Rectangle imageRect = image.getBounds(); gc.drawImage(image, imageRect.x, imageRect.y, imageRect.width, imageRect.height, x, y, width, height); } else if (colors !is null) { // draw gradient if (colors.length is 1) { Color background = colors[0] !is null ? colors[0] : defaultBackground; gc.setBackground(background); gc.fillRectangle(x, y, width, height); } else { if (vertical) { if (onBottom) { int pos = 0; if (percents[percents.length - 1] < 100) { pos = percents[percents.length - 1] * height / 100; gc.setBackground(defaultBackground); gc.fillRectangle(x, y, width, pos); } Color lastColor = colors[colors.length - 1]; if (lastColor is null) lastColor = defaultBackground; for (int i = percents.length - 1; i >= 0; i--) { gc.setForeground(lastColor); lastColor = colors[i]; if (lastColor is null) lastColor = defaultBackground; gc.setBackground(lastColor); int gradientHeight = percents[i] * height / 100; gc.fillGradientRectangle(x, y + pos, width, gradientHeight, true); pos += gradientHeight; } } else { Color lastColor = colors[0]; if (lastColor is null) lastColor = defaultBackground; int pos = 0; for (int i = 0; i < percents.length; i++) { gc.setForeground(lastColor); lastColor = colors[i + 1]; if (lastColor is null) lastColor = defaultBackground; gc.setBackground(lastColor); int gradientHeight = percents[i] * height / 100; gc.fillGradientRectangle(x, y + pos, width, gradientHeight, true); pos += gradientHeight; } if (pos < height) { gc.setBackground(defaultBackground); gc.fillRectangle(x, pos, width, height - pos + 1); } } } else { //horizontal gradient y = 0; height = getSize().y; Color lastColor = colors[0]; if (lastColor is null) lastColor = defaultBackground; int pos = 0; for (int i = 0; i < percents.length; ++i) { gc.setForeground(lastColor); lastColor = colors[i + 1]; if (lastColor is null) lastColor = defaultBackground; gc.setBackground(lastColor); int gradientWidth = (percents[i] * width / 100) - pos; gc.fillGradientRectangle(x + pos, y, gradientWidth, height, false); pos += gradientWidth; } if (pos < width) { gc.setBackground(defaultBackground); gc.fillRectangle(x + pos, y, width - pos, height); } } } } else { // draw a solid background using default background in shape if ((getStyle() & DWT.NO_BACKGROUND) !is 0 || !defaultBackground.opEquals( getBackground())) { gc.setBackground(defaultBackground); gc.fillRectangle(x, y, width, height); } } gc.setClipping(clipping); clipping.dispose(); region.dispose(); } void drawBody (Event event) { GC gc = event.gc; Point size = getSize(); // fill in body if (!minimized) { int width = size.x - borderLeft - borderRight - 2 * highlight_margin; int height = size.y - borderTop - borderBottom - tabHeight - highlight_header - highlight_margin; // Draw highlight margin if (highlight_margin > 0) { int[] shape = null; if (onBottom) { int x1 = borderLeft; int y1 = borderTop; int x2 = size.x - borderRight; int y2 = size.y - borderBottom - tabHeight - highlight_header; shape = new int[][x1 , y1 , x2 , y1 , x2 , y2 , x2 - highlight_margin , y2 , x2 - highlight_margin , y1 + highlight_margin , x1 + highlight_margin , y1 + highlight_margin , x1 + highlight_margin , y2 , x1 , y2]; } else { int x1 = borderLeft; int y1 = borderTop + tabHeight + highlight_header; int x2 = size.x - borderRight; int y2 = size.y - borderBottom; shape = new int[][x1 , y1 , x1 + highlight_margin , y1 , x1 + highlight_margin , y2 - highlight_margin , x2 - highlight_margin , y2 - highlight_margin , x2 - highlight_margin , y1 , x2 , y1 , x2 , y2 , x1 , y2]; } // If horizontal gradient, show gradient across the whole area if (selectedIndex !is -1 && selectionGradientColors !is null && selectionGradientColors.length > 1 && !selectionGradientVertical) { drawBackground(gc, shape, true); } else if (selectedIndex is -1 && gradientColors !is null && gradientColors.length > 1 && !gradientVertical) { drawBackground(gc, shape, false); } else { gc.setBackground( selectedIndex is -1 ? getBackground() : selectionBackground); gc.fillPolygon(shape); } } //Draw client area if ((getStyle() & DWT.NO_BACKGROUND) !is 0) { gc.setBackground(getBackground()); gc.fillRectangle(xClient - marginWidth, yClient - marginHeight, width, height); } } else { if ((getStyle() & DWT.NO_BACKGROUND) !is 0) { int height = borderTop + tabHeight + highlight_header + borderBottom; if (size.y > height) { gc.setBackground(getParent().getBackground()); gc.fillRectangle(0, height, size.x, size.y - height); } } } //draw 1 pixel border around outside if (borderLeft > 0) { gc.setForeground(borderColor); int x1 = borderLeft - 1; int x2 = size.x - borderRight; int y1 = onBottom ? borderTop - 1 : borderTop + tabHeight; int y2 = onBottom ? size.y - tabHeight - borderBottom - 1 : size.y - borderBottom; gc.drawLine(x1, y1, x1, y2); // left gc.drawLine(x2, y1, x2, y2); // right if (onBottom) { gc.drawLine(x1, y1, x2, y1); // top } else { gc.drawLine(x1, y2, x2, y2); // bottom } } } void drawChevron (GC gc) { if (chevronRect.width is 0 || chevronRect.height is 0) return; // draw chevron (10x7) Display display = getDisplay(); Point dpi = display.getDPI(); int fontHeight = 72 * 10 / dpi.y; FontData fd = getFont().getFontData()[0]; fd.setHeight(fontHeight); Font f = new Font(display, fd); int fHeight = f.getFontData()[0].getHeight() * dpi.y / 72; int indent = Math.max(2, (chevronRect.height - fHeight - 4) / 2); int x = chevronRect.x + 2; int y = chevronRect.y + indent; int count; if (single) { count = selectedIndex is -1 ? items.length : items.length - 1; } else { int showCount = 0; while (showCount < priority.length && items[priority[showCount]].showing) { showCount++; } count = items.length - showCount; } String chevronString = count > 99 ? "99+" : String.valueOf(count); //$NON-NLS-1$ switch (chevronImageState) { case NORMAL: { Color chevronBorder = single ? getSelectionForeground() : getForeground(); gc.setForeground(chevronBorder); gc.setFont(f); gc.drawLine(x, y, x + 2, y + 2); gc.drawLine(x + 2, y + 2, x, y + 4); gc.drawLine(x + 1, y, x + 3, y + 2); gc.drawLine(x + 3, y + 2, x + 1, y + 4); gc.drawLine(x + 4, y, x + 6, y + 2); gc.drawLine(x + 6, y + 2, x + 5, y + 4); gc.drawLine(x + 5, y, x + 7, y + 2); gc.drawLine(x + 7, y + 2, x + 4, y + 4); gc.drawString(chevronString, x + 7, y + 3, true); break; } case HOT: { gc.setForeground(display.getSystemColor(BUTTON_BORDER)); gc.setBackground(display.getSystemColor(BUTTON_FILL)); gc.setFont(f); gc.fillRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, 6, 6); gc.drawRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width - 1, chevronRect.height - 1, 6, 6); gc.drawLine(x, y, x + 2, y + 2); gc.drawLine(x + 2, y + 2, x, y + 4); gc.drawLine(x + 1, y, x + 3, y + 2); gc.drawLine(x + 3, y + 2, x + 1, y + 4); gc.drawLine(x + 4, y, x + 6, y + 2); gc.drawLine(x + 6, y + 2, x + 5, y + 4); gc.drawLine(x + 5, y, x + 7, y + 2); gc.drawLine(x + 7, y + 2, x + 4, y + 4); gc.drawString(chevronString, x + 7, y + 3, true); break; } case SELECTED: { gc.setForeground(display.getSystemColor(BUTTON_BORDER)); gc.setBackground(display.getSystemColor(BUTTON_FILL)); gc.setFont(f); gc.fillRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, 6, 6); gc.drawRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width - 1, chevronRect.height - 1, 6, 6); gc.drawLine(x + 1, y + 1, x + 3, y + 3); gc.drawLine(x + 3, y + 3, x + 1, y + 5); gc.drawLine(x + 2, y + 1, x + 4, y + 3); gc.drawLine(x + 4, y + 3, x + 2, y + 5); gc.drawLine(x + 5, y + 1, x + 7, y + 3); gc.drawLine(x + 7, y + 3, x + 6, y + 5); gc.drawLine(x + 6, y + 1, x + 8, y + 3); gc.drawLine(x + 8, y + 3, x + 5, y + 5); gc.drawString(chevronString, x + 8, y + 4, true); break; } } f.dispose(); } void drawMaximize (GC gc) { if (maxRect.width is 0 || maxRect.height is 0) return; Display display = getDisplay(); // 5x4 or 7x9 int x = maxRect.x + (CTabFolder.BUTTON_SIZE - 10) / 2; int y = maxRect.y + 3; gc.setForeground(display.getSystemColor(BUTTON_BORDER)); gc.setBackground(display.getSystemColor(BUTTON_FILL)); switch (maxImageState) { case NORMAL: { if (!maximized) { gc.fillRectangle(x, y, 9, 9); gc.drawRectangle(x, y, 9, 9); gc.drawLine(x + 1, y + 2, x + 8, y + 2); } else { gc.fillRectangle(x, y + 3, 5, 4); gc.fillRectangle(x + 2, y, 5, 4); gc.drawRectangle(x, y + 3, 5, 4); gc.drawRectangle(x + 2, y, 5, 4); gc.drawLine(x + 3, y + 1, x + 6, y + 1); gc.drawLine(x + 1, y + 4, x + 4, y + 4); } break; } case HOT: { gc.fillRoundRectangle(maxRect.x, maxRect.y, maxRect.width, maxRect.height, 6, 6); gc.drawRoundRectangle(maxRect.x, maxRect.y, maxRect.width - 1, maxRect.height - 1, 6, 6); if (!maximized) { gc.fillRectangle(x, y, 9, 9); gc.drawRectangle(x, y, 9, 9); gc.drawLine(x + 1, y + 2, x + 8, y + 2); } else { gc.fillRectangle(x, y + 3, 5, 4); gc.fillRectangle(x + 2, y, 5, 4); gc.drawRectangle(x, y + 3, 5, 4); gc.drawRectangle(x + 2, y, 5, 4); gc.drawLine(x + 3, y + 1, x + 6, y + 1); gc.drawLine(x + 1, y + 4, x + 4, y + 4); } break; } case SELECTED: { gc.fillRoundRectangle(maxRect.x, maxRect.y, maxRect.width, maxRect.height, 6, 6); gc.drawRoundRectangle(maxRect.x, maxRect.y, maxRect.width - 1, maxRect.height - 1, 6, 6); if (!maximized) { gc.fillRectangle(x + 1, y + 1, 9, 9); gc.drawRectangle(x + 1, y + 1, 9, 9); gc.drawLine(x + 2, y + 3, x + 9, y + 3); } else { gc.fillRectangle(x + 1, y + 4, 5, 4); gc.fillRectangle(x + 3, y + 1, 5, 4); gc.drawRectangle(x + 1, y + 4, 5, 4); gc.drawRectangle(x + 3, y + 1, 5, 4); gc.drawLine(x + 4, y + 2, x + 7, y + 2); gc.drawLine(x + 2, y + 5, x + 5, y + 5); } break; } } } void drawMinimize (GC gc) { if (minRect.width is 0 || minRect.height is 0) return; Display display = getDisplay(); // 5x4 or 9x3 int x = minRect.x + (BUTTON_SIZE - 10) / 2; int y = minRect.y + 3; gc.setForeground(display.getSystemColor(BUTTON_BORDER)); gc.setBackground(display.getSystemColor(BUTTON_FILL)); switch (minImageState) { case NORMAL: { if (!minimized) { gc.fillRectangle(x, y, 9, 3); gc.drawRectangle(x, y, 9, 3); } else { gc.fillRectangle(x, y + 3, 5, 4); gc.fillRectangle(x + 2, y, 5, 4); gc.drawRectangle(x, y + 3, 5, 4); gc.drawRectangle(x + 2, y, 5, 4); gc.drawLine(x + 3, y + 1, x + 6, y + 1); gc.drawLine(x + 1, y + 4, x + 4, y + 4); } break; } case HOT: { gc.fillRoundRectangle(minRect.x, minRect.y, minRect.width, minRect.height, 6, 6); gc.drawRoundRectangle(minRect.x, minRect.y, minRect.width - 1, minRect.height - 1, 6, 6); if (!minimized) { gc.fillRectangle(x, y, 9, 3); gc.drawRectangle(x, y, 9, 3); } else { gc.fillRectangle(x, y + 3, 5, 4); gc.fillRectangle(x + 2, y, 5, 4); gc.drawRectangle(x, y + 3, 5, 4); gc.drawRectangle(x + 2, y, 5, 4); gc.drawLine(x + 3, y + 1, x + 6, y + 1); gc.drawLine(x + 1, y + 4, x + 4, y + 4); } break; } case SELECTED: { gc.fillRoundRectangle(minRect.x, minRect.y, minRect.width, minRect.height, 6, 6); gc.drawRoundRectangle(minRect.x, minRect.y, minRect.width - 1, minRect.height - 1, 6, 6); if (!minimized) { gc.fillRectangle(x + 1, y + 1, 9, 3); gc.drawRectangle(x + 1, y + 1, 9, 3); } else { gc.fillRectangle(x + 1, y + 4, 5, 4); gc.fillRectangle(x + 3, y + 1, 5, 4); gc.drawRectangle(x + 1, y + 4, 5, 4); gc.drawRectangle(x + 3, y + 1, 5, 4); gc.drawLine(x + 4, y + 2, x + 7, y + 2); gc.drawLine(x + 2, y + 5, x + 5, y + 5); } break; } } } void drawTabArea (Event event) { GC gc = event.gc; Point size = getSize(); int[] shape = null; if (tabHeight is 0) { int style = getStyle(); if ((style & DWT.FLAT) !is 0 && (style & DWT.BORDER) is 0) return; int x1 = borderLeft - 1; int x2 = size.x - borderRight; int y1 = onBottom ? size.y - borderBottom - highlight_header - 1 : borderTop + highlight_header; int y2 = onBottom ? size.y - borderBottom : borderTop; if (borderLeft > 0 && onBottom) y2 -= 1; shape = new int[][x1 , y1 , x1 , y2 , x2 , y2 , x2 , y1]; // If horizontal gradient, show gradient across the whole area if (selectedIndex !is -1 && selectionGradientColors !is null && selectionGradientColors.length > 1 && !selectionGradientVertical) { drawBackground(gc, shape, true); } else if (selectedIndex is -1 && gradientColors !is null && gradientColors.length > 1 && !gradientVertical) { drawBackground(gc, shape, false); } else { gc.setBackground( selectedIndex is -1 ? getBackground() : selectionBackground); gc.fillPolygon(shape); } //draw 1 pixel border if (borderLeft > 0) { gc.setForeground(borderColor); gc.drawPolyline(shape); } return; } int x = Math.max(0, borderLeft - 1); int y = onBottom ? size.y - borderBottom - tabHeight : borderTop; int width = size.x - borderLeft - borderRight + 1; int height = tabHeight - 1; // Draw Tab Header if (onBottom) { int[] left, right; if ((getStyle() & DWT.BORDER) !is 0) { left = simple ? SIMPLE_BOTTOM_LEFT_CORNER : BOTTOM_LEFT_CORNER; right = simple ? SIMPLE_BOTTOM_RIGHT_CORNER : BOTTOM_RIGHT_CORNER; } else { left = simple ? SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS : BOTTOM_LEFT_CORNER_BORDERLESS; right = simple ? SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS : BOTTOM_RIGHT_CORNER_BORDERLESS; } shape = new int[left.length + right.length + 4]; int index = 0; shape[index++] = x; shape[index++] = y - highlight_header; for (int i = 0; i < left.length / 2; i++) { shape[index++] = x + left[2 * i]; shape[index++] = y + height + left[2 * i + 1]; if (borderLeft is 0) shape[index - 1] += 1; } for (int i = 0; i < right.length / 2; i++) { shape[index++] = x + width + right[2 * i]; shape[index++] = y + height + right[2 * i + 1]; if (borderLeft is 0) shape[index - 1] += 1; } shape[index++] = x + width; shape[index++] = y - highlight_header; } else { int[] left, right; if ((getStyle() & DWT.BORDER) !is 0) { left = simple ? SIMPLE_TOP_LEFT_CORNER : TOP_LEFT_CORNER; right = simple ? SIMPLE_TOP_RIGHT_CORNER : TOP_RIGHT_CORNER; } else { left = simple ? SIMPLE_TOP_LEFT_CORNER_BORDERLESS : TOP_LEFT_CORNER_BORDERLESS; right = simple ? SIMPLE_TOP_RIGHT_CORNER_BORDERLESS : TOP_RIGHT_CORNER_BORDERLESS; } shape = new int[left.length + right.length + 4]; int index = 0; shape[index++] = x; shape[index++] = y + height + highlight_header + 1; for (int i = 0; i < left.length / 2; i++) { shape[index++] = x + left[2 * i]; shape[index++] = y + left[2 * i + 1]; } for (int i = 0; i < right.length / 2; i++) { shape[index++] = x + width + right[2 * i]; shape[index++] = y + right[2 * i + 1]; } shape[index++] = x + width; shape[index++] = y + height + highlight_header + 1; } // Fill in background bool bkSelected = single && selectedIndex !is -1; drawBackground(gc, shape, bkSelected); // Fill in parent background for non-rectangular shape Region r = new Region(); r.add(new Rectangle(x, y, width + 1, height + 1)); r.subtract(shape); gc.setBackground(getParent().getBackground()); fillRegion(gc, r); r.dispose(); // Draw the unselected tabs. if (!single) { for (int i = 0; i < items.length; i++) { if (i !is selectedIndex && event.getBounds().intersects( items[i].getBounds())) { items[i].onPaint(gc, false); } } } // Draw selected tab if (selectedIndex !is -1) { CTabItem item = items[selectedIndex]; item.onPaint(gc, true); } else { // if no selected tab - draw line across bottom of all tabs int x1 = borderLeft; int y1 = (onBottom) ? size.y - borderBottom - tabHeight - 1 : borderTop + tabHeight; int x2 = size.x - borderRight; gc.setForeground(borderColor); gc.drawLine(x1, y1, x2, y1); } // Draw Buttons drawChevron(gc); drawMinimize(gc); drawMaximize(gc); // Draw border line if (borderLeft > 0) { RGB outside = getParent().getBackground().getRGB(); antialias(shape, borderColor.getRGB(), null, outside, gc); gc.setForeground(borderColor); gc.drawPolyline(shape); } } /** * Returns <code>true</code> if the receiver's border is visible. * * @return the receiver's border visibility state * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public bool getBorderVisible () { checkWidget(); return borderLeft is 1; } public Rectangle getClientArea () { checkWidget(); if (minimized) return new Rectangle(xClient, yClient, 0, 0); Point size = getSize(); int width = size.x - borderLeft - borderRight - 2 * marginWidth - 2 * highlight_margin; int height = size.y - borderTop - borderBottom - 2 * marginHeight - highlight_margin - highlight_header; height -= tabHeight; return new Rectangle(xClient, yClient, 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 <ul> * <li>ERROR_INVALID_RANGE - if the index is out of range</li> * </ul> * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public CTabItem getItem (int index) { //checkWidget(); if (index < 0 || index >= items.length) DWT.error(DWT.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 DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public CTabItem getItem (Point pt) { //checkWidget(); if (items.length is 0) return null; Point size = getSize(); if (size.x <= borderLeft + borderRight) return null; if (showChevron && chevronRect.contains(pt)) 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 DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public int getItemCount () { //checkWidget(); return items.length; } /** * Return the tab items. * * @return the tab items * * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public CTabItem[] getItems () { //checkWidget(); CTabItem[] tabItems = new CTabItem[items.length]; System.arraycopy(items, 0, tabItems, 0, items.length); return tabItems; } /* * 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 is null) return '\0'; int index = 0; int length = String.length(); do { while (index < length && String.charAt(index) !is '&') index++; if (++index >= length) return '\0'; if (String.charAt(index) !is '&') 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) !is '&')) index++; if (++index >= length) return String; if (String.charAt(index) !is '&') { return String.substring(0, index - 1) + String.substring(index, length); } index++; } while (index < length); return String; } /** * Returns <code>true</code> if the receiver is minimized. * * @return the receiver's minimized state * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public bool getMinimized () { checkWidget(); return minimized; } /** * Returns <code>true</code> if the minimize button * is visible. * * @return the visibility of the minimized button * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public bool 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 <code>true</code> if the receiver is maximized. * <p> * * @return the receiver's maximized state * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public bool getMaximized () { checkWidget(); return maximized; } /** * Returns <code>true</code> if the maximize button * is visible. * * @return the visibility of the maximized button * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public bool getMaximizeVisible () { checkWidget(); return showMax; } /** * Returns <code>true</code> if the receiver displays most * recently used tabs and <code>false</code> otherwise. * <p> * 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. * <p> * 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).</p> * * @return the receiver's header's visibility state * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public bool getMRUVisible () { checkWidget(); return mru; } int getRightItemEdge () { int x = getSize().x - borderRight - 3; if (showMin) x -= BUTTON_SIZE; if (showMax) x -= BUTTON_SIZE; if (showChevron) x -= 3 * BUTTON_SIZE / 2; if (topRight !is null && topRightAlignment !is DWT.FILL) { Point rightSize = topRight.computeSize(DWT.DEFAULT, DWT.DEFAULT); x -= rightSize.x + 3; } 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 DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public CTabItem getSelection () { //checkWidget(); if (selectedIndex is -1) return null; return items[selectedIndex]; } /** * Returns the receiver's selection background color. * * @return the selection background color of the receiver * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @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 DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @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 DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public int getSelectionIndex () { //checkWidget(); return selectedIndex; } /** * Returns <code>true</code> if the CTabFolder is rendered * with a simple, traditional shape. * * @return <code>true</code> if the CTabFolder is rendered with a simple shape * * @since 3.0 */ public bool getSimple () { checkWidget(); return simple; } /** * Returns <code>true</code> if the CTabFolder only displays the selected tab * and <code>false</code> if the CTabFolder displays multiple tabs. * * @return <code>true</code> if the CTabFolder only displays the selected tab and <code>false</code> if the CTabFolder displays multiple tabs * * @since 3.0 */ public bool getSingle () { checkWidget(); return single; } public int getStyle () { int style = super.getStyle(); style &= ~(DWT.TOP | DWT.BOTTOM); style |= onBottom ? DWT.BOTTOM : DWT.TOP; style &= ~(DWT.SINGLE | DWT.MULTI); style |= single ? DWT.SINGLE : DWT.MULTI; if (borderLeft !is 0) style |= DWT.BORDER; style &= ~DWT.CLOSE; if (showClose) style |= DWT.CLOSE; return style; } /** * Returns the height of the tab * * @return the height of the tab * * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public int getTabHeight () { checkWidget(); if (fixedTabHeight !is DWT.DEFAULT) return fixedTabHeight; return tabHeight - 1; // -1 for line drawn across top of tab } /** * Returns the position of the tab. Possible values are DWT.TOP or DWT.BOTTOM. * * @return the position of the tab * * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public int getTabPosition () { checkWidget(); return onBottom ? DWT.BOTTOM : DWT.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 DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> * * @since 2.1 */ public Control getTopRight () { checkWidget(); return topRight; } /** * Returns <code>true</code> if the close button appears * when the user hovers over an unselected tabs. * * @return <code>true</code> if the close button appears on unselected tabs * * @since 3.0 */ public bool getUnselectedCloseVisible () { checkWidget(); return showUnselectedClose; } /** * Returns <code>true</code> if an image appears * in unselected tabs. * * @return <code>true</code> if an image appears in unselected tabs * * @since 3.0 */ public bool 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 <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public int indexOf (CTabItem item) { checkWidget(); if (item is null) { DWT.error(DWT.ERROR_NULL_ARGUMENT); } for (int i = 0; i < items.length; i++) { if (items[i] is item) return i; } return -1; } void initAccessible () { const Accessible accessible = getAccessible(); accessible.addAccessibleListener(new class AccessibleAdapter { public void getName (AccessibleEvent e) { String name = null; int childID = e.childID; if (childID >= 0 && childID < items.length) { name = stripMnemonic(items[childID].getText()); } else if (childID is items.length + CHEVRON_CHILD_ID) { name = DWT.getMessage("DWT_ShowList"); //$NON-NLS-1$ } else if (childID is items.length + MINIMIZE_CHILD_ID) { name = minimized ? DWT.getMessage("DWT_Restore") : DWT.getMessage( "DWT_Minimize"); //$NON-NLS-1$ //$NON-NLS-2$ } else if (childID is items.length + MAXIMIZE_CHILD_ID) { name = maximized ? DWT.getMessage("DWT_Restore") : DWT.getMessage( "DWT_Maximize"); //$NON-NLS-1$ //$NON-NLS-2$ } e.result = name; } public void getHelp (AccessibleEvent e) { String help = null; int childID = e.childID; if (childID is ACC.CHILDID_SELF) { help = getToolTipText(); } else if (childID >= 0 && childID < items.length) { help = items[childID].getToolTipText(); } e.result = help; } 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 !is null) { char mnemonic = _findMnemonic(text); if (mnemonic !is '\0') { shortcut = "Alt+" + mnemonic; //$NON-NLS-1$ } } } e.result = shortcut; } } ); accessible.addAccessibleControlListener( new class AccessibleControlAdapter { 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 is ACC.CHILDID_NONE) { if (showChevron && chevronRect.contains(testPoint)) { childID = items.length + CHEVRON_CHILD_ID; } else if (showMin && minRect.contains(testPoint)) { childID = items.length + MINIMIZE_CHILD_ID; } else if (showMax && maxRect.contains(testPoint)) { childID = items.length + MAXIMIZE_CHILD_ID; } else { Rectangle location = getBounds(); location.height = location.height - getClientArea().height; if (location.contains(testPoint)) { childID = ACC.CHILDID_SELF; } } } e.childID = childID; } public void getLocation (AccessibleControlEvent e) { Rectangle location = null; Point pt = null; int childID = e.childID; if (childID is ACC.CHILDID_SELF) { location = getBounds(); pt = getParent().toDisplay(location.x, location.y); } else { if (childID >= 0 && childID < items.length && items[childID].isShowing()) { location = items[childID].getBounds(); } else if (showChevron && childID is items.length + CHEVRON_CHILD_ID) { location = chevronRect; } else if (showMin && childID is items.length + MINIMIZE_CHILD_ID) { location = minRect; } else if (showMax && childID is items.length + MAXIMIZE_CHILD_ID) { location = maxRect; } if (location !is null) { pt = toDisplay(location.x, location.y); } } if (location !is null && pt !is null) { e.x = pt.x; e.y = pt.y; e.width = location.width; e.height = location.height; } } public void getChildCount (AccessibleControlEvent e) { e.detail = items.length + EXTRA_CHILD_ID_COUNT; } public void getDefaultAction (AccessibleControlEvent e) { String action = null; int childID = e.childID; if (childID >= 0 && childID < items.length) { action = DWT.getMessage("DWT_Switch"); //$NON-NLS-1$ } if (childID >= items.length && childID < items.length + EXTRA_CHILD_ID_COUNT) { action = DWT.getMessage("DWT_Press"); //$NON-NLS-1$ } e.result = action; } public void getFocus (AccessibleControlEvent e) { int childID = ACC.CHILDID_NONE; if (isFocusControl()) { if (selectedIndex is -1) { childID = ACC.CHILDID_SELF; } else { childID = selectedIndex; } } e.childID = childID; } public void getRole (AccessibleControlEvent e) { int role = 0; int childID = e.childID; if (childID is ACC.CHILDID_SELF) { role = ACC.ROLE_TABFOLDER; } else if (childID >= 0 && childID < items.length) { role = ACC.ROLE_TABITEM; } else if (childID >= items.length && childID < items.length + EXTRA_CHILD_ID_COUNT) { role = ACC.ROLE_PUSHBUTTON; } e.detail = role; } public void getSelection (AccessibleControlEvent e) { e.childID = (selectedIndex is -1) ? ACC.CHILDID_NONE : selectedIndex; } public void getState (AccessibleControlEvent e) { int state = 0; int childID = e.childID; if (childID is 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 is childID) { state |= ACC.STATE_SELECTED; if (isFocusControl()) { state |= ACC.STATE_FOCUSED; } } } else if (childID is items.length + CHEVRON_CHILD_ID) { state = showChevron ? ACC.STATE_NORMAL : ACC.STATE_INVISIBLE; } else if (childID is items.length + MINIMIZE_CHILD_ID) { state = showMin ? ACC.STATE_NORMAL : ACC.STATE_INVISIBLE; } else if (childID is items.length + MAXIMIZE_CHILD_ID) { state = showMax ? ACC.STATE_NORMAL : ACC.STATE_INVISIBLE; } e.detail = state; } public void getChildren (AccessibleControlEvent e) { int childIdCount = items.length + EXTRA_CHILD_ID_COUNT; Object[] children = new Object[childIdCount]; for (int i = 0; i < childIdCount; i++) { children[i] = new Integer(i); } e.children = children; } }); addListener(DWT.Selection, new class(accessible) Listener { Accessible accessible; this (Accessible accessible) { this.accessible = accessible; } public void handleEvent (Event event) { if (isFocusControl()) { if (selectedIndex is -1) { accessible.setFocus(ACC.CHILDID_SELF); } else { accessible.setFocus(selectedIndex); } } } }); addListener(DWT.FocusIn, new class(accessible) Listener { Accessible accessible; this (Accessible accessible) { this.accessible = accessible; } public void handleEvent (Event event) { if (selectedIndex is -1) { accessible.setFocus(ACC.CHILDID_SELF); } else { accessible.setFocus(selectedIndex); } } }); } void onKeyDown (Event event) { switch (event.keyCode) { case DWT.ARROW_LEFT: case DWT.ARROW_RIGHT: int count = items.length; if (count is 0) return; if (selectedIndex is -1) return; int leadKey = (getStyle() & DWT.RIGHT_TO_LEFT) !is 0 ? DWT.ARROW_RIGHT : DWT.ARROW_LEFT; int offset = event.keyCode is 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 is selectedIndex) current = idx; visible[idx++] = i; } } if (current + offset >= 0 && current + offset < idx) { index = visible[current + offset]; } else { if (showChevron) { 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(DWT.Dispose, listener); notifyListeners(DWT.Dispose, event); event.type = DWT.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 !is null && !showMenu.isDisposed()) { showMenu.dispose(); showMenu = null; } int length = items.length; for (int i = 0; i < length; i++) { if (items[i] !is null) { items[i].dispose(); } } selectionGradientColors = null; selectionGradientPercents = null; selectionBgImage = null; selectionBackground = null; selectionForeground = null; disposeSelectionHighlightGradientColors(); } void onDragDetect (Event event) { bool consume = false; if (chevronRect.contains(event.x, event.y) || minRect.contains(event.x, event.y) || maxRect.contains(event.x, event.y)) { consume = true; } else { for (int i = 0; i < items.length; i++) { if (items[i].closeRect.contains(event.x, event.y)) { consume = true; break; } } } if (consume) { event.type = DWT.None; } } void onFocus (Event event) { checkWidget(); if (selectedIndex >= 0) { redraw(); } else { setSelection(0, true); } } bool onMnemonic (Event event) { char key = event.character; for (int i = 0; i < items.length; i++) { if (items[i] !is null) { char mnemonic = _findMnemonic(items[i].getText()); if (mnemonic !is '\0') { if (Character.toLowerCase(key) is mnemonic) { setSelection(i, true); return true; } } } } return false; } void onMouseDoubleClick (Event event) { if (event.button !is 1 || (event.stateMask & DWT.BUTTON2) !is 0 || (event.stateMask & DWT.BUTTON3) !is 0) return; Event e = new Event(); e.item = getItem(new Point(event.x, event.y)); if (e.item !is null) { notifyListeners(DWT.DefaultSelection, e); } } void onMouse (Event event) { int x = event.x, y = event.y; switch (event.type) { case DWT.MouseEnter: { setToolTipText(null); break; } case DWT.MouseExit: { if (minImageState !is NORMAL) { minImageState = NORMAL; redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); } if (maxImageState !is NORMAL) { maxImageState = NORMAL; redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); } if (chevronImageState !is NORMAL) { chevronImageState = NORMAL; redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false); } for (int i = 0; i < items.length; i++) { CTabItem item = items[i]; if (i !is selectedIndex && item.closeImageState !is NONE) { item.closeImageState = NONE; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); } if (i is selectedIndex && item.closeImageState !is NORMAL) { item.closeImageState = NORMAL; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); } } break; } case DWT.MouseDown: { if (minRect.contains(x, y)) { if (event.button !is 1) return; minImageState = SELECTED; redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); update(); return; } if (maxRect.contains(x, y)) { if (event.button !is 1) return; maxImageState = SELECTED; redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); update(); return; } if (chevronRect.contains(x, y)) { if (event.button !is 1) return; if (chevronImageState !is HOT) { chevronImageState = HOT; } else { chevronImageState = SELECTED; } redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false); update(); return; } CTabItem item = null; if (single) { if (selectedIndex !is -1) { Rectangle bounds = items[selectedIndex].getBounds(); if (bounds.contains(x, y)) { item = items[selectedIndex]; } } } else { for (int i = 0; i < items.length; i++) { Rectangle bounds = items[i].getBounds(); if (bounds.contains(x, y)) { item = items[i]; } } } if (item !is null) { if (item.closeRect.contains(x, y)) { if (event.button !is 1) return; item.closeImageState = SELECTED; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); update(); return; } int index = indexOf(item); if (item.showing) { setSelection(index, true); } return; } break; } case DWT.MouseMove: { _setToolTipText(event.x, event.y); bool close = false, minimize = false, maximize = false, chevron = false; if (minRect.contains(x, y)) { minimize = true; if (minImageState !is SELECTED && minImageState !is HOT) { minImageState = HOT; redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); } } if (maxRect.contains(x, y)) { maximize = true; if (maxImageState !is SELECTED && maxImageState !is HOT) { maxImageState = HOT; redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); } } if (chevronRect.contains(x, y)) { chevron = true; if (chevronImageState !is SELECTED && chevronImageState !is HOT) { chevronImageState = HOT; redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false); } } if (minImageState !is NORMAL && !minimize) { minImageState = NORMAL; redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); } if (maxImageState !is NORMAL && !maximize) { maxImageState = NORMAL; redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); } if (chevronImageState !is NORMAL && !chevron) { chevronImageState = NORMAL; redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false); } for (int i = 0; i < items.length; i++) { CTabItem item = items[i]; close = false; if (item.getBounds().contains(x, y)) { close = true; if (item.closeRect.contains(x, y)) { if (item.closeImageState !is SELECTED && item.closeImageState !is HOT) { item.closeImageState = HOT; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); } } else { if (item.closeImageState !is NORMAL) { item.closeImageState = NORMAL; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); } } } if (i !is selectedIndex && item.closeImageState !is NONE && !close) { item.closeImageState = NONE; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); } if (i is selectedIndex && item.closeImageState !is NORMAL && !close) { item.closeImageState = NORMAL; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); } } break; } case DWT.MouseUp: { if (event.button !is 1) return; if (chevronRect.contains(x, y)) { bool selected = chevronImageState is SELECTED; if (!selected) return; 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 (minRect.contains(x, y)) { bool selected = minImageState is SELECTED; minImageState = HOT; redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); if (!selected) return; CTabFolderEvent e = new CTabFolderEvent(this); e.widget = this; e.time = event.time; for (int i = 0; i < folderListeners.length; i++) { if (minimized) { folderListeners[i].restore(e); } else { folderListeners[i].minimize(e); } } return; } if (maxRect.contains(x, y)) { bool selected = maxImageState is SELECTED; maxImageState = HOT; redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); if (!selected) return; CTabFolderEvent e = new CTabFolderEvent(this); e.widget = this; e.time = event.time; for (int i = 0; i < folderListeners.length; i++) { if (maximized) { folderListeners[i].restore(e); } else { folderListeners[i].maximize(e); } } return; } CTabItem item = null; if (single) { if (selectedIndex !is -1) { Rectangle bounds = items[selectedIndex].getBounds(); if (bounds.contains(x, y)) { item = items[selectedIndex]; } } } else { for (int i = 0; i < items.length; i++) { Rectangle bounds = items[i].getBounds(); if (bounds.contains(x, y)) { item = items[i]; } } } if (item !is null) { if (item.closeRect.contains(x, y)) { bool selected = item.closeImageState is SELECTED; item.closeImageState = HOT; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); if (!selected) return; CTabFolderEvent e = new CTabFolderEvent(this); e.widget = this; e.time = event.time; e.item = item; e.doit = true; for (int j = 0; j < folderListeners.length; j++) { CTabFolder2Listener listener = folderListeners[j]; listener.close(e); } for (int j = 0; j < tabListeners.length; j++) { CTabFolderListener listener = tabListeners[j]; listener.itemClosed(e); } if (e.doit) item.dispose(); if (!isDisposed() && item.isDisposed()) { Display display = getDisplay(); Point pt = display.getCursorLocation(); pt = display.map(null, this, pt.x, pt.y); CTabItem nextItem = getItem(pt); if (nextItem !is null) { if (nextItem.closeRect.contains(pt)) { if (nextItem.closeImageState !is SELECTED && nextItem.closeImageState !is HOT) { nextItem.closeImageState = HOT; redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false); } } else { if (nextItem.closeImageState !is NORMAL) { nextItem.closeImageState = NORMAL; redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false); } } } } return; } } } } } bool onPageTraversal (Event event) { int count = items.length; if (count is 0) return false; int index = selectedIndex; if (index is -1) { index = 0; } else { int offset = (event.detail is DWT.TRAVERSE_PAGE_NEXT) ? 1 : -1; if (!mru) { index = (selectedIndex + offset + count) % count; } 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 is selectedIndex) current = idx; visible[idx++] = i; } } if (current + offset >= 0 && current + offset < idx) { index = visible[current + offset]; } else { if (showChevron) { 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 true; } } } setSelection(index, true); return true; } void onPaint (Event event) { if (inDispose) return; Font font = getFont(); if (oldFont is null || !oldFont.opEquals(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(DWT.COLOR_GREEN)); //gc.fillRectangle(-10, -10, size.x + 20, size.y+20); //} drawBody(event); gc.setFont(gcFont); gc.setForeground(gcForeground); gc.setBackground(gcBackground); drawTabArea(event); gc.setFont(gcFont); gc.setForeground(gcForeground); gc.setBackground(gcBackground); } void onResize () { if (updateItems()) redrawTabs(); Point size = getSize(); if (oldSize is null) { redraw(); } else { if (onBottom && size.y !is oldSize.y) { redraw(); } else { int x1 = Math.min(size.x, oldSize.x); if (size.x !is oldSize.x) x1 -= borderRight + highlight_margin + 2; if (!simple) x1 -= 5; // rounded top right corner int y1 = Math.min(size.y, oldSize.y); if (size.y !is oldSize.y) y1 -= borderBottom + highlight_margin; 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); } } oldSize = size; } void onTraverse (Event event) { switch (event.detail) { case DWT.TRAVERSE_ESCAPE: case DWT.TRAVERSE_RETURN: case DWT.TRAVERSE_TAB_NEXT: case DWT.TRAVERSE_TAB_PREVIOUS: Control focusControl = getDisplay().getFocusControl(); if (focusControl is this) event.doit = true; break; case DWT.TRAVERSE_MNEMONIC: event.doit = onMnemonic(event); if (event.doit) event.detail = DWT.TRAVERSE_NONE; break; case DWT.TRAVERSE_PAGE_NEXT: case DWT.TRAVERSE_PAGE_PREVIOUS: event.doit = onPageTraversal(event); event.detail = DWT.TRAVERSE_NONE; break; } } void redrawTabs () { Point size = getSize(); if (onBottom) { redraw(0, size.y - borderBottom - tabHeight - highlight_header - 1, size.x, borderBottom + tabHeight + highlight_header + 1, false); } else { redraw(0, 0, size.x, borderTop + tabHeight + highlight_header + 1, false); } } /** * Removes the listener. * * @param listener the listener * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> * * @see #addCTabFolder2Listener(CTabFolder2Listener) * * @since 3.0 */ public void removeCTabFolder2Listener (CTabFolder2Listener listener) { checkWidget(); if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); if (folderListeners.length is 0) return; int index = -1; for (int i = 0; i < folderListeners.length; i++) { if (listener is folderListeners[i]) { index = i; break; } } if (index is -1) return; if (folderListeners.length is 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 * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> * * @deprecated see removeCTabFolderCloseListener(CTabFolderListener) */ public void removeCTabFolderListener (CTabFolderListener listener) { checkWidget(); if (listener is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); if (tabListeners.length is 0) return; int index = -1; for (int i = 0; i < tabListeners.length; i++) { if (listener is tabListeners[i]) { index = i; break; } } if (index is -1) return; if (tabListeners.length is 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 <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see SelectionListener * @see #addSelectionListener */ public void removeSelectionListener (SelectionListener listener) { checkWidget(); if (listener is null) { DWT.error(DWT.ERROR_NULL_ARGUMENT); } removeListener(DWT.Selection, listener); removeListener(DWT.DefaultSelection, listener); } public void setBackground (Color color) { super.setBackground(color); redraw(); } /** * Specify a gradient of colours 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: * <pre> * cfolder.setBackground(new Color[]{display.getSystemColor(DWT.COLOR_DARK_BLUE), * display.getSystemColor(DWT.COLOR_BLUE), * display.getSystemColor(DWT.COLOR_WHITE), * display.getSystemColor(DWT.COLOR_WHITE)}, * new int[] {25, 50, 100}); * </pre> * * @param colors an array of Color that specifies the colors to appear in the gradient * in order of appearance left to right. The value <code>null</code> clears the * background gradient. The value <code>null</code> 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 DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> * * @since 3.0 */ void setBackground (Color[] colors, int[] percents) { setBackground(colors, percents, false); } /** * Specify a gradient of colours 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: * <pre> * cfolder.setBackground(new Color[]{display.getSystemColor(DWT.COLOR_DARK_BLUE), * display.getSystemColor(DWT.COLOR_BLUE), * display.getSystemColor(DWT.COLOR_WHITE), * display.getSystemColor(DWT.COLOR_WHITE)}, * new int[] {25, 50, 100}, true); * </pre> * * @param colors an array of Color that specifies the colors to appear in the gradient * in order of appearance left to right. The value <code>null</code> clears the * background gradient. The value <code>null</code> 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 DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> * * @since 3.0 */ void setBackground (Color[] colors, int[] percents, bool vertical) { checkWidget(); if (colors !is null) { if (percents is null || percents.length !is colors.length - 1) { DWT.error(DWT.ERROR_INVALID_ARGUMENT); } for (int i = 0; i < percents.length; i++) { if (percents[i] < 0 || percents[i] > 100) { DWT.error(DWT.ERROR_INVALID_ARGUMENT); } if (i > 0 && percents[i] < percents[i - 1]) { DWT.error(DWT.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 (bgImage is null) { if ((gradientColors !is null) && (colors !is null) && (gradientColors.length is colors.length)) { bool same = false; for (int i = 0; i < gradientColors.length; i++) { if (gradientColors[i] is null) { same = colors[i] is null; } else { same = gradientColors[i].opEquals(colors[i]); } if (!same) break; } if (same) { for (int i = 0; i < gradientPercents.length; i++) { same = gradientPercents[i] is percents[i]; if (!same) break; } } if (same && this.gradientVertical is vertical) return; } } else { bgImage = null; } // Store the new settings if (colors is null) { gradientColors = null; gradientPercents = null; gradientVertical = false; setBackground(cast(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(); } /** * Set the image to be drawn in the background of the unselected tab. Image * is stretched or compressed to cover entire unselected tab area. * * @param image the image to be drawn in the background * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ void setBackground (Image image) { checkWidget(); if (image is bgImage) return; if (image !is null) { gradientColors = null; gradientPercents = null; } bgImage = image; redraw(); } /** * Toggle the visibility of the border * * @param show true if the border should be displayed * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void setBorderVisible (bool show) { checkWidget(); if ((borderLeft is 1) is show) return; borderLeft = borderRight = show ? 1 : 0; borderTop = onBottom ? borderLeft : 0; borderBottom = onBottom ? 0 : borderLeft; Rectangle rectBefore = getClientArea(); updateItems(); Rectangle rectAfter = getClientArea(); if (!rectBefore.opEquals(rectAfter)) { notifyListeners(DWT.Resize, new Event()); } redraw(); } void setButtonBounds () { Point size = getSize(); int oldX, oldY, oldWidth, oldHeight; // max button oldX = maxRect.x; oldY = maxRect.y; oldWidth = maxRect.width; oldHeight = maxRect.height; maxRect.x = maxRect.y = maxRect.width = maxRect.height = 0; if (showMax) { maxRect.x = size.x - borderRight - BUTTON_SIZE - 3; if (borderRight > 0) maxRect.x += 1; maxRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE) / 2 : borderTop + (tabHeight - BUTTON_SIZE) / 2; maxRect.width = BUTTON_SIZE; maxRect.height = BUTTON_SIZE; } if (oldX !is maxRect.x || oldWidth !is maxRect.width || oldY !is maxRect.y || oldHeight !is maxRect.height) { int left = Math.min(oldX, maxRect.x); int right = Math.max(oldX + oldWidth, maxRect.x + maxRect.width); int top = onBottom ? size.y - borderBottom - tabHeight : borderTop + 1; redraw(left, top, right - left, tabHeight, false); } // min button oldX = minRect.x; oldY = minRect.y; oldWidth = minRect.width; oldHeight = minRect.height; minRect.x = minRect.y = minRect.width = minRect.height = 0; if (showMin) { minRect.x = size.x - borderRight - maxRect.width - BUTTON_SIZE - 3; if (borderRight > 0) minRect.x += 1; minRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE) / 2 : borderTop + (tabHeight - BUTTON_SIZE) / 2; minRect.width = BUTTON_SIZE; minRect.height = BUTTON_SIZE; } if (oldX !is minRect.x || oldWidth !is minRect.width || oldY !is minRect.y || oldHeight !is minRect.height) { int left = Math.min(oldX, minRect.x); int right = Math.max(oldX + oldWidth, minRect.x + minRect.width); int top = onBottom ? size.y - borderBottom - tabHeight : borderTop + 1; redraw(left, top, right - left, tabHeight, false); } // top right control oldX = topRightRect.x; oldY = topRightRect.y; oldWidth = topRightRect.width; oldHeight = topRightRect.height; topRightRect.x = topRightRect.y = topRightRect.width = topRightRect.height = 0; if (topRight !is null) { switch (topRightAlignment) { case DWT.FILL: { int rightEdge = size.x - borderRight - 3 - maxRect.width - minRect.width; if (!simple && borderRight > 0 && !showMax && !showMin) rightEdge -= 2; if (single) { if (items.length is 0 || selectedIndex is -1) { topRightRect.x = borderLeft + 3; topRightRect.width = rightEdge - topRightRect.x; } else { // fill size is 0 if item compressed CTabItem item = items[selectedIndex]; if (item.x + item.width + 7 + 3 * BUTTON_SIZE / 2 >= rightEdge) break; topRightRect.x = item.x + item.width + 7 + 3 * BUTTON_SIZE / 2; topRightRect.width = rightEdge - topRightRect.x; } } else { // fill size is 0 if chevron showing if (showChevron) break; if (items.length is 0) { topRightRect.x = borderLeft + 3; } else { CTabItem item = items[items.length - 1]; topRightRect.x = item.x + item.width; if (!simple && items.length - 1 is selectedIndex) topRightRect.x += curveWidth - curveIndent; } topRightRect.width = Math.max(0, rightEdge - topRightRect.x); } topRightRect.y = onBottom ? size.y - borderBottom - tabHeight : borderTop + 1; topRightRect.height = tabHeight - 1; break; } case DWT.RIGHT: { Point topRightSize = topRight.computeSize(DWT.DEFAULT, tabHeight, false); int rightEdge = size.x - borderRight - 3 - maxRect.width - minRect.width; if (!simple && borderRight > 0 && !showMax && !showMin) rightEdge -= 2; topRightRect.x = rightEdge - topRightSize.x; topRightRect.width = topRightSize.x; topRightRect.y = onBottom ? size.y - borderBottom - tabHeight : borderTop + 1; topRightRect.height = tabHeight - 1; } } topRight.setBounds(topRightRect); } if (oldX !is topRightRect.x || oldWidth !is topRightRect.width || oldY !is topRightRect.y || oldHeight !is topRightRect.height) { int left = Math.min(oldX, topRightRect.x); int right = Math.max(oldX + oldWidth, topRightRect.x + topRightRect.width); int top = onBottom ? size.y - borderBottom - tabHeight : borderTop + 1; redraw(left, top, right - left, tabHeight, false); } // chevron button oldX = chevronRect.x; oldY = chevronRect.y; oldWidth = chevronRect.width; oldHeight = chevronRect.height; chevronRect.x = chevronRect.y = chevronRect.height = chevronRect.width = 0; if (single) { if (selectedIndex is -1 || items.length > 1) { chevronRect.width = 3 * BUTTON_SIZE / 2; chevronRect.height = BUTTON_SIZE; chevronRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - chevronRect.height) / 2 : borderTop + (tabHeight - chevronRect.height) / 2; if (selectedIndex is -1) { chevronRect.x = size.x - borderRight - 3 - minRect.width - maxRect.width - topRightRect.width - chevronRect.width; } else { CTabItem item = items[selectedIndex]; int w = size.x - borderRight - 3 - minRect.width - maxRect.width - chevronRect.width; if (topRightRect.width > 0) w -= topRightRect.width + 3; chevronRect.x = Math.min(item.x + item.width + 3, w); } if (borderRight > 0) chevronRect.x += 1; } } else { if (showChevron) { chevronRect.width = 3 * BUTTON_SIZE / 2; chevronRect.height = BUTTON_SIZE; int i = 0, lastIndex = -1; while (i < priority.length && items[priority[i]].showing) { lastIndex = Math.max(lastIndex, priority[i++]); } if (lastIndex is -1) lastIndex = firstIndex; CTabItem lastItem = items[lastIndex]; int w = lastItem.x + lastItem.width + 3; if (!simple && lastIndex is selectedIndex) w += curveWidth - 2 * curveIndent; chevronRect.x = Math.min(w, getRightItemEdge()); chevronRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - chevronRect.height) / 2 : borderTop + (tabHeight - chevronRect.height) / 2; } } if (oldX !is chevronRect.x || oldWidth !is chevronRect.width || oldY !is chevronRect.y || oldHeight !is chevronRect.height) { int left = Math.min(oldX, chevronRect.x); int right = Math.max(oldX + oldWidth, chevronRect.x + chevronRect.width); int top = onBottom ? size.y - borderBottom - tabHeight : borderTop + 1; redraw(left, top, right - left, tabHeight, false); } } public void setFont (Font font) { checkWidget(); if (font !is null && font.opEquals(getFont())) return; super.setFont(font); oldFont = getFont(); if (!updateTabHeight(false)) { updateItems(); redraw(); } } 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 DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void setInsertMark (CTabItem item, bool 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 null * * @param after true if the mark should be displayed after the specified item * * @exception IllegalArgumentException<ul> * </ul> * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void setInsertMark (int index, bool after) { checkWidget(); if (index < -1 || index >= getItemCount()) { DWT.error(DWT.ERROR_INVALID_ARGUMENT); } } bool setItemLocation () { bool changed = false; if (items.length is 0) return false; Point size = getSize(); int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop; if (single) { int defaultX = getDisplay().getBounds().width + 10; // off screen for (int i = 0; i < items.length; i++) { CTabItem item = items[i]; if (i is selectedIndex) { firstIndex = selectedIndex; int oldX = item.x, oldY = item.y; item.x = borderLeft; item.y = y; item.showing = true; if (showClose || item.showClose) { item.closeRect.x = borderLeft + CTabItem.LEFT_MARGIN; item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE) / 2 : borderTop + (tabHeight - BUTTON_SIZE) / 2; } if (item.x !is oldX || item.y !is oldY) changed = true; } else { item.x = defaultX; item.showing = false; } } } else { int rightItemEdge = getRightItemEdge(); int maxWidth = rightItemEdge - borderLeft; int width = 0; for (int i = 0; i < priority.length; i++) { CTabItem item = items[priority[i]]; width += item.width; item.showing = i is 0 ? true : item.width > 0 && width <= maxWidth; if (!simple && priority[i] is selectedIndex) width += curveWidth - 2 * curveIndent; } int x = 0; 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 !is defaultX) changed = true; item.x = defaultX; } else { firstIndex = Math.min(firstIndex, i); if (item.x !is x || item.y !is y) changed = true; item.x = x; item.y = y; if (i is selectedIndex) { int edge = Math.min(item.x + item.width, rightItemEdge); item.closeRect.x = edge - CTabItem.RIGHT_MARGIN - BUTTON_SIZE; } else { item.closeRect.x = item.x + item.width - CTabItem.RIGHT_MARGIN - BUTTON_SIZE; } item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE) / 2 : borderTop + (tabHeight - BUTTON_SIZE) / 2; x = x + item.width; if (!simple && i is selectedIndex) x += curveWidth - 2 * curveIndent; } } } return changed; } bool setItemSize () { bool changed = false; if (isDisposed()) return changed; Point size = getSize(); if (size.x <= 0 || size.y <= 0) return changed; xClient = borderLeft + marginWidth + highlight_margin; if (onBottom) { yClient = borderTop + highlight_margin + marginHeight; } else { yClient = borderTop + tabHeight + highlight_header + marginHeight; } showChevron = false; if (single) { showChevron = true; if (selectedIndex !is -1) { CTabItem tab = items[selectedIndex]; GC gc = new GC(this); int width = tab.preferredWidth(gc, true, false); gc.dispose(); width = Math.min(width, getRightItemEdge() - borderLeft); if (tab.height !is tabHeight || tab.width !is 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) { tab.closeRect.width = BUTTON_SIZE; tab.closeRect.height = BUTTON_SIZE; } } } return changed; } if (items.length is 0) return changed; int[] widths; GC gc = new GC(this); int tabAreaWidth = size.x - borderLeft - borderRight - 3; if (showMin) tabAreaWidth -= BUTTON_SIZE; if (showMax) tabAreaWidth -= BUTTON_SIZE; if (topRightAlignment is DWT.RIGHT && topRight !is null) { Point rightSize = topRight.computeSize(DWT.DEFAULT, DWT.DEFAULT, false); tabAreaWidth -= rightSize.x + 3; } if (!simple) tabAreaWidth -= curveWidth - 2 * curveIndent; tabAreaWidth = Math.max(0, tabAreaWidth); // 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]; minWidths[index] = items[index].preferredWidth(gc, index is selectedIndex, true); minWidth += minWidths[index]; if (minWidth > tabAreaWidth) break; } if (minWidth > tabAreaWidth) { // full compression required and a chevron showChevron = items.length > 1; if (showChevron) tabAreaWidth -= 3 * BUTTON_SIZE / 2; widths = minWidths; int index = selectedIndex !is -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++) { maxWidths[i] = items[i].preferredWidth(gc, i is selectedIndex, false); 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 is 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); } } } gc.dispose(); for (int i = 0; i < items.length; i++) { CTabItem tab = items[i]; int width = widths[i]; if (tab.height !is tabHeight || tab.width !is 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 is selectedIndex || showUnselectedClose) { tab.closeRect.width = BUTTON_SIZE; tab.closeRect.height = BUTTON_SIZE; } } } } return changed; } /** * Marks the receiver's maximize button as visible if the argument is <code>true</code>, * and marks it invisible otherwise. * * @param visible the new visibility state * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public void setMaximizeVisible (bool visible) { checkWidget(); if (showMax is visible) return; // display maximize button showMax = visible; updateItems(); redraw(); } /** * Sets the layout which is associated with the receiver to be * the argument which may be null. * <p> * Note: No Layout can be set on this Control because it already * manages the size and position of its children. * </p> * * @param layout the receiver's new layout or null * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void setLayout (Layout layout) { checkWidget(); return; } /** * Sets the maximized state of the receiver. * * @param maximize the new maximized state * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public void setMaximized (bool maximize) { checkWidget(); if (this.maximized is maximize) return; if (maximize && this.minimized) setMinimized(false); this.maximized = maximize; redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); } /** * Marks the receiver's minimize button as visible if the argument is <code>true</code>, * and marks it invisible otherwise. * * @param visible the new visibility state * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public void setMinimizeVisible (bool visible) { checkWidget(); if (showMin is visible) return; // display minimize button showMin = visible; updateItems(); redraw(); } /** * Sets the minimized state of the receiver. * * @param minimize the new minimized state * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public void setMinimized (bool minimize) { checkWidget(); if (this.minimized is minimize) return; if (minimize && this.maximized) setMaximized(false); this.minimized = minimize; redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); } /** * 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 DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * <li>ERROR_INVALID_RANGE - if the count is less than zero</li> * </ul> * * @since 3.0 */ public void setMinimumCharacters (int count) { checkWidget(); if (count < 0) DWT.error(DWT.ERROR_INVALID_RANGE); if (minChars is count) return; minChars = count; if (updateItems()) redrawTabs(); } /** * 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. * <p> * 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).</p> * * @param show the new visibility state * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public void setMRUVisible (bool show) { checkWidget(); if (mru is show) return; mru = show; if (!mru) { 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; } if (updateItems()) redrawTabs(); } } /** * Set the selection to the tab at the specified item. * * @param item the tab item to be selected * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * * @exception DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ public void setSelection (CTabItem item) { checkWidget(); if (item is null) DWT.error(DWT.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 DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void setSelection (int index) { checkWidget(); if (index < 0 || index >= items.length) return; CTabItem selection = items[index]; if (selectedIndex is index) { showItem(selection); return; } int oldIndex = selectedIndex; selectedIndex = index; if (oldIndex !is -1) { items[oldIndex].closeImageState = NONE; } selection.closeImageState = NORMAL; selection.showing = false; Control newControl = selection.control; Control oldControl = null; if (oldIndex !is -1) { oldControl = items[oldIndex].control; } if (newControl !is oldControl) { if (newControl !is null && !newControl.isDisposed()) { newControl.setBounds(getClientArea()); newControl.setVisible(true); } if (oldControl !is null && !oldControl.isDisposed()) { oldControl.setVisible(false); } } showItem(selection); redraw(); } void setSelection (int index, bool notify) { int oldSelectedIndex = selectedIndex; setSelection(index); if (notify && selectedIndex !is oldSelectedIndex && selectedIndex !is -1) { Event event = new Event(); event.item = getItem(selectedIndex); notifyListeners(DWT.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 <ul> * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> * </ul> * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public void setSelectionBackground (Color color) { checkWidget(); setSelectionHighlightGradientColor(null); if (selectionBackground is color) return; if (color is null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND); selectionBackground = color; 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: * <pre> * cfolder.setBackground(new Color[]{display.getSystemColor(DWT.COLOR_DARK_BLUE), * display.getSystemColor(DWT.COLOR_BLUE), * display.getSystemColor(DWT.COLOR_WHITE), * display.getSystemColor(DWT.COLOR_WHITE)}, * new int[] {25, 50, 100}); * </pre> * * @param colors an array of Color that specifies the colors to appear in the gradient * in order of appearance left to right. The value <code>null</code> clears the * background gradient. The value <code>null</code> 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 DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> */ 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: * <pre> * cfolder.setBackground(new Color[]{display.getSystemColor(DWT.COLOR_DARK_BLUE), * display.getSystemColor(DWT.COLOR_BLUE), * display.getSystemColor(DWT.COLOR_WHITE), * display.getSystemColor(DWT.COLOR_WHITE)}, * new int[] {25, 50, 100}, true); * </pre> * * @param colors an array of Color that specifies the colors to appear in the gradient * in order of appearance left to right. The value <code>null</code> clears the * background gradient. The value <code>null</code> 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 DWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> * </ul> * * @since 3.0 */ public void setSelectionBackground (Color[] colors, int[] percents, bool vertical) { checkWidget(); int colorsLength; Color highlightBeginColor = null; //null is no highlight if (colors !is 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 is null || !((percents.length is colors.length - 1) || (percents.length is colors.length - 2))) { DWT.error(DWT.ERROR_INVALID_ARGUMENT); } for (int i = 0; i < percents.length; i++) { if (percents[i] < 0 || percents[i] > 100) { DWT.error(DWT.ERROR_INVALID_ARGUMENT); } if (i > 0 && percents[i] < percents[i - 1]) { DWT.error(DWT.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 is 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 is null) { if ((selectionGradientColors !is null) && (colors !is null) && (selectionGradientColors.length is colorsLength)) { bool same = false; for (int i = 0; i < selectionGradientColors.length; i++) { if (selectionGradientColors[i] is null) { same = colors[i] is null; } else { same = selectionGradientColors[i].opEquals(colors[i]); } if (!same) break; } if (same) { for (int i = 0; i < selectionGradientPercents.length; i++) { same = selectionGradientPercents[i] is percents[i]; if (!same) break; } } if (same && this.selectionGradientVertical is vertical) return; } } else { selectionBgImage = null; } // Store the new settings if (colors is null) { selectionGradientColors = null; selectionGradientPercents = null; selectionGradientVertical = false; setSelectionBackground(cast(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) { //Set to null to match all the early return cases. //For early returns, don't realloc the cache, we may get a cache hit next time we're given the highlight selectionHighlightGradientBegin = null; if (start is null) return; //don't bother on low colour if (getDisplay().getDepth() < 15) return; //don't bother if we don't have a background gradient if (selectionGradientColors.length < 2) return; //OK we know its a valid gradient now selectionHighlightGradientBegin = start; if (!isSelectionHighlightColorsCacheHit(start)) createSelectionHighlightGradientColors(start); //if no cache hit then compute new ones } /* * Return true if given start color, the cache of highlight colors we have * would match the highlight colors we'd compute. */ bool isSelectionHighlightColorsCacheHit (Color start) { if (selectionHighlightGradientColorsCache is null) return false; //this case should never happen but check to be safe before accessing array indexes if (selectionHighlightGradientColorsCache.length < 2) return false; Color highlightBegin = selectionHighlightGradientColorsCache[0]; Color highlightEnd = selectionHighlightGradientColorsCache[selectionHighlightGradientColorsCache.length - 1]; if (!highlightBegin.opEquals(start)) return false; //Compare number of colours we have vs. we'd compute if (selectionHighlightGradientColorsCache.length !is tabHeight) return false; //Compare existing highlight end to what it would be (selectionBackground) if (!highlightEnd.opEquals(selectionBackground)) return false; return true; } /** * 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 DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void setSelectionBackground (Image image) { checkWidget(); setSelectionHighlightGradientColor(null); if (image is selectionBgImage) return; if (image !is null) { selectionGradientColors = null; selectionGradientPercents = null; disposeSelectionHighlightGradientColors(); } selectionBgImage = image; 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 DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void setSelectionForeground (Color color) { checkWidget(); if (selectionForeground is color) return; if (color is null) color = getDisplay().getSystemColor(SELECTION_FOREGROUND); selectionForeground = color; if (selectedIndex > -1) redraw(); } /* * Allocate colors for the highlight line. * Colours will be a gradual blend ranging from to. * Blend length will be tab height. * Recompute this if tab height changes. * Could remain null if there'd be no gradient (start=end or low colour display) */ void createSelectionHighlightGradientColors (Color start) { disposeSelectionHighlightGradientColors(); //dispose if existing if (start is null) //shouldn't happen but just to be safe return; //alloc colours for entire height to ensure it matches wherever we stop drawing int fadeGradientSize = tabHeight; RGB from = start.getRGB(); RGB to = selectionBackground.getRGB(); selectionHighlightGradientColorsCache = new Color[fadeGradientSize]; int denom = fadeGradientSize - 1; for (int i = 0; i < fadeGradientSize; i++) { int propFrom = denom - i; int propTo = i; int red = (to.red * propTo + from.red * propFrom) / denom; int green = (to.green * propTo + from.green * propFrom) / denom; int blue = (to.blue * propTo + from.blue * propFrom) / denom; selectionHighlightGradientColorsCache[i] = new Color(getDisplay(), red, green, blue); } } void disposeSelectionHighlightGradientColors () { if (selectionHighlightGradientColorsCache is null) return; for (int i = 0; i < selectionHighlightGradientColorsCache.length; i++) { selectionHighlightGradientColorsCache[i].dispose(); } selectionHighlightGradientColorsCache = null; } /* * Return the gradient start color for selected tabs, which is the start of the tab fade * (end is selectionBackground). */ Color getSelectionBackgroundGradientBegin () { if (selectionGradientColors is null) return getSelectionBackground(); if (selectionGradientColors.length is 0) return getSelectionBackground(); return selectionGradientColors[0]; } /** * Sets the shape that the CTabFolder will use to render itself. * * @param simple <code>true</code> if the CTabFolder should render itself in a simple, traditional style * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public void setSimple (bool simple) { checkWidget(); if (this.simple !is simple) { this.simple = simple; Rectangle rectBefore = getClientArea(); updateItems(); Rectangle rectAfter = getClientArea(); if (!rectBefore.opEquals(rectAfter)) { notifyListeners(DWT.Resize, new Event()); } redraw(); } } /** * Sets the number of tabs that the CTabFolder should display * * @param single <code>true</code> if only the selected tab should be displayed otherwise, multiple tabs will be shown. * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public void setSingle (bool single) { checkWidget(); if (this.single !is single) { this.single = single; if (!single) { for (int i = 0; i < items.length; i++) { if (i !is selectedIndex && items[i].closeImageState is NORMAL) { items[i].closeImageState = NONE; } } } Rectangle rectBefore = getClientArea(); updateItems(); Rectangle rectAfter = getClientArea(); if (!rectBefore.opEquals(rectAfter)) { notifyListeners(DWT.Resize, new Event()); } redraw(); } } /** * 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 pixel value of the height or -1 * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li> * </ul> */ public void setTabHeight (int height) { checkWidget(); if (height < -1) { DWT.error(DWT.ERROR_INVALID_ARGUMENT); } fixedTabHeight = height; updateTabHeight(false); } /** * Specify whether the tabs should appear along the top of the folder * or along the bottom of the folder. * * @param position <code>DWT.TOP</code> for tabs along the top or <code>DWT.BOTTOM</code> for tabs along the bottom * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * <li>ERROR_INVALID_ARGUMENT - if the position value is not either DWT.TOP or DWT.BOTTOM</li> * </ul> * * @since 3.0 */ public void setTabPosition (int position) { checkWidget(); if (position !is DWT.TOP && position !is DWT.BOTTOM) { DWT.error(DWT.ERROR_INVALID_ARGUMENT); } if (onBottom !is (position is DWT.BOTTOM)) { onBottom = position is DWT.BOTTOM; borderTop = onBottom ? borderLeft : 0; borderBottom = onBottom ? 0 : borderRight; updateTabHeight(true); Rectangle rectBefore = getClientArea(); updateItems(); Rectangle rectAfter = getClientArea(); if (!rectBefore.opEquals(rectAfter)) { notifyListeners(DWT.Resize, new Event()); } 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 DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li> * </ul> * * @since 2.1 */ public void setTopRight (Control control) { setTopRight(control, DWT.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. * <p> * The alignment parameter sets the layout of the control in the tab area. * <code>DWT.RIGHT</code> will cause the control to be positioned on the far * right of the folder and it will have its default size. <code>DWT.FILL</code> * 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. * </p> * * @param control the control to be displayed in the top right corner or null * @param alignment <code>DWT.RIGHT</code> or <code>DWT.FILL</code> * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li> * </ul> * * @since 3.0 */ public void setTopRight (Control control, int alignment) { checkWidget(); if (alignment !is DWT.RIGHT && alignment !is DWT.FILL) { DWT.error(DWT.ERROR_INVALID_ARGUMENT); } if (control !is null && control.getParent() !is this) { DWT.error(DWT.ERROR_INVALID_ARGUMENT); } topRight = control; topRightAlignment = alignment; if (updateItems()) redraw(); } /** * Specify whether the close button appears * when the user hovers over an unselected tabs. * * @param visible <code>true</code> makes the close button appear * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public void setUnselectedCloseVisible (bool visible) { checkWidget(); if (showUnselectedClose is visible) return; // display close button when mouse hovers showUnselectedClose = visible; updateItems(); redraw(); } /** * Specify whether the image appears on unselected tabs. * * @param visible <code>true</code> makes the image appear * * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.0 */ public void setUnselectedImageVisible (bool visible) { checkWidget(); if (showUnselectedImage is visible) return; // display image on unselected items showUnselectedImage = visible; updateItems(); 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 <ul> * <li>ERROR_NULL_ARGUMENT - if the item is null</li> * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> * </ul> * @exception DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see CTabFolder#showSelection() * * @since 2.0 */ public void showItem (CTabItem item) { checkWidget(); if (item is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); if (item.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); int index = indexOf(item); if (index is -1) DWT.error(DWT.ERROR_INVALID_ARGUMENT); int idx = -1; for (int i = 0; i < priority.length; i++) { if (priority[i] is 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.isShowing()) return; updateItems(index); redrawTabs(); } void showList (Rectangle rect) { if (items.length is 0 || !showChevron) return; if (showMenu is null || showMenu.isDisposed()) { showMenu = new Menu(this); } else { MenuItem[] items = showMenu.getItems(); for (int i = 0; i < items.length; i++) { items[i].dispose(); } } const 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, DWT.NONE); item.setText(tab.getText()); item.setImage(tab.getImage()); item.setData(id, tab); item.addSelectionListener(new class SelectionAdapter { public void widgetSelected (SelectionEvent e) { MenuItem menuItem = cast(MenuItem) e.widget; int index = indexOf(cast(CTabItem) menuItem.getData(id)); 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 DWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see CTabFolder#showItem(CTabItem) * * @since 2.0 */ public void showSelection () { checkWidget(); if (selectedIndex !is -1) { showItem(getSelection()); } } void _setToolTipText (int x, int y) { String oldTip = getToolTipText(); String newTip = _getToolTip(x, y); if (newTip is null || !newTip.opEquals(oldTip)) { setToolTipText(newTip); } } bool updateItems () { return updateItems(selectedIndex); } bool updateItems (int showIndex) { if (!single && !mru && showIndex !is -1) { // make sure selected item will be showing int firstIndex = showIndex; if (priority[0] < showIndex) { int maxWidth = getRightItemEdge() - borderLeft; if (!simple) maxWidth -= curveWidth - 2 * curveIndent; int width = 0; int[] widths = new int[items.length]; GC gc = new GC(this); for (int i = priority[0]; i <= showIndex; i++) { widths[i] = items[i].preferredWidth(gc, i is selectedIndex, true); width += widths[i]; if (width > maxWidth) break; } if (width > maxWidth) { width = 0; for (int i = showIndex; i >= 0; i--) { if (widths[i] is 0) widths[i] = items[i].preferredWidth(gc, i is selectedIndex, true); width += widths[i]; if (width > maxWidth) break; firstIndex = i; } } else { firstIndex = priority[0]; for (int i = showIndex + 1; i < items.length; i++) { widths[i] = items[i].preferredWidth(gc, i is selectedIndex, true); width += widths[i]; if (width >= maxWidth) break; } if (width < maxWidth) { for (int i = priority[0] - 1; i >= 0; i--) { if (widths[i] is 0) widths[i] = items[i].preferredWidth(gc, i is selectedIndex, true); width += widths[i]; if (width > maxWidth) break; firstIndex = i; } } } gc.dispose(); } if (firstIndex !is priority[0]) { int index = 0; for (int i = firstIndex; i < items.length; i++) { priority[index++] = i; } for (int i = 0; i < firstIndex; i++) { priority[index++] = i; } } } bool oldShowChevron = showChevron; bool changed = setItemSize(); changed |= setItemLocation(); setButtonBounds(); changed |= showChevron !is oldShowChevron; if (changed && getToolTipText() !is null) { Point pt = getDisplay().getCursorLocation(); pt = toControl(pt); _setToolTipText(pt.x, pt.y); } return changed; } bool updateTabHeight (bool force) { int style = getStyle(); if (fixedTabHeight is 0 && (style & DWT.FLAT) !is 0 && (style & DWT.BORDER) is 0) highlight_header = 0; int oldHeight = tabHeight; if (fixedTabHeight !is DWT.DEFAULT) { tabHeight = fixedTabHeight is 0 ? 0 : fixedTabHeight + 1; // +1 for line drawn across top of tab } else { int tempHeight = 0; GC gc = new GC(this); if (items.length is 0) { tempHeight = gc.textExtent("Default", CTabItem.FLAGS).y + CTabItem.TOP_MARGIN + CTabItem.BOTTOM_MARGIN; //$NON-NLS-1$ } else { for (int i = 0; i < items.length; i++) { tempHeight = Math.max(tempHeight, items[i].preferredHeight( gc)); } } gc.dispose(); tabHeight = tempHeight; } if (!force && tabHeight is oldHeight) return false; oldSize = null; if (onBottom) { int d = tabHeight - 12; curve = new int[][0 , 13 + d , 0 , 12 + d , 2 , 12 + d , 3 , 11 + d , 5 , 11 + d , 6 , 10 + d , 7 , 10 + d , 9 , 8 + d , 10 , 8 + d , 11 , 7 + d , 11 + d , 7 , 12 + d , 6 , 13 + d , 6 , 15 + d , 4 , 16 + d , 4 , 17 + d , 3 , 19 + d , 3 , 20 + d , 2 , 22 + d , 2 , 23 + d , 1]; curveWidth = 26 + d; curveIndent = curveWidth / 3; } else { int d = tabHeight - 12; curve = new int[][0 , 0 , 0 , 1 , 2 , 1 , 3 , 2 , 5 , 2 , 6 , 3 , 7 , 3 , 9 , 5 , 10 , 5 , 11 , 6 , 11 + d , 6 + d , 12 + d , 7 + d , 13 + d , 7 + d , 15 + d , 9 + d , 16 + d , 9 + d , 17 + d , 10 + d , 19 + d , 10 + d , 20 + d , 11 + d , 22 + d , 11 + d , 23 + d , 12 + d]; curveWidth = 26 + d; curveIndent = curveWidth / 3; //this could be static but since values depend on curve, better to keep in one place topCurveHighlightStart = new int[][0 , 2 , 1 , 2 , 2 , 2 , 3 , 3 , 4 , 3 , 5 , 3 , 6 , 4 , 7 , 4 , 8 , 5 , 9 , 6 , 10 , 6]; //also, by adding in 'd' here we save some math cost when drawing the curve topCurveHighlightEnd = new int[][10 + d , 6 + d , 11 + d , 7 + d , 12 + d , 8 + d , 13 + d , 8 + d , 14 + d , 9 + d , 15 + d , 10 + d , 16 + d , 10 + d , 17 + d , 11 + d , 18 + d , 11 + d , 19 + d , 11 + d , 20 + d , 12 + d , 21 + d , 12 + d , 22 + d , 12 + d]; } notifyListeners(DWT.Resize, new Event()); return true; } String _getToolTip (int x, int y) { if (showMin && minRect.contains(x, y)) return minimized ? DWT.getMessage("DWT_Restore") : DWT.getMessage( "DWT_Minimize"); //$NON-NLS-1$ //$NON-NLS-2$ if (showMax && maxRect.contains(x, y)) return maximized ? DWT.getMessage("DWT_Restore") : DWT.getMessage( "DWT_Maximize"); //$NON-NLS-1$ //$NON-NLS-2$ if (showChevron && chevronRect.contains(x, y)) return DWT.getMessage("DWT_ShowList"); //$NON-NLS-1$ CTabItem item = getItem(new Point(x, y)); if (item is null) return null; if (!item.showing) return null; if ((showClose || item.showClose) && item.closeRect.contains(x, y)) { return DWT.getMessage("DWT_Close"); //$NON-NLS-1$ } return item.getToolTipText(); } }